// SPDX-License-Identifier: GPL-2.0
/*
- * (C) Copyright 2019 - 2020 Xilinx, Inc.
+ * (C) Copyright 2019 - 2022, Xilinx, Inc.
+ * (C) Copyright 2022 - 2023, Advanced Micro Devices, Inc.
*/
-#include <common.h>
#include <cpu_func.h>
#include <env.h>
#include <fdtdec.h>
#include <log.h>
#include <malloc.h>
+#include <net.h>
+#include <linux/errno.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include "fru.h"
-struct fru_table fru_data __section(.data);
+struct fru_table fru_data __section(".data");
static u16 fru_cal_area_len(u8 len)
{
u8 fru_checksum(u8 *addr, u8 len)
{
u8 checksum = 0;
+ u8 cnt = len;
while (len--) {
+ if (*addr == 0)
+ cnt--;
+
checksum += *addr;
addr++;
}
+ /* If all data bytes are 0's return error */
+ if (!cnt)
+ return EINVAL;
+
return checksum;
}
{
int len;
- if (type_len == FRU_TYPELEN_EOF)
- return -EINVAL;
-
*type = (type_len & FRU_TYPELEN_CODE_MASK) >> FRU_TYPELEN_TYPE_SHIFT;
len = type_len & FRU_TYPELEN_LEN_MASK;
{
u8 i, type;
int len;
- u8 *data, *term, *limit;
+ u8 *data, *term, *limit, *next_addr, *eof;
memcpy(&fru_data.brd.ver, (void *)addr, 6);
+
+ /*
+ * eof marks the last data byte (without checksum). That's why checksum
+ * is address length - 1 and last data byte is length - 2.
+ */
+ eof = (u8 *)(fru_data.brd.len * 8 + addr - 2);
+
addr += 6;
data = (u8 *)&fru_data.brd.manufacturer_type_len;
for (i = 0; ; i++, data += FRU_BOARD_MAX_LEN) {
len = fru_check_type_len(*(u8 *)addr, fru_data.brd.lang_code,
&type);
+ next_addr = (u8 *)addr + 1;
+
+ if ((u8 *)addr >= eof) {
+ debug("Reach EOF record: addr %lx, eof %lx\n", addr,
+ (unsigned long)eof);
+ break;
+ }
+
/*
- * Stop cature if it end of fields
+ * Stop capture if the type is ASCII and valid field length
+ * is 1 (0xc1) and next FRU data is less than 0x20 (space " ")
+ * or it is 0x7f (delete 'DEL').
*/
- if (len == -EINVAL)
+ if (type == FRU_TYPELEN_TYPE_ASCII8 && len == 1 &&
+ (*next_addr < 0x20 || *next_addr == 0x7F))
break;
/* Stop when amount of chars is more then fields to record */
return 0;
}
+static int fru_parse_multirec(unsigned long addr)
+{
+ struct fru_multirec_hdr mrc;
+ u8 checksum = 0;
+ u8 hdr_len = sizeof(struct fru_multirec_hdr);
+ int mac_len = 0;
+
+ debug("%s: multirec addr %lx\n", __func__, addr);
+
+ do {
+ memcpy(&mrc.rec_type, (void *)addr, hdr_len);
+
+ checksum = fru_checksum((u8 *)addr, hdr_len);
+ if (checksum) {
+ debug("%s header CRC error\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mrc.rec_type == FRU_MULTIREC_TYPE_OEM) {
+ struct fru_multirec_mac *mac = (void *)addr + hdr_len;
+ u32 type = FRU_DUT_MACID;
+
+ if (CONFIG_IS_ENABLED(FRU_SC))
+ type = FRU_SC_MACID;
+
+ if (mac->ver == type) {
+ mac_len = mrc.len - FRU_MULTIREC_MAC_OFFSET;
+ memcpy(&fru_data.mac.macid, mac->macid, mac_len);
+ }
+ }
+ addr += mrc.len + hdr_len;
+ } while (!(mrc.type & FRU_LAST_REC));
+
+ return 0;
+}
+
int fru_capture(unsigned long addr)
{
struct fru_common_hdr *hdr;
u8 checksum = 0;
+ unsigned long multirec_addr = addr;
checksum = fru_checksum((u8 *)addr, sizeof(struct fru_common_hdr));
if (checksum) {
}
hdr = (struct fru_common_hdr *)addr;
-
+ memset((void *)&fru_data, 0, sizeof(fru_data));
memcpy((void *)&fru_data, (void *)hdr,
sizeof(struct fru_common_hdr));
env_set_hex("fru_addr", addr);
+ if (hdr->off_multirec) {
+ multirec_addr += fru_cal_area_len(hdr->off_multirec);
+ fru_parse_multirec(multirec_addr);
+ }
+
return 0;
}
for (u8 i = 0; i < (sizeof(boardinfo) / sizeof(*boardinfo)); i++) {
len = fru_check_type_len(*data++, brd->lang_code,
&type);
- if (len == -EINVAL) {
- printf("**** EOF for Board Area ****\n");
- break;
+
+ /* Empty record has no len/type filled */
+ if (!len) {
+ debug("%s not found\n", boardinfo[i]);
+ continue;
}
if (type <= FRU_TYPELEN_TYPE_ASCII8 &&
else
debug("Type code: %s\n", typecode[type + 1]);
- if (!len) {
- debug("%s not found\n", boardinfo[i]);
- continue;
- }
-
switch (type) {
case FRU_TYPELEN_TYPE_BINARY:
debug("Length: %d\n", len);