/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #define CHECK_SPACE(_p, _n, _e) (((_e) - (_p)) >= (_n)) /* Low level keyword search in a record. Can be used when we * need to find the next keyword of a given type, for example * when having multiple MF/SM keyword pairs */ const void *vpd_find_keyword(const void *rec, size_t rec_sz, const char *kw, uint8_t *kw_size) { const uint8_t *p = rec, *end = rec + rec_sz; while (CHECK_SPACE(p, 3, end)) { uint8_t k1 = *(p++); uint8_t k2 = *(p++); uint8_t sz = *(p++); if (k1 == kw[0] && k2 == kw[1]) { if (kw_size) *kw_size = sz; return p; } p += sz; } return NULL; } /* vpd_valid - does some basic sanity checks to ensure a VPD blob is * actually a VPD blob */ bool vpd_valid(const void *vvpd, size_t vpd_size) { const uint8_t *vpd = vvpd; int size, i = 0; /* find the record start byte */ while (i < vpd_size) if (vpd[i++] == 0x84) break; if (i >= vpd_size) return false; /* next two bytes are the record length, little endian */ size = 2; size += vpd[i]; size += vpd[i + 1] << 8; i += size; /* skip to the end marker */ if (i >= vpd_size || vpd[i] != 0x78) return false; return true; } /* Locate a record in a VPD blob * * Note: This works with VPD LIDs. It will scan until it finds * the first 0x84, so it will skip all those 0's that the VPD * LIDs seem to contain */ const void *vpd_find_record(const void *vpd, size_t vpd_size, const char *record, size_t *sz) { const uint8_t *p = vpd, *end = vpd + vpd_size; bool first_start = true; size_t rec_sz; uint8_t namesz = 0; const char *rec_name; if (!vpd) return NULL; while (CHECK_SPACE(p, 4, end)) { /* Get header byte */ if (*(p++) != 0x84) { /* Skip initial crap in VPD LIDs */ if (first_start) continue; break; } first_start = false; rec_sz = *(p++); rec_sz |= *(p++) << 8; if (!CHECK_SPACE(p, rec_sz, end)) { prerror("VPD: Malformed or truncated VPD," " record size doesn't fit\n"); return NULL; } /* Find record name */ rec_name = vpd_find_keyword(p, rec_sz, "RT", &namesz); if (rec_name && strncmp(record, rec_name, namesz) == 0) { if (sz) *sz = rec_sz; return p; } p += rec_sz; if (*(p++) != 0x78) { prerror("VPD: Malformed or truncated VPD," " missing final 0x78 in record %.4s\n", rec_name ? rec_name : "????"); return NULL; } } return NULL; } /* Locate a keyword in a record in a VPD blob * * Note: This works with VPD LIDs. It will scan until it finds * the first 0x84, so it will skip all those 0's that the VPD * LIDs seem to contain */ const void *vpd_find(const void *vpd, size_t vpd_size, const char *record, const char *keyword, uint8_t *sz) { size_t rec_sz; const uint8_t *p; p = vpd_find_record(vpd, vpd_size, record, &rec_sz); if (p) p = vpd_find_keyword(p, rec_sz, keyword, sz); return p; } static void *vpd_lid; static size_t vpd_lid_size; static uint32_t vpd_lid_no; /* Helper to load a VPD LID. Pass a ptr to the corresponding LX keyword */ static void *vpd_lid_preload(const uint8_t *lx) { int rc; if (!fsp_present()) return NULL; /* Now this is a guess game as we don't have the info from the * pHyp folks. But basically, it seems to boil down to loading * a LID whose name is 0x80e000yy where yy is the last 2 digits * of the LX record in hex. * * [ Correction: After a chat with some folks, it looks like it's * actually 4 digits, though the lid number is limited to fff * so we weren't far off. ] * * For safety, we look for a matching LX record in an LXRn * (n = lxrn argument) or in VINI if lxrn=0xff */ vpd_lid_no = 0x80e00000 | ((lx[6] & 0xf) << 8) | lx[7]; /* We don't quite know how to get to the LID directory so * we don't know the size. Let's allocate 16K. All the VPD LIDs * I've seen so far are much smaller. */ #define VPD_LID_MAX_SIZE 0x4000 vpd_lid = malloc(VPD_LID_MAX_SIZE); if (!vpd_lid) { prerror("VPD: Failed to allocate memory for LID\n"); return NULL; } /* Adjust LID number for flash side */ vpd_lid_no = fsp_adjust_lid_side(vpd_lid_no); printf("VPD: Trying to load VPD LID 0x%08x...\n", vpd_lid_no); vpd_lid_size = VPD_LID_MAX_SIZE; /* Load it from the FSP */ rc = fsp_preload_lid(vpd_lid_no, vpd_lid, &vpd_lid_size); if (rc) { prerror("VPD: Error %d loading VPD LID\n", rc); goto fail; } return vpd_lid; fail: free(vpd_lid); return NULL; } void vpd_iohub_load(struct dt_node *hub_node) { uint8_t record[4] = { 'L','X','R','0' }; /* not null terminated */ const void *valid_lx; uint8_t lx_size; int r; const uint32_t *p; const uint8_t *lx; unsigned int lxrn; if (!fsp_present()) return; p = dt_prop_get_def(hub_node, "ibm,vpd-lx-info", NULL); if (!p) return; lxrn = p[0]; lx = (const char *)&p[1]; /* verify the lid preload has started */ if (!vpd_lid || !vpd_lid_no) { prlog(PR_WARNING, "VPD: WARNING: Unable to load VPD lid"); return; } r = fsp_wait_lid_loaded(vpd_lid_no); if (r) goto fail; /* Validate it */ if (lxrn < 9) record[3] = '0' + lxrn; else memcpy(record, "VINI", 4); valid_lx = vpd_find(vpd_lid, vpd_lid_size, record, "LX", &lx_size); if (!valid_lx || lx_size != 8) { prerror("VPD: Cannot find validation LX record\n"); goto fail; } if (memcmp(valid_lx, lx, 8) != 0) { prerror("VPD: LX record mismatch !\n"); goto fail; } printf("VPD: Loaded %zu bytes\n", vpd_lid_size); dt_add_property(hub_node, "ibm,io-vpd", vpd_lid, vpd_lid_size); free(vpd_lid); return; fail: free(vpd_lid); vpd_lid = NULL; prerror("VPD: Failed to load VPD LID\n"); return; } void vpd_preload(struct dt_node *hub_node) { const uint32_t *p; const char *lxr; p = dt_prop_get_def(hub_node, "ibm,vpd-lx-info", NULL); if (!p) return; lxr = (const char *)&p[1]; vpd_lid = vpd_lid_preload(lxr); } void preload_io_vpd(void) { const struct dt_property *prop; prop = dt_find_property(dt_root, "ibm,io-vpd"); if (!prop) { /* LX VPD Lid not already loaded */ vpd_preload(dt_root); } }