aboutsummaryrefslogtreecommitdiff
path: root/core/vpd.c
blob: 20fe09597c48edd247cac18c77191f5158dfaf24 (plain)
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;
}