aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/vpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/core/vpd.c')
-rw-r--r--roms/skiboot/core/vpd.c139
1 files changed, 139 insertions, 0 deletions
diff --git a/roms/skiboot/core/vpd.c b/roms/skiboot/core/vpd.c
new file mode 100644
index 000000000..20fe09597
--- /dev/null
+++ b/roms/skiboot/core/vpd.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Parse Vital Product Data (VPD)
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <skiboot.h>
+#include <vpd.h>
+#include <string.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;
+}
+
+/* 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;
+}