blob: 9d718196cbb16e6b886910f636bfc768fb58d220 [file] [log] [blame]
John Wang02700402019-10-06 16:34:29 +08001#include <assert.h>
2#include <endian.h>
3#include <stdbool.h>
4#include <stdint.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "bios.h"
9#include "bios_table.h"
10
11#define POINTER_CHECK(pointer) \
12 do { \
13 if (pointer == NULL) \
14 return PLDM_ERROR_INVALID_DATA; \
15 } while (0)
16
17#define ATTR_TYPE_EXPECT(type, expected) \
18 do { \
19 if (type != expected && type != (expected | 0x80)) \
20 return PLDM_ERROR_INVALID_DATA; \
21 } while (0)
22
John Wang871c9272019-12-09 18:02:15 +080023static bool attribute_is_readonly(uint8_t attr_type)
24{
25 return (attr_type & 0x80);
26}
27
John Wang3ad21752019-10-06 16:42:21 +080028#define BUFFER_SIZE_EXPECT(current_size, expected_size) \
29 do { \
30 if (current_size < expected_size) \
31 return PLDM_ERROR_INVALID_LENGTH; \
32 } while (0)
33
John Wangdd9a6282019-10-11 18:52:46 +080034#define MEMBER_SIZE(type, member) sizeof(((struct type *)0)->member)
35
John Wang827c5de2019-11-07 18:27:27 +080036static void set_errmsg(const char **errmsg, const char *msg)
37{
38 if (errmsg != NULL)
39 *errmsg = msg;
40}
41
John Wangdd9a6282019-10-11 18:52:46 +080042static uint16_t get_bios_string_handle()
43{
44 static uint16_t handle = 0;
45 assert(handle != UINT16_MAX);
46
47 return handle++;
48}
49
50size_t pldm_bios_table_string_entry_encode_length(uint16_t string_length)
51{
52 return sizeof(struct pldm_bios_string_table_entry) -
53 MEMBER_SIZE(pldm_bios_string_table_entry, name) + string_length;
54}
55
56void pldm_bios_table_string_entry_encode(void *entry, size_t entry_length,
57 const char *str, uint16_t str_length)
58{
59 size_t length = pldm_bios_table_string_entry_encode_length(str_length);
60 assert(length <= entry_length);
61 struct pldm_bios_string_table_entry *string_entry = entry;
62 string_entry->string_handle = htole16(get_bios_string_handle());
63 string_entry->string_length = htole16(str_length);
64 memcpy(string_entry->name, str, str_length);
65}
66
67int pldm_bios_table_string_entry_encode_check(void *entry, size_t entry_length,
68 const char *str,
69 uint16_t str_length)
70{
71 if (str_length == 0)
72 return PLDM_ERROR_INVALID_DATA;
73 POINTER_CHECK(entry);
74 POINTER_CHECK(str);
75 size_t length = pldm_bios_table_string_entry_encode_length(str_length);
76 BUFFER_SIZE_EXPECT(entry_length, length);
77 pldm_bios_table_string_entry_encode(entry, entry_length, str,
78 str_length);
79 return PLDM_SUCCESS;
80}
81
82uint16_t pldm_bios_table_string_entry_decode_handle(
83 const struct pldm_bios_string_table_entry *entry)
84{
85 return le16toh(entry->string_handle);
86}
87
88uint16_t pldm_bios_table_string_entry_decode_string_length(
89 const struct pldm_bios_string_table_entry *entry)
90{
91 return le16toh(entry->string_length);
92}
93
94uint16_t pldm_bios_table_string_entry_decode_string(
95 const struct pldm_bios_string_table_entry *entry, char *buffer, size_t size)
96{
97 uint16_t length =
98 pldm_bios_table_string_entry_decode_string_length(entry);
99 length = length < size ? length : size;
100 memcpy(buffer, entry->name, length);
101 buffer[length] = 0;
102 return length;
103}
104
105int pldm_bios_table_string_entry_decode_string_check(
106 const struct pldm_bios_string_table_entry *entry, char *buffer, size_t size)
107{
108 POINTER_CHECK(entry);
109 POINTER_CHECK(buffer);
110 size_t length =
111 pldm_bios_table_string_entry_decode_string_length(entry);
112 BUFFER_SIZE_EXPECT(size, length + 1);
113 pldm_bios_table_string_entry_decode_string(entry, buffer, size);
114 return PLDM_SUCCESS;
115}
116
117static size_t string_table_entry_length(const void *table_entry)
118{
119 const struct pldm_bios_string_table_entry *entry = table_entry;
120 return sizeof(*entry) - sizeof(entry->name) +
121 pldm_bios_table_string_entry_decode_string_length(entry);
122}
123
John Wangccc04552019-10-14 14:28:25 +0800124static uint16_t get_bios_attr_handle()
125{
126 static uint16_t handle = 0;
127 assert(handle != UINT16_MAX);
128
129 return handle++;
130}
131
132static void attr_table_entry_encode_header(void *entry, size_t length,
133 uint8_t attr_type,
134 uint16_t string_handle)
135{
136 struct pldm_bios_attr_table_entry *attr_entry = entry;
137 assert(sizeof(*attr_entry) <= length);
138 attr_entry->attr_handle = htole16(get_bios_attr_handle());
139 attr_entry->attr_type = attr_type;
140 attr_entry->string_handle = htole16(string_handle);
141}
142
John Wangb0da7a02020-01-07 16:55:00 +0800143uint16_t pldm_bios_table_attr_entry_decode_attribute_handle(
144 const struct pldm_bios_attr_table_entry *entry)
145{
146 return le16toh(entry->attr_handle);
147}
148
149uint16_t pldm_bios_table_attr_entry_decode_string_handle(
150 const struct pldm_bios_attr_table_entry *entry)
151{
152 return le16toh(entry->string_handle);
153}
154
John Wangccc04552019-10-14 14:28:25 +0800155size_t pldm_bios_table_attr_entry_enum_encode_length(uint8_t pv_num,
156 uint8_t def_num)
157{
158 return sizeof(struct pldm_bios_attr_table_entry) -
159 MEMBER_SIZE(pldm_bios_attr_table_entry, metadata) +
160 sizeof(pv_num) + pv_num * sizeof(uint16_t) + sizeof(def_num) +
161 def_num;
162}
163
164void pldm_bios_table_attr_entry_enum_encode(
165 void *entry, size_t entry_length,
166 const struct pldm_bios_table_attr_entry_enum_info *info)
167{
168 size_t length = pldm_bios_table_attr_entry_enum_encode_length(
169 info->pv_num, info->def_num);
170 assert(length <= entry_length);
171 uint8_t attr_type = info->read_only ? PLDM_BIOS_ENUMERATION_READ_ONLY
172 : PLDM_BIOS_ENUMERATION;
173 attr_table_entry_encode_header(entry, entry_length, attr_type,
174 info->name_handle);
175 struct pldm_bios_attr_table_entry *attr_entry = entry;
176 attr_entry->metadata[0] = info->pv_num;
177 uint16_t *pv_hdls =
178 (uint16_t *)(attr_entry->metadata + 1 /* sizeof(pv num) */);
179 size_t i;
180 for (i = 0; i < info->pv_num; i++)
181 pv_hdls[i] = htole16(info->pv_handle[i]);
182 attr_entry->metadata[1 + info->pv_num * sizeof(uint16_t)] =
183 info->def_num;
184 memcpy(attr_entry->metadata + 1 /* sizeof(pv num) */ +
185 info->pv_num * sizeof(uint16_t) + 1 /* sizeof(def num)*/,
186 info->def_index, info->def_num);
187}
188
189int pldm_bios_table_attr_entry_enum_encode_check(
190 void *entry, size_t entry_length,
191 const struct pldm_bios_table_attr_entry_enum_info *info)
192{
193 POINTER_CHECK(entry);
194 POINTER_CHECK(info);
195 size_t length = pldm_bios_table_attr_entry_enum_encode_length(
196 info->pv_num, info->def_num);
197 BUFFER_SIZE_EXPECT(entry_length, length);
198 pldm_bios_table_attr_entry_enum_encode(entry, entry_length, info);
199 return PLDM_SUCCESS;
200}
201
John Wangdd9a6282019-10-11 18:52:46 +0800202#define ATTR_TYPE_EXPECT(type, expected) \
203 do { \
204 if (type != expected && type != (expected | 0x80)) \
205 return PLDM_ERROR_INVALID_DATA; \
206 } while (0)
207
John Wang02700402019-10-06 16:34:29 +0800208uint8_t pldm_bios_table_attr_entry_enum_decode_pv_num(
209 const struct pldm_bios_attr_table_entry *entry)
210{
211 return entry->metadata[0];
212}
213
214int pldm_bios_table_attr_entry_enum_decode_pv_num_check(
215 const struct pldm_bios_attr_table_entry *entry, uint8_t *pv_num)
216{
217 POINTER_CHECK(entry);
218 POINTER_CHECK(pv_num);
219 ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION);
220 *pv_num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
221 return PLDM_SUCCESS;
222}
223
224uint8_t pldm_bios_table_attr_entry_enum_decode_def_num(
225 const struct pldm_bios_attr_table_entry *entry)
226{
227 uint8_t pv_num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
228 return entry->metadata[sizeof(uint8_t) /* pv_num */ +
229 sizeof(uint16_t) * pv_num];
230}
231
232int pldm_bios_table_attr_entry_enum_decode_def_num_check(
233 const struct pldm_bios_attr_table_entry *entry, uint8_t *def_num)
234{
235 POINTER_CHECK(entry);
236 POINTER_CHECK(def_num);
237 ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION);
238 *def_num = pldm_bios_table_attr_entry_enum_decode_def_num(entry);
239 return PLDM_SUCCESS;
240}
241
John Wang3ad21752019-10-06 16:42:21 +0800242uint8_t pldm_bios_table_attr_entry_enum_decode_pv_hdls(
243 const struct pldm_bios_attr_table_entry *entry, uint16_t *pv_hdls,
244 uint8_t pv_num)
245{
246 uint8_t num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
247 num = num < pv_num ? num : pv_num;
248 size_t i;
249 for (i = 0; i < num; i++) {
250 uint16_t *hdl = (uint16_t *)(entry->metadata + sizeof(uint8_t) +
251 i * sizeof(uint16_t));
252 pv_hdls[i] = le16toh(*hdl);
253 }
254 return num;
255}
256
257int pldm_bios_table_attr_entry_enum_decode_pv_hdls_check(
258 const struct pldm_bios_attr_table_entry *entry, uint16_t *pv_hdls,
259 uint8_t pv_num)
260{
261 POINTER_CHECK(entry);
262 POINTER_CHECK(pv_hdls);
263 ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_ENUMERATION);
264 uint8_t num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
265 if (num != pv_num)
266 return PLDM_ERROR_INVALID_DATA;
267 pldm_bios_table_attr_entry_enum_decode_pv_hdls(entry, pv_hdls, pv_num);
268 return PLDM_SUCCESS;
269}
270
John Wang02700402019-10-06 16:34:29 +0800271/** @brief Get length of an enum attribute entry
272 */
John Wang49484a12019-12-02 14:21:53 +0800273static size_t attr_table_entry_length_enum(const void *entry)
John Wang02700402019-10-06 16:34:29 +0800274{
275 uint8_t pv_num = pldm_bios_table_attr_entry_enum_decode_pv_num(entry);
276 uint8_t def_num = pldm_bios_table_attr_entry_enum_decode_def_num(entry);
John Wangccc04552019-10-14 14:28:25 +0800277 return pldm_bios_table_attr_entry_enum_encode_length(pv_num, def_num);
John Wang02700402019-10-06 16:34:29 +0800278}
279
John Wangccc04552019-10-14 14:28:25 +0800280struct attr_table_string_entry_fields {
281 uint8_t string_type;
282 uint16_t min_length;
283 uint16_t max_length;
284 uint16_t def_length;
285 uint8_t def_string[1];
286} __attribute__((packed));
287
288size_t pldm_bios_table_attr_entry_string_encode_length(uint16_t def_str_len)
289{
290 return sizeof(struct pldm_bios_attr_table_entry) -
291 MEMBER_SIZE(pldm_bios_attr_table_entry, metadata) +
292 sizeof(struct attr_table_string_entry_fields) -
293 MEMBER_SIZE(attr_table_string_entry_fields, def_string) +
294 def_str_len;
295}
296
297void pldm_bios_table_attr_entry_string_encode(
298 void *entry, size_t entry_length,
299 const struct pldm_bios_table_attr_entry_string_info *info)
300{
301 size_t length =
302 pldm_bios_table_attr_entry_string_encode_length(info->def_length);
303 assert(length <= entry_length);
304 uint8_t attr_type =
305 info->read_only ? PLDM_BIOS_STRING_READ_ONLY : PLDM_BIOS_STRING;
306 attr_table_entry_encode_header(entry, entry_length, attr_type,
307 info->name_handle);
308 struct pldm_bios_attr_table_entry *attr_entry = entry;
309 struct attr_table_string_entry_fields *attr_fields =
310 (struct attr_table_string_entry_fields *)attr_entry->metadata;
311 attr_fields->string_type = info->string_type;
312 attr_fields->min_length = htole16(info->min_length);
313 attr_fields->max_length = htole16(info->max_length);
314 attr_fields->def_length = htole16(info->def_length);
315 if (info->def_length != 0 && info->def_string != NULL)
316 memcpy(attr_fields->def_string, info->def_string,
317 info->def_length);
318}
319
John Wang827c5de2019-11-07 18:27:27 +0800320#define PLDM_STRING_TYPE_MAX 5
321#define PLDM_STRING_TYPE_VENDOR 0xff
322
323int pldm_bios_table_attr_entry_string_info_check(
324 const struct pldm_bios_table_attr_entry_string_info *info,
325 const char **errmsg)
326{
327 if (info->min_length > info->max_length) {
328 set_errmsg(errmsg, "MinimumStingLength should not be greater "
329 "than MaximumStringLength");
330 return PLDM_ERROR_INVALID_DATA;
331 }
332 if (info->min_length == info->max_length &&
333 info->def_length != info->min_length) {
334 set_errmsg(errmsg, "Wrong DefaultStringLength");
335 return PLDM_ERROR_INVALID_DATA;
336 }
337 if (info->def_length > info->max_length ||
338 info->def_length < info->min_length) {
339 set_errmsg(errmsg, "Wrong DefaultStringLength");
340 return PLDM_ERROR_INVALID_DATA;
341 }
342 if (info->string_type > PLDM_STRING_TYPE_MAX &&
343 info->string_type != PLDM_STRING_TYPE_VENDOR) {
344 set_errmsg(errmsg, "Wrong StringType");
345 return PLDM_ERROR_INVALID_DATA;
346 }
347 if (info->def_length != strlen(info->def_string)) {
348 set_errmsg(errmsg, "Length of DefaultString should be equal to "
349 "DefaultStringLength");
350 return PLDM_ERROR_INVALID_DATA;
351 }
352
353 return PLDM_SUCCESS;
354}
355
John Wangccc04552019-10-14 14:28:25 +0800356int pldm_bios_table_attr_entry_string_encode_check(
357 void *entry, size_t entry_length,
358 const struct pldm_bios_table_attr_entry_string_info *info)
359{
360 POINTER_CHECK(entry);
361 POINTER_CHECK(info);
362 size_t length =
363 pldm_bios_table_attr_entry_string_encode_length(info->def_length);
364 BUFFER_SIZE_EXPECT(entry_length, length);
John Wang827c5de2019-11-07 18:27:27 +0800365 if (pldm_bios_table_attr_entry_string_info_check(info, NULL) !=
366 PLDM_SUCCESS)
John Wangccc04552019-10-14 14:28:25 +0800367 return PLDM_ERROR_INVALID_DATA;
368 pldm_bios_table_attr_entry_string_encode(entry, entry_length, info);
369 return PLDM_SUCCESS;
370}
John Wang02700402019-10-06 16:34:29 +0800371
372uint16_t pldm_bios_table_attr_entry_string_decode_def_string_length(
373 const struct pldm_bios_attr_table_entry *entry)
374{
John Wangccc04552019-10-14 14:28:25 +0800375 struct attr_table_string_entry_fields *fields =
376 (struct attr_table_string_entry_fields *)entry->metadata;
377 return le16toh(fields->def_length);
John Wang02700402019-10-06 16:34:29 +0800378}
379
380int pldm_bios_table_attr_entry_string_decode_def_string_length_check(
381 const struct pldm_bios_attr_table_entry *entry, uint16_t *def_string_length)
382{
383 POINTER_CHECK(entry);
384 POINTER_CHECK(def_string_length);
385 ATTR_TYPE_EXPECT(entry->attr_type, PLDM_BIOS_STRING);
386 *def_string_length =
387 pldm_bios_table_attr_entry_string_decode_def_string_length(entry);
388 return PLDM_SUCCESS;
389}
390
John Wangb0da7a02020-01-07 16:55:00 +0800391uint8_t pldm_bios_table_attr_entry_string_decode_string_type(
392 const struct pldm_bios_attr_table_entry *entry)
393{
394 struct attr_table_string_entry_fields *fields =
395 (struct attr_table_string_entry_fields *)entry->metadata;
396 return fields->string_type;
397}
398
John Wang02700402019-10-06 16:34:29 +0800399/** @brief Get length of a string attribute entry
400 */
John Wang49484a12019-12-02 14:21:53 +0800401static size_t attr_table_entry_length_string(const void *entry)
John Wang02700402019-10-06 16:34:29 +0800402{
John Wangccc04552019-10-14 14:28:25 +0800403 uint16_t def_str_len =
John Wang02700402019-10-06 16:34:29 +0800404 pldm_bios_table_attr_entry_string_decode_def_string_length(entry);
John Wangccc04552019-10-14 14:28:25 +0800405 return pldm_bios_table_attr_entry_string_encode_length(def_str_len);
John Wang02700402019-10-06 16:34:29 +0800406}
407
John Wangca230822019-10-16 11:39:27 +0800408struct attr_table_integer_entry_fields {
409 uint64_t lower_bound;
410 uint64_t upper_bound;
411 uint32_t scalar_increment;
412 uint64_t default_value;
413} __attribute__((packed));
414
415size_t pldm_bios_table_attr_entry_integer_encode_length()
416{
417 return sizeof(struct pldm_bios_attr_table_entry) - 1 +
418 sizeof(struct attr_table_integer_entry_fields);
419}
420
421void pldm_bios_table_attr_entry_integer_encode(
422 void *entry, size_t entry_length,
423 const struct pldm_bios_table_attr_entry_integer_info *info)
424{
425 size_t length = pldm_bios_table_attr_entry_integer_encode_length();
426 assert(length <= entry_length);
427 uint8_t attr_type =
428 info->read_only ? PLDM_BIOS_INTEGER_READ_ONLY : PLDM_BIOS_INTEGER;
429 attr_table_entry_encode_header(entry, entry_length, attr_type,
430 info->name_handle);
431 struct pldm_bios_attr_table_entry *attr_entry = entry;
432 struct attr_table_integer_entry_fields *attr_fields =
433 (struct attr_table_integer_entry_fields *)attr_entry->metadata;
434 attr_fields->lower_bound = htole64(info->lower_bound);
435 attr_fields->upper_bound = htole64(info->upper_bound);
436 attr_fields->scalar_increment = htole32(info->scalar_increment);
437 attr_fields->default_value = htole64(info->default_value);
438}
439
John Wang827c5de2019-11-07 18:27:27 +0800440int pldm_bios_table_attr_entry_integer_info_check(
441 const struct pldm_bios_table_attr_entry_integer_info *info,
442 const char **errmsg)
443{
444 if (info->lower_bound == info->upper_bound) {
445 if (info->default_value != info->lower_bound) {
446 set_errmsg(errmsg, "Wrong DefaultValue");
447 return PLDM_ERROR_INVALID_DATA;
448 }
449 if (info->scalar_increment != 0) {
450 set_errmsg(errmsg, "Wrong ScalarIncrement");
451 return PLDM_ERROR_INVALID_DATA;
452 }
453 return PLDM_SUCCESS;
454 }
455 if (info->lower_bound > info->upper_bound) {
456 set_errmsg(errmsg,
457 "LowerBound should not be greater than UpperBound");
458 return PLDM_ERROR_INVALID_DATA;
459 }
460 if (info->default_value > info->upper_bound ||
461 info->default_value < info->lower_bound) {
462 set_errmsg(errmsg, "Wrong DefaultValue");
463 return PLDM_ERROR_INVALID_DATA;
464 }
465 if (info->scalar_increment == 0) {
466 set_errmsg(errmsg, "ScalarIncrement should not be zero when "
467 "lower_bound != upper_bound");
468 return PLDM_ERROR_INVALID_DATA;
469 }
470 if ((info->default_value - info->lower_bound) %
471 info->scalar_increment !=
472 0) {
473 set_errmsg(errmsg, "Wrong DefaultValue or ScalarIncrement");
474 return PLDM_ERROR_INVALID_DATA;
475 }
476 return PLDM_SUCCESS;
477}
478
John Wangca230822019-10-16 11:39:27 +0800479int pldm_bios_table_attr_entry_integer_encode_check(
480 void *entry, size_t entry_length,
481 const struct pldm_bios_table_attr_entry_integer_info *info)
482{
483 POINTER_CHECK(entry);
484 POINTER_CHECK(info);
485 size_t length = pldm_bios_table_attr_entry_integer_encode_length();
486 BUFFER_SIZE_EXPECT(entry_length, length);
John Wang827c5de2019-11-07 18:27:27 +0800487 if (pldm_bios_table_attr_entry_integer_info_check(info, NULL) !=
488 PLDM_SUCCESS)
John Wangca230822019-10-16 11:39:27 +0800489 return PLDM_ERROR_INVALID_DATA;
490 pldm_bios_table_attr_entry_integer_encode(entry, entry_length, info);
491 return PLDM_SUCCESS;
492}
493
John Wang49484a12019-12-02 14:21:53 +0800494static size_t attr_table_entry_length_integer(const void *entry)
John Wangca230822019-10-16 11:39:27 +0800495{
496 (void)entry;
497 return pldm_bios_table_attr_entry_integer_encode_length();
498}
499
John Wang49484a12019-12-02 14:21:53 +0800500struct table_entry_length {
John Wang02700402019-10-06 16:34:29 +0800501 uint8_t attr_type;
John Wang49484a12019-12-02 14:21:53 +0800502 size_t (*entry_length_handler)(const void *);
John Wang02700402019-10-06 16:34:29 +0800503};
504
John Wang49484a12019-12-02 14:21:53 +0800505#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
506
John Wangb6333cb2019-12-10 09:43:42 +0800507static const struct table_entry_length *find_table_entry_length_by_type(
508 uint8_t attr_type, const struct table_entry_length *handlers, size_t count)
John Wang49484a12019-12-02 14:21:53 +0800509{
510 size_t i;
511 for (i = 0; i < count; i++) {
512 if (attr_type == handlers[i].attr_type)
513 return &handlers[i];
514 }
515 return NULL;
516}
517
John Wangb6333cb2019-12-10 09:43:42 +0800518static const struct table_entry_length attr_table_entries[] = {
John Wang02700402019-10-06 16:34:29 +0800519 {.attr_type = PLDM_BIOS_ENUMERATION,
520 .entry_length_handler = attr_table_entry_length_enum},
521 {.attr_type = PLDM_BIOS_ENUMERATION_READ_ONLY,
522 .entry_length_handler = attr_table_entry_length_enum},
523 {.attr_type = PLDM_BIOS_STRING,
524 .entry_length_handler = attr_table_entry_length_string},
525 {.attr_type = PLDM_BIOS_STRING_READ_ONLY,
526 .entry_length_handler = attr_table_entry_length_string},
John Wangca230822019-10-16 11:39:27 +0800527 {.attr_type = PLDM_BIOS_INTEGER,
528 .entry_length_handler = attr_table_entry_length_integer},
529 {.attr_type = PLDM_BIOS_INTEGER_READ_ONLY,
530 .entry_length_handler = attr_table_entry_length_integer},
John Wang02700402019-10-06 16:34:29 +0800531};
532
John Wang02700402019-10-06 16:34:29 +0800533static size_t attr_table_entry_length(const void *table_entry)
534{
535 const struct pldm_bios_attr_table_entry *entry = table_entry;
John Wangb6333cb2019-12-10 09:43:42 +0800536 const struct table_entry_length *attr_table_entry =
John Wang49484a12019-12-02 14:21:53 +0800537 find_table_entry_length_by_type(entry->attr_type,
538 attr_table_entries,
539 ARRAY_SIZE(attr_table_entries));
John Wang02700402019-10-06 16:34:29 +0800540 assert(attr_table_entry != NULL);
541 assert(attr_table_entry->entry_length_handler != NULL);
542
543 return attr_table_entry->entry_length_handler(entry);
544}
545
John Wangb0da7a02020-01-07 16:55:00 +0800546uint16_t pldm_bios_table_attr_value_entry_decode_attribute_handle(
547 const struct pldm_bios_attr_val_table_entry *entry)
548{
549 return le16toh(entry->attr_handle);
550}
551
552uint8_t pldm_bios_table_attr_value_entry_decode_attribute_type(
553 const struct pldm_bios_attr_val_table_entry *entry)
554{
555 return entry->attr_type;
556}
557
John Wang3ad21752019-10-06 16:42:21 +0800558size_t pldm_bios_table_attr_value_entry_encode_enum_length(uint8_t count)
559{
560 return sizeof(struct pldm_bios_attr_val_table_entry) - 1 +
561 sizeof(count) + count;
562}
563
564void pldm_bios_table_attr_value_entry_encode_enum(
565 void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
566 uint8_t count, uint8_t *handles)
567{
568 size_t length =
569 pldm_bios_table_attr_value_entry_encode_enum_length(count);
570 assert(length <= entry_length);
571
572 struct pldm_bios_attr_val_table_entry *table_entry = entry;
573 table_entry->attr_handle = htole16(attr_handle);
574 table_entry->attr_type = attr_type;
575 table_entry->value[0] = count;
576 if (count != 0)
577 memcpy(&table_entry->value[1], handles, count);
578}
579
John Wang49484a12019-12-02 14:21:53 +0800580uint8_t pldm_bios_table_attr_value_entry_enum_decode_number(
581 const struct pldm_bios_attr_val_table_entry *entry)
582{
583 return entry->value[0];
584}
585
John Wangb0da7a02020-01-07 16:55:00 +0800586uint8_t pldm_bios_table_attr_value_entry_enum_decode_handles(
587 const struct pldm_bios_attr_val_table_entry *entry, uint8_t *handles,
588 uint8_t number)
589{
590 uint8_t curr_num =
591 pldm_bios_table_attr_value_entry_enum_decode_number(entry);
592 number = number < curr_num ? number : curr_num;
593 memcpy(handles, &entry->value[1], number);
594
595 return number;
596}
597
John Wang3ad21752019-10-06 16:42:21 +0800598int pldm_bios_table_attr_value_entry_encode_enum_check(
599 void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
600 uint8_t count, uint8_t *handles)
601{
602 POINTER_CHECK(entry);
603 if (count != 0 && handles == NULL)
604 return PLDM_ERROR_INVALID_DATA;
605 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_ENUMERATION);
606 size_t length =
607 pldm_bios_table_attr_value_entry_encode_enum_length(count);
608 BUFFER_SIZE_EXPECT(entry_length, length);
609 pldm_bios_table_attr_value_entry_encode_enum(
610 entry, entry_length, attr_handle, attr_type, count, handles);
611 return PLDM_SUCCESS;
612}
613
John Wang49484a12019-12-02 14:21:53 +0800614static size_t attr_value_table_entry_length_enum(const void *entry)
615{
616 uint8_t number =
617 pldm_bios_table_attr_value_entry_enum_decode_number(entry);
618 return pldm_bios_table_attr_value_entry_encode_enum_length(number);
619}
620
John Wang3ad21752019-10-06 16:42:21 +0800621size_t
622pldm_bios_table_attr_value_entry_encode_string_length(uint16_t string_length)
623{
624 return sizeof(struct pldm_bios_attr_val_table_entry) - 1 +
625 sizeof(string_length) + string_length;
626}
627
628void pldm_bios_table_attr_value_entry_encode_string(
629 void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
630 uint16_t str_length, const char *str)
631{
632 size_t length =
633 pldm_bios_table_attr_value_entry_encode_string_length(str_length);
634 assert(length <= entry_length);
635
636 struct pldm_bios_attr_val_table_entry *table_entry = entry;
637 table_entry->attr_handle = htole16(attr_handle);
638 table_entry->attr_type = attr_type;
639 if (str_length != 0)
640 memcpy(table_entry->value + sizeof(str_length), str,
641 str_length);
642 str_length = htole16(str_length);
643 memcpy(table_entry->value, &str_length, sizeof(str_length));
644}
645
John Wang49484a12019-12-02 14:21:53 +0800646uint16_t pldm_bios_table_attr_value_entry_string_decode_length(
647 const struct pldm_bios_attr_val_table_entry *entry)
648{
649 uint16_t str_length = 0;
650 memcpy(&str_length, entry->value, sizeof(str_length));
651 return le16toh(str_length);
652}
653
John Wangb0da7a02020-01-07 16:55:00 +0800654void pldm_bios_table_attr_value_entry_string_decode_string(
655 const struct pldm_bios_attr_val_table_entry *entry,
656 struct variable_field *current_string)
657{
658 current_string->length =
659 pldm_bios_table_attr_value_entry_string_decode_length(entry);
660 current_string->ptr =
661 entry->value + sizeof(uint16_t); // sizeof(CurrentStringLength)
662}
663
John Wang3ad21752019-10-06 16:42:21 +0800664int pldm_bios_table_attr_value_entry_encode_string_check(
665 void *entry, size_t entry_length, uint16_t attr_handle, uint8_t attr_type,
666 uint16_t str_length, const char *str)
667{
668 POINTER_CHECK(entry);
669 if (str_length != 0 && str == NULL)
670 return PLDM_ERROR_INVALID_DATA;
671 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_STRING);
672 size_t length =
673 pldm_bios_table_attr_value_entry_encode_string_length(str_length);
674 BUFFER_SIZE_EXPECT(entry_length, length);
675 pldm_bios_table_attr_value_entry_encode_string(
676 entry, entry_length, attr_handle, attr_type, str_length, str);
677 return PLDM_SUCCESS;
678}
679
John Wang49484a12019-12-02 14:21:53 +0800680static size_t attr_value_table_entry_length_string(const void *entry)
681{
682 uint16_t str_length =
683 pldm_bios_table_attr_value_entry_string_decode_length(entry);
684 return pldm_bios_table_attr_value_entry_encode_string_length(
685 str_length);
686}
687
John Wangca230822019-10-16 11:39:27 +0800688size_t pldm_bios_table_attr_value_entry_encode_integer_length()
689{
690 return sizeof(struct pldm_bios_attr_val_table_entry) - 1 +
691 sizeof(uint64_t);
692}
693void pldm_bios_table_attr_value_entry_encode_integer(void *entry,
694 size_t entry_length,
695 uint16_t attr_handle,
696 uint8_t attr_type,
697 uint64_t cv)
698{
699 size_t length =
700 pldm_bios_table_attr_value_entry_encode_integer_length();
701 assert(length <= entry_length);
702
703 struct pldm_bios_attr_val_table_entry *table_entry = entry;
704 table_entry->attr_handle = htole16(attr_handle);
705 table_entry->attr_type = attr_type;
706 cv = htole64(cv);
707 memcpy(table_entry->value, &cv, sizeof(uint64_t));
708}
709
710int pldm_bios_table_attr_value_entry_encode_integer_check(void *entry,
711 size_t entry_length,
712 uint16_t attr_handle,
713 uint8_t attr_type,
714 uint64_t cv)
715{
716 POINTER_CHECK(entry);
717 size_t length =
718 pldm_bios_table_attr_value_entry_encode_integer_length();
719 ATTR_TYPE_EXPECT(attr_type, PLDM_BIOS_INTEGER);
720 BUFFER_SIZE_EXPECT(entry_length, length);
721 pldm_bios_table_attr_value_entry_encode_integer(
722 entry, entry_length, attr_handle, attr_type, cv);
723 return PLDM_SUCCESS;
724}
725
John Wangb0da7a02020-01-07 16:55:00 +0800726uint64_t pldm_bios_table_attr_value_entry_integer_decode_cv(
727 const struct pldm_bios_attr_val_table_entry *entry)
728{
729 uint64_t cv = 0;
730 memcpy(&cv, entry->value, sizeof(cv));
731 cv = le64toh(cv);
732 return cv;
733}
734
John Wang49484a12019-12-02 14:21:53 +0800735static size_t attr_value_table_entry_length_integer(const void *entry)
736{
737 (void)entry;
738 return pldm_bios_table_attr_value_entry_encode_integer_length();
739}
740
John Wangb6333cb2019-12-10 09:43:42 +0800741static const struct table_entry_length attr_value_table_entries[] = {
John Wang49484a12019-12-02 14:21:53 +0800742 {.attr_type = PLDM_BIOS_ENUMERATION,
743 .entry_length_handler = attr_value_table_entry_length_enum},
744 {.attr_type = PLDM_BIOS_ENUMERATION_READ_ONLY,
745 .entry_length_handler = attr_value_table_entry_length_enum},
746 {.attr_type = PLDM_BIOS_STRING,
747 .entry_length_handler = attr_value_table_entry_length_string},
748 {.attr_type = PLDM_BIOS_STRING_READ_ONLY,
749 .entry_length_handler = attr_value_table_entry_length_string},
750 {.attr_type = PLDM_BIOS_INTEGER,
751 .entry_length_handler = attr_value_table_entry_length_integer},
752 {.attr_type = PLDM_BIOS_INTEGER_READ_ONLY,
753 .entry_length_handler = attr_value_table_entry_length_integer},
754};
755
756static size_t attr_value_table_entry_length(const void *table_entry)
757{
758 const struct pldm_bios_attr_val_table_entry *entry = table_entry;
John Wangb6333cb2019-12-10 09:43:42 +0800759 const struct table_entry_length *entry_length =
John Wang49484a12019-12-02 14:21:53 +0800760 find_table_entry_length_by_type(
761 entry->attr_type, attr_value_table_entries,
762 ARRAY_SIZE(attr_value_table_entries));
763 assert(entry_length != NULL);
764 assert(entry_length->entry_length_handler != NULL);
765
766 return entry_length->entry_length_handler(entry);
767}
768
John Wang8e877e02020-02-03 16:06:55 +0800769size_t pldm_bios_table_attr_value_entry_length(
770 const struct pldm_bios_attr_val_table_entry *entry)
771{
772 return attr_value_table_entry_length(entry);
773}
774
John Wang3342adb2019-11-29 16:03:58 +0800775uint16_t pldm_bios_table_attr_value_entry_decode_handle(
776 const struct pldm_bios_attr_val_table_entry *entry)
777{
778 return le16toh(entry->attr_handle);
779}
780
John Wang79c37f12019-10-31 15:46:31 +0800781static size_t pad_size_get(size_t size_without_pad)
782{
783 return ((size_without_pad % 4) ? (4 - size_without_pad % 4) : 0);
784}
785
786static uint8_t *pad_append(uint8_t *table_end, size_t pad_size)
787{
788 while (pad_size--)
789 *table_end++ = 0;
790
791 return table_end;
792}
793
794static uint8_t *checksum_append(uint8_t *table_end, uint32_t checksum)
795{
796 checksum = htole32(checksum);
797 memcpy(table_end, &checksum, sizeof(checksum));
798
799 return table_end + sizeof(checksum);
800}
801
802size_t pldm_bios_table_pad_checksum_size(size_t size_without_pad)
803{
804 size_t size = pad_size_get(size_without_pad) +
805 sizeof(uint32_t) /*sizeof(checksum)*/;
806 return size;
807}
808
John Wang871c9272019-12-09 18:02:15 +0800809size_t pldm_bios_table_append_pad_checksum(void *table, size_t size,
810 size_t size_without_pad)
John Wang79c37f12019-10-31 15:46:31 +0800811{
812
813 size_t pad_checksum_size =
814 pldm_bios_table_pad_checksum_size(size_without_pad);
John Wang871c9272019-12-09 18:02:15 +0800815 size_t total_length = size_without_pad + pad_checksum_size;
816 assert(size >= total_length);
John Wang79c37f12019-10-31 15:46:31 +0800817
818 uint8_t *table_end = (uint8_t *)table + size_without_pad;
819 size_t pad_size = pad_size_get(size_without_pad);
820 table_end = pad_append(table_end, pad_size);
821
822 uint32_t checksum = crc32(table, size_without_pad + pad_size);
823 checksum_append(table_end, checksum);
John Wang871c9272019-12-09 18:02:15 +0800824
825 return total_length;
John Wang79c37f12019-10-31 15:46:31 +0800826}
827
John Wang02700402019-10-06 16:34:29 +0800828struct pldm_bios_table_iter {
829 const uint8_t *table_data;
830 size_t table_len;
831 size_t current_pos;
832 size_t (*entry_length_handler)(const void *table_entry);
833};
834
835struct pldm_bios_table_iter *
836pldm_bios_table_iter_create(const void *table, size_t length,
837 enum pldm_bios_table_types type)
838{
839 struct pldm_bios_table_iter *iter = malloc(sizeof(*iter));
840 assert(iter != NULL);
841 iter->table_data = table;
842 iter->table_len = length;
843 iter->current_pos = 0;
844 iter->entry_length_handler = NULL;
845 switch (type) {
846 case PLDM_BIOS_STRING_TABLE:
John Wangdd9a6282019-10-11 18:52:46 +0800847 iter->entry_length_handler = string_table_entry_length;
John Wang02700402019-10-06 16:34:29 +0800848 break;
849 case PLDM_BIOS_ATTR_TABLE:
850 iter->entry_length_handler = attr_table_entry_length;
851 break;
852 case PLDM_BIOS_ATTR_VAL_TABLE:
John Wang49484a12019-12-02 14:21:53 +0800853 iter->entry_length_handler = attr_value_table_entry_length;
John Wang02700402019-10-06 16:34:29 +0800854 break;
855 }
856
857 return iter;
858}
859
860void pldm_bios_table_iter_free(struct pldm_bios_table_iter *iter)
861{
862 free(iter);
863}
864
865#define pad_and_check_max 7
866bool pldm_bios_table_iter_is_end(const struct pldm_bios_table_iter *iter)
867{
868 if (iter->table_len - iter->current_pos <= pad_and_check_max)
869 return true;
870 return false;
871}
872
873void pldm_bios_table_iter_next(struct pldm_bios_table_iter *iter)
874{
875 if (pldm_bios_table_iter_is_end(iter))
876 return;
877 const void *entry = iter->table_data + iter->current_pos;
878 iter->current_pos += iter->entry_length_handler(entry);
879}
880
881const void *pldm_bios_table_iter_value(struct pldm_bios_table_iter *iter)
882{
883 return iter->table_data + iter->current_pos;
John Wang3ad21752019-10-06 16:42:21 +0800884}
John Wangdd9a6282019-10-11 18:52:46 +0800885
John Wang3342adb2019-11-29 16:03:58 +0800886typedef bool (*equal_handler)(const void *entry, const void *key);
887
John Wangdd9a6282019-10-11 18:52:46 +0800888static const void *
John Wang3342adb2019-11-29 16:03:58 +0800889pldm_bios_table_entry_find_by_iter(struct pldm_bios_table_iter *iter,
890 const void *key, equal_handler equal)
John Wangdd9a6282019-10-11 18:52:46 +0800891{
892 const void *entry;
893 while (!pldm_bios_table_iter_is_end(iter)) {
894 entry = pldm_bios_table_iter_value(iter);
895 if (equal(entry, key))
896 return entry;
897 pldm_bios_table_iter_next(iter);
898 }
899 return NULL;
900}
901
John Wang3342adb2019-11-29 16:03:58 +0800902static const void *
903pldm_bios_table_entry_find_from_table(const void *table, size_t length,
904 enum pldm_bios_table_types type,
905 equal_handler equal, const void *key)
906{
907 struct pldm_bios_table_iter *iter =
908 pldm_bios_table_iter_create(table, length, type);
909 const void *entry =
910 pldm_bios_table_entry_find_by_iter(iter, key, equal);
911 pldm_bios_table_iter_free(iter);
912 return entry;
913}
914
915static bool string_table_handle_equal(const void *entry, const void *key)
John Wangdd9a6282019-10-11 18:52:46 +0800916{
917 const struct pldm_bios_string_table_entry *string_entry = entry;
918 uint16_t handle = *(uint16_t *)key;
919 if (pldm_bios_table_string_entry_decode_handle(string_entry) == handle)
920 return true;
921 return false;
922}
923
John Wang3342adb2019-11-29 16:03:58 +0800924const struct pldm_bios_string_table_entry *
925pldm_bios_table_string_find_by_handle(const void *table, size_t length,
926 uint16_t handle)
927{
928 return pldm_bios_table_entry_find_from_table(
929 table, length, PLDM_BIOS_STRING_TABLE, string_table_handle_equal,
930 &handle);
931}
932
John Wangdd9a6282019-10-11 18:52:46 +0800933struct string_equal_arg {
934 uint16_t str_length;
935 const char *str;
936};
937
John Wang3342adb2019-11-29 16:03:58 +0800938static bool string_table_string_equal(const void *entry, const void *key)
John Wangdd9a6282019-10-11 18:52:46 +0800939{
940 const struct pldm_bios_string_table_entry *string_entry = entry;
941 const struct string_equal_arg *arg = key;
942 if (arg->str_length !=
943 pldm_bios_table_string_entry_decode_string_length(string_entry))
944 return false;
945 if (memcmp(string_entry->name, arg->str, arg->str_length) != 0)
946 return false;
947 return true;
948}
949
950const struct pldm_bios_string_table_entry *
951pldm_bios_table_string_find_by_string(const void *table, size_t length,
952 const char *str)
953{
954 uint16_t str_length = strlen(str);
955 struct string_equal_arg arg = {str_length, str};
John Wang3342adb2019-11-29 16:03:58 +0800956 return pldm_bios_table_entry_find_from_table(
957 table, length, PLDM_BIOS_STRING_TABLE, string_table_string_equal,
958 &arg);
John Wangdd9a6282019-10-11 18:52:46 +0800959}
960
John Wangb0da7a02020-01-07 16:55:00 +0800961static bool attr_table_handle_equal(const void *entry, const void *key)
962{
963 uint16_t handle = *(uint16_t *)key;
964 return pldm_bios_table_attr_entry_decode_attribute_handle(entry) ==
965 handle;
966}
967
968const struct pldm_bios_attr_table_entry *
969pldm_bios_table_attr_find_by_handle(const void *table, size_t length,
970 uint16_t handle)
971{
972 return pldm_bios_table_entry_find_from_table(
973 table, length, PLDM_BIOS_ATTR_TABLE, attr_table_handle_equal,
974 &handle);
975}
976
John Wang3342adb2019-11-29 16:03:58 +0800977static bool attr_value_table_handle_equal(const void *entry, const void *key)
John Wangdd9a6282019-10-11 18:52:46 +0800978{
John Wang3342adb2019-11-29 16:03:58 +0800979 uint16_t handle = *(uint16_t *)key;
980 return pldm_bios_table_attr_value_entry_decode_handle(entry) == handle;
981}
982
983const struct pldm_bios_attr_val_table_entry *
984pldm_bios_table_attr_value_find_by_handle(const void *table, size_t length,
985 uint16_t handle)
986{
987 return pldm_bios_table_entry_find_from_table(
988 table, length, PLDM_BIOS_ATTR_VAL_TABLE,
989 attr_value_table_handle_equal, &handle);
John Wangdd9a6282019-10-11 18:52:46 +0800990}
John Wang871c9272019-12-09 18:02:15 +0800991
992int pldm_bios_table_attr_value_copy_and_update(
993 const void *src_table, size_t src_length, void *dest_table,
994 size_t *dest_length, const void *entry, size_t entry_length)
995{
996 struct pldm_bios_table_iter *iter = pldm_bios_table_iter_create(
997 src_table, src_length, PLDM_BIOS_ATTR_VAL_TABLE);
998
999 int rc = PLDM_SUCCESS;
1000 const struct pldm_bios_attr_val_table_entry *tmp;
1001 size_t buffer_length = *dest_length, copied_length = 0, length = 0;
1002 while (!pldm_bios_table_iter_is_end(iter)) {
1003 tmp = pldm_bios_table_iter_attr_value_entry_value(iter);
1004 length = attr_value_table_entry_length(tmp);
1005
1006 /* we need the tmp's entry_length here, iter_next will calculate
1007 * it too, use current_pos directly to avoid calculating it
1008 * twice */
1009 iter->current_pos += length;
1010 if (tmp->attr_handle ==
1011 ((const struct pldm_bios_attr_val_table_entry *)entry)
1012 ->attr_handle) {
1013 if (attribute_is_readonly(tmp->attr_type)) {
1014 rc = PLDM_ERROR_INVALID_DATA;
1015 goto out;
1016 }
1017 length = entry_length;
1018 tmp = entry;
1019 }
1020 if (copied_length + length > buffer_length) {
1021 rc = PLDM_ERROR_INVALID_LENGTH;
1022 goto out;
1023 }
1024 memcpy((uint8_t *)dest_table + copied_length, tmp, length);
1025 copied_length += length;
1026 }
1027
1028 size_t pad_checksum_size =
1029 pldm_bios_table_pad_checksum_size(copied_length);
1030 if ((pad_checksum_size + copied_length) > buffer_length) {
1031 rc = PLDM_ERROR_INVALID_LENGTH;
1032 goto out;
1033 }
1034
1035 *dest_length = pldm_bios_table_append_pad_checksum(
1036 dest_table, buffer_length, copied_length);
1037out:
1038 pldm_bios_table_iter_free(iter);
1039 return rc;
1040}