aboutsummaryrefslogtreecommitdiff
path: root/core/vpd.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-02 15:36:20 +1000
commit1d880992fd8c8457a2d990ac6622cfd58fb1b261 (patch)
treec4c843b12e96b5612c315db5a23c5da1a900618c /core/vpd.c
downloadskiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.zip
skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.gz
skiboot-1d880992fd8c8457a2d990ac6622cfd58fb1b261.tar.bz2
Initial commit of Open Source release
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'core/vpd.c')
-rw-r--r--core/vpd.c211
1 files changed, 211 insertions, 0 deletions
diff --git a/core/vpd.c b/core/vpd.c
new file mode 100644
index 0000000..deb552c
--- /dev/null
+++ b/core/vpd.c
@@ -0,0 +1,211 @@
+/* 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 <skiboot.h>
+#include <vpd.h>
+#include <string.h>
+#include <fsp.h>
+#include <device.h>
+
+#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;
+}
+
+/* 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;
+
+ 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) {
+ *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;
+}
+
+/* Helper to load a VPD LID. Pass a ptr to the corresponding LX keyword */
+static void *vpd_lid_load(const uint8_t *lx, uint8_t lxrn, size_t *size)
+{
+ /* 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
+ */
+ uint32_t 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
+ void *data = malloc(VPD_LID_MAX_SIZE);
+ char record[4] = "LXR0";
+ const void *valid_lx;
+ uint8_t lx_size;
+ int rc;
+
+ if (!data) {
+ prerror("VPD: Failed to allocate memory for LID\n");
+ return NULL;
+ }
+
+ /* Adjust LID number for flash side */
+ lid_no = fsp_adjust_lid_side(lid_no);
+ printf("VPD: Trying to load VPD LID 0x%08x...\n", lid_no);
+
+ *size = VPD_LID_MAX_SIZE;
+
+ /* Load it from the FSP */
+ rc = fsp_fetch_data(0, FSP_DATASET_NONSP_LID, lid_no, 0, data, size);
+ if (rc) {
+ prerror("VPD: Error %d loading VPD LID\n", rc);
+ goto fail;
+ }
+
+ /* Validate it */
+ if (lxrn < 9)
+ record[3] = '0' + lxrn;
+ else
+ memcpy(record, "VINI", 4);
+
+ valid_lx = vpd_find(data, *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", *size);
+
+ /* Got it ! */
+ return realloc(data, *size);
+ fail:
+ free(data);
+ return NULL;
+}
+
+void vpd_iohub_load(struct dt_node *hub_node)
+{
+ void *vpd;
+ size_t sz;
+ const uint32_t *p;
+ unsigned int lx_idx;
+ const char *lxr;
+
+ p = dt_prop_get_def(hub_node, "ibm,vpd-lx-info", NULL);
+ if (!p)
+ return;
+
+ lx_idx = p[0];
+ lxr = (const char *)&p[1];
+
+ vpd = vpd_lid_load(lxr, lx_idx, &sz);
+ if (!vpd) {
+ prerror("VPD: Failed to load VPD LID\n");
+ } else {
+ dt_add_property(hub_node, "ibm,io-vpd", vpd, sz);
+ free(vpd);
+ }
+}