On Mon, Aug 13, 2018 at 12:02:41PM -0400, Neil Horman wrote: > 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 > Ping, I can't remember when you went on holiday, so I just wanted to check in on the staus here
Best Neil > Signed-off-by: Neil Horman <nhor...@tuxdriver.com> > CC: jdelv...@suse.de > CC: aara...@redhat.com > CC: ja...@redhat.com > CC: elli...@hpe.com > > --- > Change Notes: > V1->V2) Updated string formatting to print matching number of bytes > for unsigned shorts (ell...@hpe.com) > > Adjusted string format for bDescriptor (ell...@hpe.com) > > Prefaced PCI id's with 0x (ell...@hpe.com) > 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 > > _______________________________________________ https://lists.nongnu.org/mailman/listinfo/dmidecode-devel