[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [dmidecode] [PATCH v4] update dmidecode to parse Modern Management C
From: |
Neil Horman |
Subject: |
Re: [dmidecode] [PATCH v4] update dmidecode to parse Modern Management Controller blocks |
Date: |
Tue, 4 Sep 2018 11:38:34 -0400 |
User-agent: |
Mutt/1.10.1 (2018-07-13) |
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 <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
>
>
- Re: [dmidecode] [PATCH v4] update dmidecode to parse Modern Management Controller blocks,
Neil Horman <=