139 lines
3 KiB
C
139 lines
3 KiB
C
// 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;
|
|
}
|