diff options
Diffstat (limited to 'roms/skiboot/hdata/vpd.c')
-rw-r--r-- | roms/skiboot/hdata/vpd.c | 767 |
1 files changed, 767 insertions, 0 deletions
diff --git a/roms/skiboot/hdata/vpd.c b/roms/skiboot/hdata/vpd.c new file mode 100644 index 000000000..c778de346 --- /dev/null +++ b/roms/skiboot/hdata/vpd.c @@ -0,0 +1,767 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2013-2019 IBM Corp. */ + +#include <skiboot.h> +#include <vpd.h> +#include <string.h> +#include "spira.h" +#include "hdata.h" +#include <device.h> +#include "hdata.h" +#include <inttypes.h> +#include <mem_region-malloc.h> + +struct card_info { + const char *ccin; /* Customer card identification number */ + const char *description; +}; + +static const struct card_info card_table[] = { + {"2B06", "System planar 2S4U"}, + {"2B07", "System planar 1S4U"}, + {"2B2E", "System planar 2S2U"}, + {"2B2F", "System planar 1S2U"}, + {"2CD4", "System planar 2S4U"}, + {"2CD5", "System planar 1S4U"}, + {"2CD6", "System planar 2S2U"}, + {"2CD7", "System planar 1S2U"}, + {"2CD7", "System planar 1S2U"}, + {"2B09", "Base JBOD, RAID and Backplane HD"}, + {"57D7", "Split JBOD, RAID Card"}, + {"2B0B", "Native I/O Card"}, + + /* Anchor cards */ + {"52FE", "System Anchor Card - IBM Power 824"}, + {"52F2", "System Anchor Card - IBM Power 814"}, + {"52F5", "System Anchor Card - IBM Power 822"}, + {"561A", "System Anchor Card - IBM Power 824L"}, + {"524D", "System Anchor Card - IBM Power 822L"}, + {"560F", "System Anchor Card - IBM Power 812L"}, + {"561C", "System Anchor Card - DS8870"}, + + /* Memory DIMMs */ + {"31E0", "16GB CDIMM"}, + {"31E8", "16GB CDIMM"}, + {"31E1", "32GB CDIMM"}, + {"31E9", "32GB CDIMM"}, + {"31E2", "64GB CDIMM"}, + {"31EA", "64GB CDIMM"}, + + /* Power supplies */ + {"2B1D", "Power Supply 900W AC"}, + {"2B1E", "Power Supply 1400W AC"}, + {"2B75", "Power Supply 1400W HVDC"}, + + /* Fans */ + {"2B1F", "Fan 4U (A1, A2, A3, A4)"}, + {"2B29", "Fan 2U (A1, A2, A3, A4, A5, A6)"}, + + /* Other cards */ +}; + +struct vpd_key_map { + const char *keyword; /* 2 char keyword */ + const char *description; +}; + +static const struct vpd_key_map vpd_key_table[] = { + {"AA", "ac-power-supply"}, + {"AM", "air-mover"}, + {"AV", "anchor-card"}, + + {"BA", "bus-adapter-card"}, + {"BC", "battery-charger"}, + {"BD", "bus-daughter-card"}, + {"BE", "bus-expansion-card"}, + {"BP", "backplane"}, + {"BR", "backplane-riser"}, + {"BX", "backplane-extender"}, + + {"CA", "calgary-bridge"}, + {"CB", "infiniband-connector"}, + {"CC", "clock-card"}, + {"CD", "card-connector"}, + {"CE", "ethernet-connector"}, + {"CL", "calgary-phb"}, + {"CI", "capacity-card"}, + {"CO", "sma-connector"}, + {"CP", "processor-capacity-card"}, + {"CR", "rio-connector"}, + {"CS", "serial-connector"}, + {"CU", "usb-connector"}, + + {"DB", "dasd-backplane"}, + {"DC", "drawer-card"}, + {"DE", "drawer-extension"}, + {"DI", "drawer-interposer"}, + {"DL", "p7ih-dlink-connector"}, + {"DT", "legacy-pci-card"}, + {"DV", "media-drawer-led"}, + + {"EI", "enclosure-led"}, + {"EF", "enclosure-fault-led"}, + {"ES", "embedded-sas"}, + {"ET", "ethernet-riser"}, + {"EV", "enclosure"}, + + {"FM", "frame"}, + {"FN", "fru-stocking-part-number"}, + + {"HB", "host-rio-pci-card"}, + {"HD", "high-speed-card"}, + {"HM", "hmc-connector"}, + + {"IB", "io-backplane"}, + {"IC", "io-card"}, + {"ID", "ide-connector"}, + {"II", "io-drawer-led"}, + {"IP", "interplane-card"}, + {"IS", "smp-vbus-cable"}, + {"IT", "enclosure-cable"}, + {"IV", "io-enclosure"}, + + {"KV", "keyboard-led"}, + + {"L2", "l2-cache-module"}, + {"L3", "l3-cache-module"}, + {"LC", "squadrons-light-connector"}, + {"LR", "p7ih-connector"}, + {"LO", "system-locate-led"}, + {"LT", "squadrons-light-strip"}, + + {"MB", "media-backplane"}, + {"ME", "map-extension"}, + {"MM", "mip-meter"}, + {"MS", "ms-dimm"}, + + {"NB", "nvram-battery"}, + {"NC", "sp-node-controller"}, + {"ND", "numa-dimm"}, + + {"OD", "cuod-card"}, + {"OP", "op-panel"}, + {"OS", "oscillator"}, + + {"P2", "ioc"}, + {"P5", "ioc-bridge"}, + {"PB", "io-drawer-backplane"}, + {"PC", "power-capacitor"}, + {"PD", "processor-card"}, + {"PF", "processor"}, + {"PI", "ioc-phb"}, + {"PO", "spcn"}, + {"PN", "spcn-connector"}, + {"PR", "pci-riser-card"}, + {"PS", "power-supply"}, + {"PT", "pass-through-card"}, + {"PX", "psc-sync-card"}, + {"PW", "power-connector"}, + + {"RG", "regulator"}, + {"RI", "riser"}, + {"RK", "rack-indicator"}, + {"RW", "riscwatch-connector"}, + + {"SA", "sys-attn-led"}, + {"SB", "backup-sysvpd"}, + {"SC", "scsi-connector"}, + {"SD", "sas-connector"}, + {"SI", "scsi-ide-converter"}, + {"SL", "phb-slot"}, + {"SN", "smp-cable-connector"}, + {"SP", "service-processor"}, + {"SR", "service-card"}, + {"SS", "soft-switch"}, + {"SV", "system-vpd"}, + {"SY", "legacy-sysvpd"}, + + {"TD", "tod-clock"}, + {"TI", "torrent-pcie-phb"}, + {"TL", "torrent-riser"}, + {"TM", "thermal-sensor"}, + {"TP", "tpmd-adapter"}, + {"TR", "torrent-bridge"}, + + {"VV", "root-node-vpd"}, + + {"WD", "water_device"}, +}; + +static const char *vpd_map_name(const char *vpd_name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vpd_key_table); i++) + if (!strcmp(vpd_key_table[i].keyword, vpd_name)) + return vpd_key_table[i].description; + + prlog(PR_WARNING, "VPD: Could not map FRU ID %s to a known name\n", + vpd_name); + + return "Unknown"; +} + +static const struct card_info *card_info_lookup(char *ccin) +{ + int i; + for(i = 0; i < ARRAY_SIZE(card_table); i++) + /* CCIN is always 4 bytes in size */ + if (!strncmp(card_table[i].ccin, ccin, 4)) + return &card_table[i]; + return NULL; +} + +/* Discard trailing spaces and populate device tree */ +static struct dt_property *dt_add_prop_sanitize_val(struct dt_node *node, + const char *name, const char *val, int vlen) +{ + char *prop = zalloc(vlen + 1); + int i; + struct dt_property *p = NULL; + + if (!prop) + return p; + + memcpy(prop, val, vlen); + for (i = vlen - 1; i >= 0; i--) { + if (prop[i] != 0x20) { + prop[i + 1] = '\0'; + break; + } + } + + if (i >= 0 && !dt_find_property(node, name)) + p = dt_add_property_string(node, name, prop); + + free(prop); + return p; +} + +/* + * OpenPower system does not provide processor vendor name under FRU VPD. + * Parse processor module VPD to get vendor detail + */ +void dt_add_proc_vendor(struct dt_node *proc_node, + const void *mvpd, unsigned int mvpd_sz) +{ + const void *kw; + uint8_t sz; + + kw = vpd_find(mvpd, mvpd_sz, "VINI", "VN", &sz); + if (kw) + dt_add_prop_sanitize_val(proc_node, "vendor", kw, sz); +} + +/* + * For OpenPOWER, we only decipher OPFR records. While OP HDAT have VINI + * records too, populating the fields in there is optional. Also, there + * is an overlap in the fields contained therein. + */ +static void vpd_opfr_parse(struct dt_node *node, + const void *fruvpd, unsigned int fruvpd_sz) +{ + const void *kw; + uint8_t sz; + + /* Vendor Name */ + kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VN", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "vendor", kw, sz); + + /* FRU Description */ + kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "DR", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "description", kw, sz); + + /* Part number */ + kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VP", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "part-number", kw, sz); + + /* Serial number */ + kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "VS", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "serial-number", kw, sz); + + /* Build date in BCD */ + kw = vpd_find(fruvpd, fruvpd_sz, "OPFR", "MB", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "build-date", kw, sz); + + return; +} + +/* + * For CPUs, parse the VRML data. + */ +static void vpd_vrml_parse(struct dt_node *node, + const void *fruvpd, unsigned int fruvpd_sz) +{ + const void *kw; + uint8_t sz; + + /* Part number */ + kw = vpd_find(fruvpd, fruvpd_sz, "VRML", "PN", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "part-number", kw, sz); + + /* Serial number */ + kw = vpd_find(fruvpd, fruvpd_sz, "VRML", "SN", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "serial-number", kw, sz); + + return; +} + +static void vpd_vini_parse(struct dt_node *node, + const void *fruvpd, unsigned int fruvpd_sz) +{ + const void *kw; + const char *desc; + uint8_t sz; + const struct card_info *cinfo; + + /* FRU Stocking Part Number */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "FN", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "fru-number", kw, sz); + + /* Serial Number */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "SN", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "serial-number", kw, sz); + + /* Part Number */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "PN", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "part-number", kw, sz); + + /* Vendor Name */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "VN", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "vendor", kw, sz); + + /* CCIN Extension */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CE", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "ccin-extension", kw, sz); + + /* HW Version info */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "HW", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "hw-version", kw, sz); + + /* Card type info */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CT", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "card-type", kw, sz); + + /* HW characteristics info */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "B3", &sz); + if (kw) + dt_add_prop_sanitize_val(node, "hw-characteristics", kw, sz); + + /* Customer Card Identification Number (CCIN) */ + kw = vpd_find(fruvpd, fruvpd_sz, "VINI", "CC", &sz); + if (kw) { + dt_add_prop_sanitize_val(node, "ccin", kw, sz); + + cinfo = card_info_lookup((char *)kw); + if (cinfo) { + dt_add_property_string(node, + "description", cinfo->description); + } else { + desc = vpd_find(fruvpd, fruvpd_sz, "VINI", "DR", &sz); + if (desc) { + dt_add_prop_sanitize_val(node, + "description", desc, sz); + } else { + dt_add_property_string(node, "description", "Unknown"); + prlog(PR_WARNING, + "VPD: CCIN desc not available for: %s\n", + (char*)kw); + } + } + } + + return; +} + +static bool valid_child_entry(const struct slca_entry *entry) +{ + if ((entry->install_indic == SLCA_INSTALL_INSTALLED) && + (entry->vpd_collected == SLCA_VPD_COLLECTED)) + return true; + + return false; +} + +void vpd_data_parse(struct dt_node *node, const void *fruvpd, u32 fruvpd_sz) +{ + if (vpd_find_record(fruvpd, fruvpd_sz, "OPFR", NULL)) + vpd_opfr_parse(node, fruvpd, fruvpd_sz); + else if (vpd_find_record(fruvpd, fruvpd_sz, "VRML", NULL)) + vpd_vrml_parse(node, fruvpd, fruvpd_sz); + else + vpd_vini_parse(node, fruvpd, fruvpd_sz); +} + +/* Create the /vpd node and add its children */ +void dt_init_vpd_node(void) +{ + const char *name, *p_name; + int count, index; + uint64_t addr, p_addr; + struct dt_node *dt_vpd; + struct HDIF_common_hdr *slca_hdr; + struct dt_node *parent, *node; + const struct slca_entry *entry, *p_entry; + + dt_vpd = dt_new(dt_root, "vpd"); + assert(dt_vpd); + dt_add_property_string(dt_vpd, "compatible", "ibm,opal-v3-vpd"); + dt_add_property_cells(dt_vpd, "#size-cells", 0); + dt_add_property_cells(dt_vpd, "#address-cells", 1); + + slca_hdr = get_hdif(&spira.ntuples.slca, SLCA_HDIF_SIG); + if (!slca_hdr) { + prerror("SLCA Invalid\n"); + return; + } + + count = HDIF_get_iarray_size(slca_hdr, SLCA_IDATA_ARRAY); + if (count < 0) { + prerror("SLCA: Can't find SLCA array size!\n"); + return; + } + + for (index = 0; index < count; index++) { + /* Get SLCA entry */ + entry = slca_get_entry(index); + if (!entry) + continue; + + /* + * A child entry is valid if all of the following criteria is met + * a. SLCA_INSTALL_INSTALLED is set in s_entry->install_indic + * b. SLCA_VPD_COLLECTED is set in s_entry->vpd_collected + * c. The SLCA is not a duplicate entry. + */ + if (!valid_child_entry(entry)) + goto next_entry; + + name = vpd_map_name(entry->fru_id); + addr = be16_to_cpu(entry->rsrc_id); + /* Check node is already created or not */ + if (dt_find_by_name_addr(dt_vpd, name, addr)) + goto next_entry; + + /* Get parent node */ + if (index == SLCA_ROOT_INDEX) { + parent = dt_vpd; + } else { + p_entry = slca_get_entry(be16_to_cpu(entry->parent_index)); + if (!p_entry) + goto next_entry; + p_name = vpd_map_name(p_entry->fru_id); + p_addr = be16_to_cpu(p_entry->rsrc_id); + parent = dt_find_by_name_addr(dt_vpd, p_name, p_addr); + } + if (!parent) + goto next_entry; + + node = dt_new_addr(parent, name, addr); + if (!node) { + prerror("VPD: Creating node at %s@%"PRIx64" failed\n", + name, addr); + goto next_entry; + } + + /* Add location code */ + slca_vpd_add_loc_code(node, be16_to_cpu(entry->my_index)); + /* Add FRU label */ + dt_add_property(node, "fru-type", entry->fru_id, 2); + dt_add_property_cells(node, "reg", addr); + dt_add_property_cells(node, "#size-cells", 0); + dt_add_property_cells(node, "#address-cells", 1); + +next_entry: + /* Skip dups -- dups are contiguous */ + index += entry->nr_dups; + } +} + +struct dt_node *dt_add_vpd_node(const struct HDIF_common_hdr *hdr, + int indx_fru, int indx_vpd) +{ + const struct spira_fru_id *fru_id; + unsigned int fruvpd_sz, fru_id_sz; + const struct slca_entry *entry; + struct dt_node *dt_vpd, *node; + const void *fruvpd; + const char *name; + uint64_t addr; + + fru_id = HDIF_get_idata(hdr, indx_fru, &fru_id_sz); + if (!fru_id) + return NULL; + + fruvpd = HDIF_get_idata(hdr, indx_vpd, &fruvpd_sz); + if (!CHECK_SPPTR(fruvpd)) + return NULL; + + dt_vpd = dt_find_by_path(dt_root, "/vpd"); + if (!dt_vpd) + return NULL; + + entry = slca_get_entry(be16_to_cpu(fru_id->slca_index)); + if (!entry) + return NULL; + + name = vpd_map_name(entry->fru_id); + addr = be16_to_cpu(entry->rsrc_id); + /* Get the node already created */ + node = dt_find_by_name_addr(dt_vpd, name, addr); + /* + * It is unlikely that node not found because vpd nodes have the + * corresponding slca entry which we would have used to populate the vpd + * tree during the 'first' pass above so that we just need to perform + * VINI parse and add the vpd data.. + */ + if (!node) + return NULL; + + /* Parse VPD fields, ensure that it has not been added already */ + if (vpd_valid(fruvpd, fruvpd_sz) + && !dt_find_property(node, "ibm,vpd")) { + dt_add_property(node, "ibm,vpd", fruvpd, fruvpd_sz); + vpd_data_parse(node, fruvpd, fruvpd_sz); + } + + return node; +} + +static void dt_add_model_name(void) +{ + const char *model_name = NULL; + const struct machine_info *mi; + const struct iplparams_sysparams *p; + const struct HDIF_common_hdr *iplp; + const struct dt_property *model; + + model = dt_find_property(dt_root, "model"); + + iplp = get_hdif(&spira.ntuples.ipl_parms, "IPLPMS"); + if (!iplp) + goto def_model; + + p = HDIF_get_idata(iplp, IPLPARAMS_SYSPARAMS, NULL); + if (!CHECK_SPPTR(p)) + goto def_model; + + if (be16_to_cpu(iplp->version) >= 0x60) + model_name = p->sys_type_str; + +def_model: + if ((!model_name || model_name[0] == '\0') && model) { + mi = machine_info_lookup(model->prop); + if (mi) + model_name = mi->name; + } + + if(model_name) + dt_add_property_string(dt_root, "model-name", model_name); +} + +static void sysvpd_parse_opp(const void *sysvpd, unsigned int sysvpd_sz) +{ + const char *v; + uint8_t sz; + + v = vpd_find(sysvpd, sysvpd_sz, "OSYS", "MM", &sz); + if (v) + dt_add_prop_sanitize_val(dt_root, "model", v, sz); + else + dt_add_property_string(dt_root, "model", "Unknown"); + + v = vpd_find(sysvpd, sysvpd_sz, "OSYS", "SS", &sz); + if (v) + dt_add_prop_sanitize_val(dt_root, "system-id", v, sz); + else + dt_add_property_string(dt_root, "system-id", "Unknown"); +} + + +static void sysvpd_parse_legacy(const void *sysvpd, unsigned int sysvpd_sz) +{ + const char *model; + const char *system_id; + const char *brand; + uint8_t sz; + + model = vpd_find(sysvpd, sysvpd_sz, "VSYS", "TM", &sz); + if (model) + dt_add_prop_sanitize_val(dt_root, "model", model, sz); + else + dt_add_property_string(dt_root, "model", "Unknown"); + + system_id = vpd_find(sysvpd, sysvpd_sz, "VSYS", "SE", &sz); + if (system_id) + dt_add_prop_sanitize_val(dt_root, "system-id", system_id, sz); + else + dt_add_property_string(dt_root, "system-id", "Unknown"); + + brand = vpd_find(sysvpd, sysvpd_sz, "VSYS", "BR", &sz); + if (brand) + dt_add_prop_sanitize_val(dt_root, "system-brand", brand, sz); + else + dt_add_property_string(dt_root, "brand", "Unknown"); +} + +static void sysvpd_parse(void) +{ + const void *sysvpd; + unsigned int sysvpd_sz; + unsigned int fru_id_sz; + struct dt_node *dt_vpd; + const struct spira_fru_id *fru_id; + struct HDIF_common_hdr *sysvpd_hdr; + + sysvpd_hdr = get_hdif(&spira.ntuples.system_vpd, SYSVPD_HDIF_SIG); + if (!sysvpd_hdr) + return; + + fru_id = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_FRU_ID, &fru_id_sz); + if (!fru_id) + return; + + sysvpd = HDIF_get_idata(sysvpd_hdr, SYSVPD_IDATA_KW_VPD, &sysvpd_sz); + if (!CHECK_SPPTR(sysvpd)) + return; + + /* Add system VPD */ + dt_vpd = dt_find_by_path(dt_root, "/vpd"); + if (dt_vpd) { + dt_add_property(dt_vpd, "ibm,vpd", sysvpd, sysvpd_sz); + slca_vpd_add_loc_code(dt_vpd, be16_to_cpu(fru_id->slca_index)); + } + + /* Look for the new OpenPower "OSYS" first */ + if (vpd_find_record(sysvpd, sysvpd_sz, "OSYS", NULL)) + sysvpd_parse_opp(sysvpd, sysvpd_sz); + else + sysvpd_parse_legacy(sysvpd, sysvpd_sz); + + dt_add_model_name(); +} + +static void iokid_vpd_parse(const struct HDIF_common_hdr *iohub_hdr) +{ + const struct HDIF_child_ptr *iokids; + const struct HDIF_common_hdr *iokid; + unsigned int i; + + iokids = HDIF_child_arr(iohub_hdr, CECHUB_CHILD_IO_KIDS); + if (!CHECK_SPPTR(iokids)) { + prerror("VPD: No IOKID child array\n"); + return; + } + + for (i = 0; i < be32_to_cpu(iokids->count); i++) { + iokid = HDIF_child(iohub_hdr, iokids, i, + IOKID_FRU_HDIF_SIG); + /* IO KID VPD structure layout is similar to FRUVPD */ + if (iokid) + dt_add_vpd_node(iokid, + FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD); + } +} + +static void iohub_vpd_parse(void) +{ + const struct HDIF_common_hdr *iohub_hdr; + const struct cechub_hub_fru_id *fru_id_data; + unsigned int i, vpd_sz, fru_id_sz; + + if (!get_hdif(&spira.ntuples.cec_iohub_fru, CECHUB_FRU_HDIF_SIG)) { + prerror("VPD: Could not find IO HUB FRU data\n"); + return; + } + + for_each_ntuple_idx(&spira.ntuples.cec_iohub_fru, iohub_hdr, + i, CECHUB_FRU_HDIF_SIG) { + + fru_id_data = HDIF_get_idata(iohub_hdr, + CECHUB_FRU_ID_DATA_AREA, + &fru_id_sz); + + /* P8, IO HUB is on processor card and we have a + * daughter card array + */ + if (fru_id_data && + be32_to_cpu(fru_id_data->card_type) == CECHUB_FRU_TYPE_CPU_CARD) { + iokid_vpd_parse(iohub_hdr); + continue; + } + + if (HDIF_get_idata(iohub_hdr, + CECHUB_ASCII_KEYWORD_VPD, &vpd_sz)) + dt_add_vpd_node(iohub_hdr, CECHUB_FRU_ID_DATA, + CECHUB_ASCII_KEYWORD_VPD); + } +} + +static void _vpd_parse(struct spira_ntuple tuple) +{ + const struct HDIF_common_hdr *fruvpd_hdr; + unsigned int i; + + if (!get_hdif(&tuple, FRUVPD_HDIF_SIG)) + return; + + for_each_ntuple_idx(&tuple, fruvpd_hdr, i, FRUVPD_HDIF_SIG) + dt_add_vpd_node(fruvpd_hdr, + FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD); +} + +void vpd_parse(void) +{ + const struct HDIF_common_hdr *fruvpd_hdr; + + /* System VPD uses the VSYS record, so its special */ + sysvpd_parse(); + + /* Enclosure */ + _vpd_parse(spira.ntuples.nt_enclosure_vpd); + + /* Backplane */ + _vpd_parse(spira.ntuples.backplane_vpd); + + /* clock card -- does this use the FRUVPD sig? */ + _vpd_parse(spira.ntuples.clock_vpd); + + /* Anchor card */ + _vpd_parse(spira.ntuples.anchor_vpd); + + /* Op panel -- does this use the FRUVPD sig? */ + _vpd_parse(spira.ntuples.op_panel_vpd); + + /* External cache FRU vpd -- does this use the FRUVPD sig? */ + _vpd_parse(spira.ntuples.ext_cache_fru_vpd); + + /* Misc CEC FRU */ + _vpd_parse(spira.ntuples.misc_cec_fru_vpd); + + /* CEC IO HUB FRU */ + iohub_vpd_parse(); + + /* + * SP subsystem -- while the rest of the SPINFO structure is + * different, the FRU ID data and pointer pair to keyword VPD + * are the same offset as a FRUVPD entry. So reuse it + */ + fruvpd_hdr = get_hdif(&spira.ntuples.sp_subsys, SPSS_HDIF_SIG); + if (fruvpd_hdr) + dt_add_vpd_node(fruvpd_hdr, + FRUVPD_IDATA_FRU_ID, FRUVPD_IDATA_KW_VPD); +} |