dmidecode-devel
[Top][All Lists]
Advanced

[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
> 
> 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]