1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 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;
}
|