[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[dmidecode] [PATCH v4] update dmidecode to parse Modern Management Contr
From: |
Neil Horman |
Subject: |
[dmidecode] [PATCH v4] update dmidecode to parse Modern Management Controller blocks |
Date: |
Mon, 13 Aug 2018 12:02:41 -0400 |
Starting with version 0x300 the SMBIOS specification defined in more
detail the contents of the management controller type. DMTF further
reserved values to define the Redfish host interface specification.
Update dmidecode to properly parse and present that information
Signed-off-by: Neil Horman <address@hidden>
CC: address@hidden
CC: address@hidden
CC: address@hidden
CC: address@hidden
---
Change Notes:
V1->V2) Updated string formatting to print matching number of bytes
for unsigned shorts (address@hidden)
Adjusted string format for bDescriptor (address@hidden)
Prefaced PCI id's with 0x (address@hidden)
V2->V3) Updated word and dword accesses to do appropriate endian
conversion
Updated Interface type and protocol type lists to reflect
overall SMBIOS spec rather than just RedFish host spec, and stay
more compatible with pre version 3 SMBIOS layouts
Adjusted IFC_PROTO_RECORD_BASE to be 6 rather than 7, as this is
in keeping with the spec, and is validated against the overall
type 42 record length in his dmidecode dump. I'm convinced that
the layout of the system I'm testing on has an extra byte
inserted between the protocol record count and the start of the
protocol records.
V3->V4) Moved type 42 defines to more appropriate section
Removed defines to avoid namespace clashes
Renamed function to dmi_parse_controller_structure
Renamed PCI[e] to PCI/PCIe
Added check to make sure structure length is sane
Consolidated Host Interface parsing to
dmi_management_controller_host_type
Restrict the controller parsing structure to only decode network
interface types
Validate that strucure recorded length is enough to decode ifc
specific data
Removed bString comment
Corrected protocol count comment
Separated protocol parsing to its own function
Adjusted hlen size
Fixed Ip/IP casing
Test each protocol record for containment in overall struct
length
Use dmi_system_uuid
Converted ennumeration string lookups to their own function
Guaranteed the non-null-ness of inet_ntop (note, inet_ntop is
POSIX-2001 compliant and so should be very portable)
Cleaned up host name copy using printf string precision
specifier
Cleaned up smbios version check
Fixed record cursor advancement
Misc cleanups
---
dmidecode.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 345 insertions(+), 15 deletions(-)
diff --git a/dmidecode.c b/dmidecode.c
index 76faed9..f7f1a9e 100644
--- a/dmidecode.c
+++ b/dmidecode.c
@@ -56,6 +56,10 @@
* - "PC Client Platform TPM Profile (PTP) Specification"
* Family "2.0", Level 00, Revision 00.43, January 26, 2015
*
https://trustedcomputinggroup.org/pc-client-platform-tpm-profile-ptp-specification/
+ * - "RedFish Host Interface Specification" (DMTF DSP0270)
+ * https://www.dmtf.org/sites/default/files/DSP0270_1.0.1.pdf
+ * - "DMTF SMBIOS Specification" (DMTF DSP0134)
+ *
https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
*/
#include <stdio.h>
@@ -63,6 +67,7 @@
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
+#include <arpa/inet.h>
#ifdef __FreeBSD__
#include <errno.h>
@@ -3379,6 +3384,7 @@ static void dmi_additional_info(const struct dmi_header
*h, const char *prefix)
static const char *dmi_management_controller_host_type(u8 code)
{
/* DMTF DSP0239 (MCTP) version 1.1.0 */
+ /* DMTF DSP0134 (SMBIOS version 3.1.1) */
static const char *type[] = {
"KCS: Keyboard Controller Style", /* 0x02 */
"8250 UART Register Compatible",
@@ -3391,7 +3397,11 @@ static const char
*dmi_management_controller_host_type(u8 code)
if (code >= 0x02 && code <= 0x08)
return type[code - 0x02];
- if (code == 0xF0)
+ else if (code <= 0x3F)
+ return "MCTP";
+ else if (code == 0x40)
+ return "Network";
+ else if (code == 0xF0)
return "OEM";
return out_of_spec;
}
@@ -3447,6 +3457,321 @@ static void dmi_tpm_characteristics(u64 code, const
char *prefix)
prefix, characteristics[i - 2]);
}
+/*
+ * DSP0134: 7.43.2: Protocol Record Types
+ */
+static const char *dmi_protocol_record_type(u8 type)
+{
+ const char *protocoltypes[] = {
+ "Reserved",
+ "Reserved",
+ "IPMI",
+ "MCTP",
+ "Redfish over IP",
+ };
+
+ if (type == 0xF0)
+ return "OEM";
+ else if (type <= 0x4)
+ return protocoltypes[type];
+ return out_of_spec;
+}
+
+/*
+ * DSP0270: 8.6: Protocol IP Assignment types
+ */
+static const char *dmi_protocol_assignment_type(u8 type)
+{
+ const char *assigntype[] = {
+ "Unknown",
+ "Static",
+ "DHCP",
+ "AutoConf",
+ "Host Selected",
+ };
+
+ if (type <= 0x4)
+ return assigntype[type];
+ return out_of_spec;
+}
+
+/*
+ * DSP0270: 8.6: Protocol IP Address type
+ */
+static const char *dmi_address_type(u8 type)
+{
+ const char *addressformat[] = {
+ "Unknown",
+ "IPv4",
+ "IPv6",
+ };
+
+ if (type <= 0x2)
+ return addressformat[type];
+ return out_of_spec;
+}
+
+static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
+{
+ u8 rid;
+ u8 *rdata;
+ u8 buf[64];
+ u8 aval;
+ u8 addrtype;
+ u8 hlen;
+ const char *addr;
+ int af;
+
+ /* DSP0270: 8.5: Protocol Identifier*/
+ rid = rec[0x0];
+ /* DSP0270: 8.5: Protocol Record Data */
+ rdata = &rec[0x2];
+
+ /*
+ * DSP0134: 7.43.2: If the protocol type is OEM defined,
+ * Map it down to the OEM index in our prtocol types
+ * array. Otherwise, if its larger than the last
+ * defined type on the list, index it as unknnown
+ */
+ printf("%s\tProtocol ID: %02x (%s)\n", prefix, rid,
dmi_protocol_record_type(rid));
+
+ /*
+ * Don't decode anything other than redfish for now
+ * Note 0x4 is Redfish over IP in DSP0134: 7.43.2
+ * and DSP0270: 8.5
+ */
+ if (rid != 0x4)
+ return;
+
+ /*
+ * DSP0270: 8.6: Redfish Over IP Service UUID
+ * Note: ver is hardcoded to 0x311 here just for
+ * convienience. It could get passed from the smbios
+ * header, but thats alot of passing of pointers just
+ * to get that info, and the only thing its used for is
+ * to determine the endianess of the field. since we only
+ * do this parsing on versions of smbios after 3.1.1, and the
+ * endianess of the field is alway little after version 2.6.0
+ * we can just pick a sufficiently recent version here
+ */
+ printf("%s\t\tService UUID: ", prefix);
+ dmi_system_uuid(&rdata[0], 0x311);
+ printf("\n");
+
+ /*
+ * DSP0270: 8.6: Redfish Over IP Host IP Assignment Type
+ * Note, using decimal indicies here, as the DSP0270
+ * uses decimal, so as to make it more comparable
+ */
+ aval = rdata[16];
+
+ printf("%s\t\tHost IP Assignment Type: %s\n", prefix,
+ dmi_protocol_assignment_type(aval));
+
+ /*
+ * DSP0270: 8.6: Redfish Over IP Host Address format
+ */
+ addrtype = (u8)rdata[17];
+
+ printf("%s\t\tHost IP Address Format: %s\n", prefix,
+
dmi_address_type(addrtype));
+
+ /* DSP0270: 8.6 IP Assignment types */
+ /* We only use the Host IP Address and Mask if the assignment type is
static */
+ if (aval == 0x1 || aval == 0x3)
+ {
+ /* DSP0270: 8.6: the Host IPV[4|6] Address */
+ af = addrtype == 0x1 ? AF_INET : AF_INET6;
+ addr = inet_ntop(af, &rdata[18], (char *)buf, 64);
+ addr = addr ? addr: out_of_spec;
+ printf("%s\t\t%s Address: %s\n", prefix,
+ (addrtype == 0x1 ? "IPv4" : "IPv6"), addr);
+
+ /* DSP0270: 8.6: Prints the Host IPV[4|6] Mask */
+ addr = inet_ntop(af, &rdata[34], (char *)buf, 64);
+ addr = addr ? addr : out_of_spec;
+ printf("%s\t\t%s Mask: %s\n", prefix,
+ (addrtype == 0x1 ? "IPv4" : "IPv6"), addr);
+ }
+
+ /* DSP0270: 8.6: Get the Redfish Service IP Discovery Type */
+ aval = rdata[50];
+
+ /* Redfish Service IP Discovery type mirrors Host IP Assignment type */
+ printf("%s\t\tRedfish Service IP Discovery Type: %s\n", prefix,
+ dmi_protocol_assignment_type(aval));
+
+ /* DSP0270: 8.6: Get the Redfish Service IP Address Format */
+ addrtype = rdata[51];
+
+ printf("%s\t\tRedfish Service IP Address Format: %s\n", prefix,
+
dmi_address_type(addrtype));
+ if (aval == 0x1 || aval == 0x3)
+ {
+ u16 port;
+ u32 vlan;
+
+ af = addrtype == 0x1 ? AF_INET : AF_INET6;
+ addr = inet_ntop(af, &rdata[52], (char *)buf, 64);
+ addr = addr ? addr : out_of_spec; /* Ensure the string is
non-null */
+
+ /* DSP0270: 8.6: Prints the Redfish IPV[4|6]Service Address */
+ printf("%s\t\t%s Redfish Service Address: %s\n", prefix,
+ (addrtype == 0x1 ? "IPv4" : "IPv6"), addr);
+
+ /* DSP0270: 8.6: Prints the Redfish IPV[4|6] Service Mask */
+ addr = inet_ntop(af, &rdata[68], (char *)buf, 64);
+ addr = addr ? addr : out_of_spec;
+ printf("%s\t\t%s Redfish Service Mask: %s\n", prefix,
+ (addrtype == 0x1 ? "IPv4" : "IPv6"), addr);
+
+ /* DSP0270: 8.6: Redfish vlan and port info */
+ port = WORD(&rdata[84]);
+ vlan = DWORD(&rdata[86]);
+ printf("%s\t\tRedfish Service Port: %u\n", prefix, port);
+ printf("%s\t\tRedfish Service Vlan: %u\n", prefix, vlan);
+ }
+
+ /* DSP0270: 8.6: Redfish host length and name */
+ hlen = rdata[90];
+ printf("%s\t\tRedfish Service Hostname: %*s\n", prefix, hlen,
&rdata[91]);
+}
+
+/*
+ * DSP0270: 8.3: Device type ennumeration
+ */
+static const char *dmi_parse_device_type(u8 type)
+{
+ const char *devname[] = {
+ "Unknown",
+ "Unknown",
+ "USB",
+ "PCI/PCIe",
+ };
+
+ if (type >= 0x80)
+ return "OEM";
+
+ if (type <= 0x3)
+ return devname[type];
+ return out_of_spec;
+}
+
+static void dmi_parse_controller_structure(const struct dmi_header *h,
+ const char *prefix)
+{
+ int i;
+ u8 *data = h->data;
+ /* Host interface type */
+ u8 type = data[0x4];
+ /* Host Interface specific data length */
+ u8 len = data[0x5];
+ u8 count;
+
+ /*
+ * DSP0134: Minimum length of this struct is 0xB bytes
+ */
+ if (h->length < 0xB)
+ return;
+
+ /*
+ * Also need to ensure that the interface specific data length
+ * plus the size of the structure to that point don't exceed
+ * the defined length of the structure, or we will overrun its
+ * bounds
+ */
+ if ((len + 0x7) > h->length)
+ return;
+
+ printf("%sHost Interface Type: %s\n", prefix,
+ dmi_management_controller_host_type(type));
+
+ /*
+ * The following decodes are code for Network interface host types only
+ * As defined in DSP0270
+ */
+ if (type != 0x40)
+ return;
+
+ if (len != 0)
+ {
+ /* DSP0270: 8.3 Table 2: Device Type */
+ type = data[0x6];
+
+ printf("%sDevice Type: %s\n", prefix,
dmi_parse_device_type(type));
+ if ((type == 0x2) && (len >= 6))
+ {
+ /* USB Device Type - need at least 6 bytes */
+ u8 *usbdata = &data[0x7];
+ /* USB Device Descriptor: idVendor */
+ printf("%s\tidVendor: 0x%04x\n", prefix,
WORD(&usbdata[0x0]));
+ /* USB Device Descriptor: idProduct */
+ printf("%s\tidProduct: 0x%04x\n", prefix,
WORD(&usbdata[0x2]));
+ printf("%s\tSerialNumber:\n", prefix);
+ /* USB Device Descriptor: bDescriptor */
+ printf("%s\t\tbDescriptor: 0x%02x\n", prefix,
usbdata[0x5]);
+ }
+ else if ((type == 0x3) && (len >= 8))
+ {
+ /* PCI Device Type - Need at least 8 bytes*/
+ u8 *pcidata = &data[0x7];
+ /* PCI Device Descriptor: VendorID */
+ printf("%s\tVendorID: 0x%04x\n", prefix,
WORD(&pcidata[0x0]));
+ /* PCI Device Descriptor: DeviceID */
+ printf("%s\tDeviceID: 0x%04x\n", prefix,
WORD(&pcidata[0x2]));
+ /* PCI Device Descriptor: PCI SubvendorID */
+ printf("%s\tSubVendorID: 0x%04x\n", prefix,
WORD(&pcidata[0x4]));
+ /* PCI Device Descriptor: PCI SubdeviceID */
+ printf("%s\tSubDeviceID: 0x%04x\n", prefix,
WORD(&pcidata[0x6]));
+ }
+ else if ((type == 0x4) && (len >= 4))
+ {
+ /* OEM Device Type - Need at least 4 bytes */
+ u32 *oemdata = (u32 *)&data[0x7];
+ /* OEM Device Descriptor: IANA */
+ printf("%s\t Vendor ID: 0x%08x\n", prefix,
DWORD(&oemdata[0x0]));
+ }
+ /* Don't mess with unknown types for now */
+ }
+
+ /*
+ * DSP0270: 8.2 and 8.5: Protcol record count and protocol records
+ * Move to the Protocol Count Record
+ * data[0x6] points to the start of the interface specific
+ * data, and len is the length of that region
+ */
+ data = &data[0x6+len];
+ len = len+0x6;
+
+ /* Get the protocol records count */
+ count = (u8)data[0x0];
+ if (count)
+ {
+ u8 *rec = &data[0x1];
+ for (i = 0; i < count; i++)
+ {
+ /*
+ * Need to ensure that this record doesn't overrun
+ * the total length of the type 42 struct
+ */
+ if (len + rec[1] > h->length)
+ return;
+ len += rec[1];
+
+ dmi_parse_protocol_record(prefix, rec);
+ /*
+ * DSP0270: 8.6
+ * Each record is rec[1] bytes long, starting at the
+ * data byte immediately following the length field
+ * That means we need to add the byte for the rec id,
+ * the byte for the length field, and the value of the
+ * length field itself
+ */
+ rec += rec[1] + 2;
+ }
+ }
+}
+
/*
* Main
*/
@@ -4582,22 +4907,27 @@ static void dmi_decode(const struct dmi_header *h, u16
ver)
case 42: /* 7.43 Management Controller Host Interface */
printf("Management Controller Host Interface\n");
- if (h->length < 0x05) break;
- printf("\tInterface Type: %s\n",
-
dmi_management_controller_host_type(data[0x04]));
- /*
- * There you have a type-dependent, variable-length
- * part in the middle of the structure, with no
- * length specifier, so no easy way to decode the
- * common, final part of the structure. What a pity.
- */
- if (h->length < 0x09) break;
- if (data[0x04] == 0xF0) /* OEM */
+ if (ver < 0x0302)
{
- printf("\tVendor ID: 0x%02X%02X%02X%02X\n",
- data[0x05], data[0x06], data[0x07],
- data[0x08]);
+ if (h->length < 0x05) break;
+ printf("\tInterface Type: %s\n",
+
dmi_management_controller_host_type(data[0x04]));
+ /*
+ * There you have a type-dependent,
variable-length
+ * part in the middle of the structure, with no
+ * length specifier, so no easy way to decode
the
+ * common, final part of the structure. What a
pity.
+ */
+ if (h->length < 0x09) break;
+ if (data[0x04] == 0xF0) /* OEM */
+ {
+ printf("\tVendor ID:
0x%02X%02X%02X%02X\n",
+ data[0x05], data[0x06],
data[0x07],
+ data[0x08]);
+ }
}
+ else
+ dmi_parse_controller_structure(h, "\t");
break;
case 43: /* 7.44 TPM Device */
--
2.17.1
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [dmidecode] [PATCH v4] update dmidecode to parse Modern Management Controller blocks,
Neil Horman <=