• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <string.h>
5 #include <delay.h>
6 #include <stdlib.h>
7 
8 #include "ipmi_if.h"
9 #include "ipmi_ops.h"
10 
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 */
16 
ipmi_read_fru(const int port,struct ipmi_read_fru_data_req * req,uint8_t * fru_data)17 static enum cb_err ipmi_read_fru(const int port, struct ipmi_read_fru_data_req *req,
18 			uint8_t *fru_data)
19 {
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;
25 
26 	if (req == NULL || fru_data == NULL) {
27 		printk(BIOS_ERR, "%s failed, null pointer parameter\n",
28 			 __func__);
29 		return CB_ERR;
30 	}
31 
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;
36 
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;
47 				}
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;
56 			}
57 			break;
58 		}
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);
66 
67 	return CB_SUCCESS;
68 }
69 
70 /* data: data to check, offset: offset to checksum. */
checksum(uint8_t * data,int offset)71 static uint8_t checksum(uint8_t *data, int offset)
72 {
73 	uint8_t c = 0;
74 	for (; offset > 0; offset--, data++)
75 		c += *data;
76 	return -c;
77 }
78 
data2str(const uint8_t * frudata,char * stringdata,uint8_t length)79 static uint8_t data2str(const uint8_t *frudata, char *stringdata, uint8_t length)
80 {
81 	uint8_t type;
82 
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;
89 	}
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;
94 }
95 
96 /*
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.
99  */
read_data_string(const uint8_t * data_ptr,char ** string)100 static int read_data_string(const uint8_t *data_ptr, char **string)
101 {
102 	uint8_t length;
103 
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;
109 	}
110 
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;
116 	}
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;
121 	}
122 
123 	return length;
124 }
125 
read_fru_chassis_info_area(const int port,const uint8_t id,uint8_t offset,struct fru_chassis_info * info)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)
128 {
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;
133 
134 	if (!offset)
135 		return CB_ERR;
136 
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;
145 	}
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;
151 	}
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;
160 	}
161 	if (checksum(data_ptr, length)) {
162 		printk(BIOS_ERR, "Bad FRU chassis info checksum.\n");
163 		ret = CB_ERR;
164 		goto out;
165 	}
166 	/* Read chassis type. */
167 	info->chassis_type = data_ptr[CHASSIS_TYPE_OFFSET];
168 
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);
172 
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);
176 
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;
187 	}
188 	if (!info->custom_count)
189 		goto out;
190 
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;
198 	}
199 
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++;
208 		}
209 		data_ptr += length + 1;
210 	}
211 
212 out:
213 	free(data_ptr);
214 	return ret;
215 }
216 
read_fru_board_info_area(const int port,const uint8_t id,uint8_t offset,struct fru_board_info * info)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)
219 {
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;
224 
225 	if (!offset)
226 		return CB_ERR;
227 
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;
236 	}
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;
242 	}
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;
251 	}
252 	if (checksum(data_ptr, length)) {
253 		printk(BIOS_ERR, "Bad FRU board info checksum.\n");
254 		ret = CB_ERR;
255 		goto out;
256 	}
257 	printk(BIOS_DEBUG, "Read board manufacturer string\n");
258 	length = read_data_string(data_ptr + BOARD_MAN_TYPE_LEN_OFFSET,
259 		&info->manufacturer);
260 
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);
264 
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);
268 
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);
272 
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);
276 
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;
286 	}
287 	if (!info->custom_count)
288 		goto out;
289 
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;
297 	}
298 
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++;
307 		}
308 		data_ptr += length + 1;
309 	}
310 
311 out:
312 	free(data_ptr);
313 	return ret;
314 }
315 
read_fru_product_info_area(const int port,const uint8_t id,uint8_t offset,struct fru_product_info * info)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)
318 {
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;
323 
324 	if (!offset)
325 		return CB_ERR;
326 
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;
335 	}
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;
341 	}
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;
350 	}
351 	if (checksum(data_ptr, length)) {
352 		printk(BIOS_ERR, "Bad FRU product info checksum.\n");
353 		ret = CB_ERR;
354 		goto out;
355 	}
356 	printk(BIOS_DEBUG, "Read product manufacturer string.\n");
357 	length = read_data_string(data_ptr + PRODUCT_MAN_TYPE_LEN_OFFSET,
358 		&info->manufacturer);
359 
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);
363 
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);
367 
368 	data_ptr += length + 1;
369 	printk(BIOS_DEBUG, "Read product version string.\n");
370 	length = read_data_string(data_ptr, &info->product_version);
371 
372 	data_ptr += length + 1;
373 	printk(BIOS_DEBUG, "Read serial number string.\n");
374 	length = read_data_string(data_ptr, &info->serial_number);
375 
376 	data_ptr += length + 1;
377 	printk(BIOS_DEBUG, "Read asset tag string.\n");
378 	length = read_data_string(data_ptr, &info->asset_tag);
379 
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);
383 
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;
393 	}
394 	if (!info->custom_count)
395 		goto out;
396 
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;
404 	}
405 
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++;
414 		}
415 		data_ptr += length + 1;
416 	}
417 
418 out:
419 	free(data_ptr);
420 	return ret;
421 }
422 
read_fru_areas(const int port,const uint8_t id,uint16_t offset,struct fru_info_str * fru_info_str)423 void read_fru_areas(const int port, const uint8_t id, uint16_t offset,
424 			struct fru_info_str *fru_info_str)
425 {
426 	struct ipmi_read_fru_data_req req;
427 	struct ipmi_fru_common_hdr fru_common_hdr;
428 
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;
441 		}
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;
453 	}
454 
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);
461 }
462 
read_fru_one_area(const int port,const uint8_t id,uint16_t offset,struct fru_info_str * fru_info_str,enum fru_area fru_area)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)
465 {
466 	struct ipmi_read_fru_data_req req;
467 	struct ipmi_fru_common_hdr fru_common_hdr;
468 
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;
476 		}
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;
488 	}
489 
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;
509 	}
510 }
511 
print_fru_areas(struct fru_info_str * fru_info_str)512 void print_fru_areas(struct fru_info_str *fru_info_str)
513 {
514 	int count = 0;
515 	if (fru_info_str == NULL) {
516 		printk(BIOS_ERR, "FRU data is null pointer\n");
517 		return;
518 	}
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;
522 
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);
538 
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));
543 	}
544 
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);
556 
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));
561 	}
562 
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);
569 
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));
574 	}
575 }
576