blob: 20fe09597c48edd247cac18c77191f5158dfaf24 [file] [log] [blame]
// 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;
}