acpi: Add IORT helper functions
[coreboot2.git] / src / drivers / ipmi / ipmi_fru.c
blob844eb8e8a3c024c7dfffe13d5531caebee88ace3
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <console/console.h>
4 #include <string.h>
5 #include <delay.h>
6 #include <stdlib.h>
8 #include "ipmi_if.h"
9 #include "ipmi_ops.h"
11 #define MAX_FRU_BUSY_RETRY 5
12 #define READ_FRU_DATA_RETRY_INTERVAL_MS 30 /* From IPMI spec v2.0 rev 1.1 */
13 #define OFFSET_LENGTH_MULTIPLIER 8 /* offsets/lengths are multiples of 8 */
14 #define NUM_DATA_BYTES(t) (t & 0x3f) /* Encoded in type/length byte */
15 #define FRU_END_OF_FIELDS 0xc1 /* type/length byte encoded to indicate no more info fields */
17 static enum cb_err ipmi_read_fru(const int port, struct ipmi_read_fru_data_req *req,
18 uint8_t *fru_data)
20 int ret;
21 uint8_t total_size;
22 uint16_t offset = 0;
23 struct ipmi_read_fru_data_rsp rsp;
24 int retry_count = 0;
26 if (req == NULL || fru_data == NULL) {
27 printk(BIOS_ERR, "%s failed, null pointer parameter\n",
28 __func__);
29 return CB_ERR;
32 total_size = req->count;
33 do {
34 if (req->count > CONFIG_IPMI_FRU_SINGLE_RW_SZ)
35 req->count = CONFIG_IPMI_FRU_SINGLE_RW_SZ;
37 while (retry_count <= MAX_FRU_BUSY_RETRY) {
38 ret = ipmi_message(port, IPMI_NETFN_STORAGE, 0x0,
39 IPMI_READ_FRU_DATA, (const unsigned char *)req,
40 sizeof(*req), (unsigned char *)&rsp, sizeof(rsp));
41 if (rsp.resp.completion_code == 0x81) {
42 /* Device is busy */
43 if (retry_count == MAX_FRU_BUSY_RETRY) {
44 printk(BIOS_ERR, "IPMI: %s command failed, "
45 "device busy timeout\n", __func__);
46 return CB_ERR;
48 printk(BIOS_ERR, "IPMI: FRU device is busy, "
49 "retry count:%d\n", retry_count);
50 retry_count++;
51 mdelay(READ_FRU_DATA_RETRY_INTERVAL_MS);
52 } else if (ret < sizeof(struct ipmi_rsp) || rsp.resp.completion_code) {
53 printk(BIOS_ERR, "IPMI: %s command failed (ret=%d resp=0x%x)\n",
54 __func__, ret, rsp.resp.completion_code);
55 return CB_ERR;
57 break;
59 retry_count = 0;
60 memcpy(fru_data + offset, rsp.data, rsp.count);
61 offset += rsp.count;
62 total_size -= rsp.count;
63 req->fru_offset += rsp.count;
64 req->count = total_size;
65 } while (total_size > 0);
67 return CB_SUCCESS;
70 /* data: data to check, offset: offset to checksum. */
71 static uint8_t checksum(uint8_t *data, int offset)
73 uint8_t c = 0;
74 for (; offset > 0; offset--, data++)
75 c += *data;
76 return -c;
79 static uint8_t data2str(const uint8_t *frudata, char *stringdata, uint8_t length)
81 uint8_t type;
83 /* bit[7:6] is the type code. */
84 type = ((frudata[0] & 0xc0) >> 6);
85 if (type != ASCII_8BIT) {
86 printk(BIOS_ERR, "%s typecode %d is unsupported, FRU string only "
87 "supports 8-bit ASCII + Latin 1 for now.\n", __func__, type);
88 return 0;
90 /* In the spec the string data is always the next byte to the type/length byte. */
91 memcpy(stringdata, frudata + 1, length);
92 stringdata[length] = '\0';
93 return length;
97 * Read data string from data_ptr and store it to string, return the
98 * length of the string or 0 when it's failed.
100 static int read_data_string(const uint8_t *data_ptr, char **string)
102 uint8_t length;
104 length = NUM_DATA_BYTES(data_ptr[0]);
105 if (length == 0) {
106 printk(BIOS_DEBUG, "%s:%d - failed due to length is zero\n", __func__,
107 __LINE__);
108 return 0;
111 *string = malloc(length + 1);
112 if (!*string) {
113 printk(BIOS_ERR, "%s failed to malloc %d bytes for string data.\n", __func__,
114 length + 1);
115 return 0;
117 if (!data2str((const uint8_t *)data_ptr, *string, length)) {
118 printk(BIOS_ERR, "%s:%d - data2str failed\n", __func__, __LINE__);
119 free(*string);
120 return 0;
123 return length;
126 static enum cb_err read_fru_chassis_info_area(const int port, const uint8_t id,
127 uint8_t offset, struct fru_chassis_info *info)
129 uint8_t length;
130 struct ipmi_read_fru_data_req req;
131 uint8_t *data_ptr, *end, *custom_data_ptr;
132 int ret = CB_SUCCESS;
134 if (!offset)
135 return CB_ERR;
137 offset = offset * OFFSET_LENGTH_MULTIPLIER;
138 req.fru_device_id = id;
139 /* Read Chassis Info Area length first. */
140 req.fru_offset = offset + 1;
141 req.count = sizeof(length);
142 if (ipmi_read_fru(port, &req, &length) != CB_SUCCESS || !length) {
143 printk(BIOS_ERR, "%s failed, length: %d\n", __func__, length);
144 return CB_ERR;
146 length = length * OFFSET_LENGTH_MULTIPLIER;
147 data_ptr = (uint8_t *)malloc(length);
148 if (!data_ptr) {
149 printk(BIOS_ERR, "malloc %d bytes for chassis info failed\n", length);
150 return CB_ERR;
152 end = data_ptr + length;
153 /* Read Chassis Info Area data. */
154 req.fru_offset = offset;
155 req.count = length;
156 if (ipmi_read_fru(port, &req, data_ptr) != CB_SUCCESS) {
157 printk(BIOS_ERR, "%s failed to read fru\n", __func__);
158 ret = CB_ERR;
159 goto out;
161 if (checksum(data_ptr, length)) {
162 printk(BIOS_ERR, "Bad FRU chassis info checksum.\n");
163 ret = CB_ERR;
164 goto out;
166 /* Read chassis type. */
167 info->chassis_type = data_ptr[CHASSIS_TYPE_OFFSET];
169 printk(BIOS_DEBUG, "Read chassis part number string.\n");
170 length = read_data_string(data_ptr + CHASSIS_TYPE_OFFSET + 1,
171 &info->chassis_partnumber);
173 printk(BIOS_DEBUG, "Read chassis serial number string.\n");
174 data_ptr += CHASSIS_TYPE_OFFSET + 1 + length + 1;
175 length = read_data_string(data_ptr, &info->serial_number);
177 printk(BIOS_DEBUG, "Read custom chassis info fields.\n");
178 data_ptr += length + 1;
179 /* Check how many valid custom fields first. */
180 info->custom_count = 0;
181 custom_data_ptr = data_ptr;
182 while ((data_ptr < end) && ((data_ptr[0] != FRU_END_OF_FIELDS))) {
183 length = NUM_DATA_BYTES(data_ptr[0]);
184 if (length > 0)
185 info->custom_count++;
186 data_ptr += length + 1;
188 if (!info->custom_count)
189 goto out;
191 info->chassis_custom = malloc(info->custom_count * sizeof(char *));
192 if (!info->chassis_custom) {
193 printk(BIOS_ERR, "%s failed to malloc %zu bytes for "
194 "chassis custom data array.\n", __func__,
195 info->custom_count * sizeof(char *));
196 ret = CB_ERR;
197 goto out;
200 /* Start reading custom chassis data. */
201 data_ptr = custom_data_ptr;
202 int count = 0;
203 while ((data_ptr < end) && ((data_ptr[0] != FRU_END_OF_FIELDS))) {
204 length = NUM_DATA_BYTES(data_ptr[0]);
205 if (length > 0) {
206 length = read_data_string(data_ptr, info->chassis_custom + count);
207 count++;
209 data_ptr += length + 1;
212 out:
213 free(data_ptr);
214 return ret;
217 static enum cb_err read_fru_board_info_area(const int port, const uint8_t id,
218 uint8_t offset, struct fru_board_info *info)
220 uint8_t length;
221 struct ipmi_read_fru_data_req req;
222 uint8_t *data_ptr, *end, *custom_data_ptr;
223 int ret = CB_SUCCESS;
225 if (!offset)
226 return CB_ERR;
228 offset = offset * OFFSET_LENGTH_MULTIPLIER;
229 req.fru_device_id = id;
230 /* Read Board Info Area length first. */
231 req.fru_offset = offset + 1;
232 req.count = sizeof(length);
233 if (ipmi_read_fru(port, &req, &length) != CB_SUCCESS || !length) {
234 printk(BIOS_ERR, "%s failed, length: %d\n", __func__, length);
235 return CB_ERR;
237 length = length * OFFSET_LENGTH_MULTIPLIER;
238 data_ptr = (uint8_t *)malloc(length);
239 if (!data_ptr) {
240 printk(BIOS_ERR, "malloc %d bytes for board info failed\n", length);
241 return CB_ERR;
243 end = data_ptr + length;
244 /* Read Board Info Area data. */
245 req.fru_offset = offset;
246 req.count = length;
247 if (ipmi_read_fru(port, &req, data_ptr) != CB_SUCCESS) {
248 printk(BIOS_ERR, "%s failed to read fru\n", __func__);
249 ret = CB_ERR;
250 goto out;
252 if (checksum(data_ptr, length)) {
253 printk(BIOS_ERR, "Bad FRU board info checksum.\n");
254 ret = CB_ERR;
255 goto out;
257 printk(BIOS_DEBUG, "Read board manufacturer string\n");
258 length = read_data_string(data_ptr + BOARD_MAN_TYPE_LEN_OFFSET,
259 &info->manufacturer);
261 printk(BIOS_DEBUG, "Read board product name string.\n");
262 data_ptr += BOARD_MAN_TYPE_LEN_OFFSET + length + 1;
263 length = read_data_string(data_ptr, &info->product_name);
265 printk(BIOS_DEBUG, "Read board serial number string.\n");
266 data_ptr += length + 1;
267 length = read_data_string(data_ptr, &info->serial_number);
269 printk(BIOS_DEBUG, "Read board part number string.\n");
270 data_ptr += length + 1;
271 length = read_data_string(data_ptr, &info->part_number);
273 printk(BIOS_DEBUG, "Read board FRU file ID string.\n");
274 data_ptr += length + 1;
275 length = read_data_string(data_ptr, &info->fru_file_id);
277 /* Check how many valid custom fields first. */
278 data_ptr += length + 1;
279 info->custom_count = 0;
280 custom_data_ptr = data_ptr;
281 while ((data_ptr < end) && ((data_ptr[0] != FRU_END_OF_FIELDS))) {
282 length = NUM_DATA_BYTES(data_ptr[0]);
283 if (length > 0)
284 info->custom_count++;
285 data_ptr += length + 1;
287 if (!info->custom_count)
288 goto out;
290 info->board_custom = malloc(info->custom_count * sizeof(char *));
291 if (!info->board_custom) {
292 printk(BIOS_ERR, "%s failed to malloc %zu bytes for "
293 "board custom data array.\n", __func__,
294 info->custom_count * sizeof(char *));
295 ret = CB_ERR;
296 goto out;
299 /* Start reading custom board data. */
300 data_ptr = custom_data_ptr;
301 int count = 0;
302 while ((data_ptr < end) && ((data_ptr[0] != FRU_END_OF_FIELDS))) {
303 length = NUM_DATA_BYTES(data_ptr[0]);
304 if (length > 0) {
305 length = read_data_string(data_ptr, info->board_custom + count);
306 count++;
308 data_ptr += length + 1;
311 out:
312 free(data_ptr);
313 return ret;
316 static enum cb_err read_fru_product_info_area(const int port, const uint8_t id,
317 uint8_t offset, struct fru_product_info *info)
319 uint8_t length;
320 struct ipmi_read_fru_data_req req;
321 uint8_t *data_ptr, *end, *custom_data_ptr;
322 int ret = CB_SUCCESS;
324 if (!offset)
325 return CB_ERR;
327 offset = offset * OFFSET_LENGTH_MULTIPLIER;
328 req.fru_device_id = id;
329 /* Read Product Info Area length first. */
330 req.fru_offset = offset + 1;
331 req.count = sizeof(length);
332 if (ipmi_read_fru(port, &req, &length) != CB_SUCCESS || !length) {
333 printk(BIOS_ERR, "%s failed, length: %d\n", __func__, length);
334 return CB_ERR;
336 length = length * OFFSET_LENGTH_MULTIPLIER;
337 data_ptr = (uint8_t *)malloc(length);
338 if (!data_ptr) {
339 printk(BIOS_ERR, "malloc %d bytes for product info failed\n", length);
340 return CB_ERR;
342 end = data_ptr + length;
343 /* Read Product Info Area data. */
344 req.fru_offset = offset;
345 req.count = length;
346 if (ipmi_read_fru(port, &req, data_ptr) != CB_SUCCESS) {
347 printk(BIOS_ERR, "%s failed to read fru\n", __func__);
348 ret = CB_ERR;
349 goto out;
351 if (checksum(data_ptr, length)) {
352 printk(BIOS_ERR, "Bad FRU product info checksum.\n");
353 ret = CB_ERR;
354 goto out;
356 printk(BIOS_DEBUG, "Read product manufacturer string.\n");
357 length = read_data_string(data_ptr + PRODUCT_MAN_TYPE_LEN_OFFSET,
358 &info->manufacturer);
360 data_ptr += PRODUCT_MAN_TYPE_LEN_OFFSET + length + 1;
361 printk(BIOS_DEBUG, "Read product_name string.\n");
362 length = read_data_string(data_ptr, &info->product_name);
364 data_ptr += length + 1;
365 printk(BIOS_DEBUG, "Read product part/model number.\n");
366 length = read_data_string(data_ptr, &info->product_partnumber);
368 data_ptr += length + 1;
369 printk(BIOS_DEBUG, "Read product version string.\n");
370 length = read_data_string(data_ptr, &info->product_version);
372 data_ptr += length + 1;
373 printk(BIOS_DEBUG, "Read serial number string.\n");
374 length = read_data_string(data_ptr, &info->serial_number);
376 data_ptr += length + 1;
377 printk(BIOS_DEBUG, "Read asset tag string.\n");
378 length = read_data_string(data_ptr, &info->asset_tag);
380 printk(BIOS_DEBUG, "Read product FRU file ID string.\n");
381 data_ptr += length + 1;
382 length = read_data_string(data_ptr, &info->fru_file_id);
384 /* Check how many valid custom fields first. */
385 data_ptr += length + 1;
386 info->custom_count = 0;
387 custom_data_ptr = data_ptr;
388 while ((data_ptr < end) && ((data_ptr[0] != FRU_END_OF_FIELDS))) {
389 length = NUM_DATA_BYTES(data_ptr[0]);
390 if (length > 0)
391 info->custom_count++;
392 data_ptr += length + 1;
394 if (!info->custom_count)
395 goto out;
397 info->product_custom = malloc(info->custom_count * sizeof(char *));
398 if (!info->product_custom) {
399 printk(BIOS_ERR, "%s failed to malloc %zu bytes for "
400 "product custom data array.\n", __func__,
401 info->custom_count * sizeof(char *));
402 ret = CB_ERR;
403 goto out;
406 /* Start reading custom product data. */
407 data_ptr = custom_data_ptr;
408 int count = 0;
409 while ((data_ptr < end) && ((data_ptr[0] != FRU_END_OF_FIELDS))) {
410 length = NUM_DATA_BYTES(data_ptr[0]);
411 if (length > 0) {
412 length = read_data_string(data_ptr, info->product_custom + count);
413 count++;
415 data_ptr += length + 1;
418 out:
419 free(data_ptr);
420 return ret;
423 void read_fru_areas(const int port, const uint8_t id, uint16_t offset,
424 struct fru_info_str *fru_info_str)
426 struct ipmi_read_fru_data_req req;
427 struct ipmi_fru_common_hdr fru_common_hdr;
429 /* Set all the char pointers to 0 first, to avoid mainboard
430 * overwriting SMBIOS string with any non-NULL char pointer
431 * by accident. */
432 memset(fru_info_str, 0, sizeof(*fru_info_str));
433 req.fru_device_id = id;
434 req.fru_offset = offset;
435 req.count = sizeof(fru_common_hdr);
436 /* Read FRU common header first */
437 if (ipmi_read_fru(port, &req, (uint8_t *)&fru_common_hdr) == CB_SUCCESS) {
438 if (checksum((uint8_t *)&fru_common_hdr, sizeof(fru_common_hdr))) {
439 printk(BIOS_ERR, "Bad FRU common header checksum.\n");
440 return;
442 printk(BIOS_DEBUG, "FRU common header: format_version: %x\n"
443 "product_area_offset: %x\n"
444 "board_area_offset: %x\n"
445 "chassis_area_offset: %x\n",
446 fru_common_hdr.format_version,
447 fru_common_hdr.product_area_offset,
448 fru_common_hdr.board_area_offset,
449 fru_common_hdr.chassis_area_offset);
450 } else {
451 printk(BIOS_ERR, "Read FRU common header failed\n");
452 return;
455 read_fru_product_info_area(port, id, fru_common_hdr.product_area_offset,
456 &fru_info_str->prod_info);
457 read_fru_board_info_area(port, id, fru_common_hdr.board_area_offset,
458 &fru_info_str->board_info);
459 read_fru_chassis_info_area(port, id, fru_common_hdr.chassis_area_offset,
460 &fru_info_str->chassis_info);
463 void read_fru_one_area(const int port, const uint8_t id, uint16_t offset,
464 struct fru_info_str *fru_info_str, enum fru_area fru_area)
466 struct ipmi_read_fru_data_req req;
467 struct ipmi_fru_common_hdr fru_common_hdr;
469 req.fru_device_id = id;
470 req.fru_offset = offset;
471 req.count = sizeof(fru_common_hdr);
472 if (ipmi_read_fru(port, &req, (uint8_t *)&fru_common_hdr) == CB_SUCCESS) {
473 if (checksum((uint8_t *)&fru_common_hdr, sizeof(fru_common_hdr))) {
474 printk(BIOS_ERR, "Bad FRU common header checksum.\n");
475 return;
477 printk(BIOS_DEBUG, "FRU common header: format_version: %x\n"
478 "product_area_offset: %x\n"
479 "board_area_offset: %x\n"
480 "chassis_area_offset: %x\n",
481 fru_common_hdr.format_version,
482 fru_common_hdr.product_area_offset,
483 fru_common_hdr.board_area_offset,
484 fru_common_hdr.chassis_area_offset);
485 } else {
486 printk(BIOS_ERR, "Read FRU common header failed\n");
487 return;
490 switch (fru_area) {
491 case PRODUCT_INFO_AREA:
492 memset(&fru_info_str->prod_info, 0, sizeof(fru_info_str->prod_info));
493 read_fru_product_info_area(port, id, fru_common_hdr.product_area_offset,
494 &fru_info_str->prod_info);
495 break;
496 case BOARD_INFO_AREA:
497 memset(&fru_info_str->board_info, 0, sizeof(fru_info_str->board_info));
498 read_fru_board_info_area(port, id, fru_common_hdr.board_area_offset,
499 &fru_info_str->board_info);
500 break;
501 case CHASSIS_INFO_AREA:
502 memset(&fru_info_str->chassis_info, 0, sizeof(fru_info_str->chassis_info));
503 read_fru_chassis_info_area(port, id, fru_common_hdr.chassis_area_offset,
504 &fru_info_str->chassis_info);
505 break;
506 default:
507 printk(BIOS_ERR, "Invalid fru_area: %d\n", fru_area);
508 break;
512 void print_fru_areas(struct fru_info_str *fru_info_str)
514 int count = 0;
515 if (fru_info_str == NULL) {
516 printk(BIOS_ERR, "FRU data is null pointer\n");
517 return;
519 struct fru_product_info prod_info = fru_info_str->prod_info;
520 struct fru_board_info board_info = fru_info_str->board_info;
521 struct fru_chassis_info chassis_info = fru_info_str->chassis_info;
523 printk(BIOS_DEBUG, "Printing Product Info Area...\n");
524 if (prod_info.manufacturer != NULL)
525 printk(BIOS_DEBUG, "manufacturer: %s\n", prod_info.manufacturer);
526 if (prod_info.product_name != NULL)
527 printk(BIOS_DEBUG, "product name: %s\n", prod_info.product_name);
528 if (prod_info.product_partnumber != NULL)
529 printk(BIOS_DEBUG, "product part number: %s\n", prod_info.product_partnumber);
530 if (prod_info.product_version != NULL)
531 printk(BIOS_DEBUG, "product version: %s\n", prod_info.product_version);
532 if (prod_info.serial_number != NULL)
533 printk(BIOS_DEBUG, "serial number: %s\n", prod_info.serial_number);
534 if (prod_info.asset_tag != NULL)
535 printk(BIOS_DEBUG, "asset tag: %s\n", prod_info.asset_tag);
536 if (prod_info.fru_file_id != NULL)
537 printk(BIOS_DEBUG, "FRU file ID: %s\n", prod_info.fru_file_id);
539 for (count = 0; count < prod_info.custom_count; count++) {
540 if (*(prod_info.product_custom + count) != NULL)
541 printk(BIOS_DEBUG, "product custom data %i: %s\n", count,
542 *(prod_info.product_custom + count));
545 printk(BIOS_DEBUG, "Printing Board Info Area...\n");
546 if (board_info.manufacturer != NULL)
547 printk(BIOS_DEBUG, "manufacturer: %s\n", board_info.manufacturer);
548 if (board_info.product_name != NULL)
549 printk(BIOS_DEBUG, "product name: %s\n", board_info.product_name);
550 if (board_info.serial_number != NULL)
551 printk(BIOS_DEBUG, "serial number: %s\n", board_info.serial_number);
552 if (board_info.part_number != NULL)
553 printk(BIOS_DEBUG, "part number: %s\n", board_info.part_number);
554 if (board_info.fru_file_id != NULL)
555 printk(BIOS_DEBUG, "FRU file ID: %s\n", board_info.fru_file_id);
557 for (count = 0; count < board_info.custom_count; count++) {
558 if (*(board_info.board_custom + count) != NULL)
559 printk(BIOS_DEBUG, "board custom data %i: %s\n", count,
560 *(board_info.board_custom + count));
563 printk(BIOS_DEBUG, "Printing Chassis Info Area...\n");
564 printk(BIOS_DEBUG, "chassis type: 0x%x\n", chassis_info.chassis_type);
565 if (chassis_info.chassis_partnumber != NULL)
566 printk(BIOS_DEBUG, "part number: %s\n", chassis_info.chassis_partnumber);
567 if (chassis_info.serial_number != NULL)
568 printk(BIOS_DEBUG, "serial number: %s\n", chassis_info.serial_number);
570 for (count = 0; count < chassis_info.custom_count; count++) {
571 if (*(chassis_info.chassis_custom + count) != NULL)
572 printk(BIOS_DEBUG, "chassis custom data %i: %s\n", count,
573 *(chassis_info.chassis_custom + count));