diff options
Diffstat (limited to 'roms/skiboot/core/vpd.c')
-rw-r--r-- | roms/skiboot/core/vpd.c | 139 |
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; +} |