From patchwork Mon Oct 14 13:29:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michal Simek X-Patchwork-Id: 1176369 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=xilinx.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=monstr-eu.20150623.gappssmtp.com header.i=@monstr-eu.20150623.gappssmtp.com header.b="K+xvHbTF"; dkim-atps=neutral Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 46sKFw47Lyz9s4Y for ; Tue, 15 Oct 2019 00:31:16 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id 1072FC21E2C; Mon, 14 Oct 2019 13:30:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=RCVD_IN_MSPIKE_H2, T_DKIM_INVALID autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 35683C21DF9; Mon, 14 Oct 2019 13:29:44 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id A7C23C21D9A; Mon, 14 Oct 2019 13:29:22 +0000 (UTC) Received: from mail-wr1-f66.google.com (mail-wr1-f66.google.com [209.85.221.66]) by lists.denx.de (Postfix) with ESMTPS id 1D431C21E0B for ; Mon, 14 Oct 2019 13:29:20 +0000 (UTC) Received: by mail-wr1-f66.google.com with SMTP id j11so19780494wrp.1 for ; Mon, 14 Oct 2019 06:29:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=monstr-eu.20150623.gappssmtp.com; s=20150623; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references :in-reply-to:references; bh=xYwgcSC/w4ss94ojyiAShHRTLRSXciiwK8PL/6Jbzf0=; b=K+xvHbTFSFKjHV7PvPTQNZwQ7lA3d8x07daUXSRX/XUhatFXLgXf9y6897Ow2NiS03 CnH/pocDSxbCVN9kO+2U9W521Hv8CpanM50YOlhKDmV+XA9IZpSe/AcZZXVdQ7iapSaS KCsi6Y3v3zrrorLwt4JnVw+n1vm0Yy4iDJPaavY5Is8sp3DhJHTB2afvgu20K85O38oH 5dznl6pJ5LXyl6DtXQsxtvBVitwKyyKOQM5Pw++8FjSEYx4FPUc7vbfzseF41hRoOvyl KsIxNgBRtPaC1TtB8NbjMZBuVO4FX4/TQMJlldnvMmkziwCp15E3uhQESmwpS134fULM ucxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references:in-reply-to:references; bh=xYwgcSC/w4ss94ojyiAShHRTLRSXciiwK8PL/6Jbzf0=; b=puqm9kHIQ5tu0bqj7/nOgQYHjodyAxrrxqg7uAnnNkDuPPGVsNMydDUrAp+lDiJdLG nanITCmyGNG0vy2S0u/mLXuzkDFZAhP9somjT/5XEn707YLNK3JsFMF3b/GQP/6Rg0XF Buz8mJ3PNOgsSxupXYmhpNGfJgHgoybhO24MpCZS4xvl8qExWzvnsn+KbPXLu4PzsjcD wYXilyxt2AsqH3d0sQtbEy556QR6/B6ZDgj6QlhSjiaZ60U54Rzu6vrDlHgb3yAn4Zb+ eRZWJygKtUyvZiXrGFIA3o72c2FR/49aY+URTuhby1FNskh28kQWai4/6LuTM59r60hN Ll1w== X-Gm-Message-State: APjAAAW/wmv7W82jzad0k2fGa19cbvOc5Q55DPhVvZHKo9PJ66WcEv/P mFxu15hXAvC0wAI+caaUYFwM2+0r/JE+tBAl X-Google-Smtp-Source: APXvYqzBFNVrQLTkfPn4Lh4Uzd5dx1H+B5+dOVS76oU59w9O9gHXlMS+L16pbdz1RzldbT6fFCoIxg== X-Received: by 2002:a5d:614c:: with SMTP id y12mr27483870wrt.235.1571059759265; Mon, 14 Oct 2019 06:29:19 -0700 (PDT) Received: from localhost (nat-35.starnet.cz. [178.255.168.35]) by smtp.gmail.com with ESMTPSA id 3sm19342065wmo.22.2019.10.14.06.29.18 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 14 Oct 2019 06:29:18 -0700 (PDT) From: Michal Simek To: u-boot@lists.denx.de, git@xilinx.com Date: Mon, 14 Oct 2019 15:29:12 +0200 Message-Id: <299fbc3c72c46ceb1b03fb2843876ff3e2f255ca.1571059750.git.michal.simek@xilinx.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: Cc: Stefan Roese , Ryder Lee , Heinrich Schuchardt , Sven Schwermer , Ramon Fried , Eugeniu Rosca , Boris Brezillon , Ruslan Trofymenko Subject: [U-Boot] [PATCH 1/3] cmd: fru: Add support for FRU commands X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Siva Durga Prasad Paladugu This patch adds support for fru commands "fru capture" and "fru display". The fru capture parses the FRU table present at an address and stores in a structure for later use. The fru display prints the content of captured structured in a readable format. As of now, it supports only common header and board area of FRU. Also, it supports only English language code and ASCII8 format. fru_data variable is placed to data section because fru parser can be called very early before bss is initialized. And also information needs to be shared that's why it is exported via header. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- MAINTAINERS | 3 + cmd/Kconfig | 8 ++ cmd/Makefile | 1 + cmd/fru.c | 68 +++++++++++++ common/Makefile | 1 + common/fru_ops.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++ include/fru.h | 64 ++++++++++++ 7 files changed, 392 insertions(+) create mode 100644 cmd/fru.c create mode 100644 common/fru_ops.c create mode 100644 include/fru.h diff --git a/MAINTAINERS b/MAINTAINERS index 2ef29768555c..420ce26cd0aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -404,6 +404,9 @@ M: Michal Simek S: Maintained T: git https://gitlab.denx.de/u-boot/custodians/u-boot-microblaze.git F: arch/arm/mach-versal/ +F: cmd/fru.c +F: common/fru_ops.c +F: include/fru.h N: (? +#include +#include +#include + +static int do_fru_capture(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned long addr; + char *endp; + + if (argc < cmdtp->maxargs) + return CMD_RET_USAGE; + + addr = simple_strtoul(argv[2], &endp, 16); + if (*argv[1] == 0 || *endp != 0) + return -1; + + return fru_capture(addr); +} + +static int do_fru_display(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + fru_display(); + return CMD_RET_SUCCESS; +} + +static cmd_tbl_t cmd_fru_sub[] = { + U_BOOT_CMD_MKENT(capture, 3, 0, do_fru_capture, "", ""), + U_BOOT_CMD_MKENT(display, 2, 0, do_fru_display, "", ""), +}; + +static int do_fru(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + cmd_tbl_t *c; + + if (argc < 2) + return CMD_RET_USAGE; + + c = find_cmd_tbl(argv[1], &cmd_fru_sub[0], + ARRAY_SIZE(cmd_fru_sub)); + if (c) + return c->cmd(c, flag, argc, argv); + + return CMD_RET_USAGE; +} + +/***************************************************/ +#ifdef CONFIG_SYS_LONGHELP +static char fru_help_text[] = + "capture - Parse and capture FRU table present at address.\n" + "fru display - Displays content of FRU table that was captured using\n" + " fru capture command\n" + ; +#endif + +U_BOOT_CMD( + fru, 3, 1, do_fru, + "FRU table info", + fru_help_text +) diff --git a/common/Makefile b/common/Makefile index 302d8beaf356..10cbe9a15ea1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_UPDATE_TFTP) += update.o obj-$(CONFIG_DFU_TFTP) += update.o obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o obj-$(CONFIG_CMDLINE) += cli_readline.o cli_simple.o +obj-$(CONFIG_CMD_FRU) += fru_ops.o endif # !CONFIG_SPL_BUILD diff --git a/common/fru_ops.c b/common/fru_ops.c new file mode 100644 index 000000000000..dd54bd9c0214 --- /dev/null +++ b/common/fru_ops.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2019 Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include + +struct fru_table fru_data __attribute__((section(".data"))); + +static u16 fru_cal_area_len(u8 len) +{ + return len * FRU_COMMON_HDR_LEN_MULTIPLIER; +} + +static u8 fru_version(u8 ver) +{ + return ver & FRU_COMMON_HDR_VER_MASK; +} + +static int fru_check_language(u8 code) +{ + if (code != FRU_LANG_CODE_ENGLISH && code != FRU_LANG_CODE_ENGLISH_1) { + printf("FRU_ERROR: Only English Language is supported\n"); + return -EINVAL; + } + + return code; +} + +static u8 fru_checksum(u8 *addr, u8 len) +{ + u8 checksum = 0; + + while (len--) { + checksum += *addr; + addr++; + } + + return checksum; +} + +static int fru_check_type_len(u8 type_len, u8 language, u8 *type) +{ + 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; + + return len; +} + +static int fru_parse_board(unsigned long addr) +{ + u8 i, type; + int len; + u8 *data, *term; + + memcpy(&fru_data.brd.ver, (void *)addr, 6); + addr += 6; + data = (u8 *)&fru_data.brd.manufacturer_type_len; + + for (i = 0; ; i++, data += FRU_BOARD_MAX_LEN) { + *data++ = *(u8 *)addr; + len = fru_check_type_len(*(u8 *)addr, fru_data.brd.lang_code, + &type); + /* + * Stop cature if it end of fields + */ + if (len == -EINVAL) + break; + + /* + * Dont capture data if type is not ASCII8 + */ + if (type != FRU_TYPELEN_TYPE_ASCII8) + return 0; + + addr += 1; + if (!len) + continue; + memcpy(data, (u8 *)addr, len); + term = data + (u8)len; + *term = 0; + addr += len; + } + + if (i < FRU_BOARD_AREA_TOTAL_FIELDS) { + printf("Board area require minimum %d fields\n", + FRU_BOARD_AREA_TOTAL_FIELDS); + return -EINVAL; + } + + return 0; +} + +int fru_capture(unsigned long addr) +{ + struct fru_common_hdr *hdr; + u8 checksum = 0; + + checksum = fru_checksum((u8 *)addr, sizeof(struct fru_common_hdr)); + if (checksum) { + printf("%s Common header CRC error\n", __func__); + return -EINVAL; + } + + hdr = (struct fru_common_hdr *)addr; + + memcpy((void *)&fru_data.hdr, (void *)hdr, + sizeof(struct fru_common_hdr)); + + fru_data.captured = true; + + if (hdr->off_board) { + addr += fru_cal_area_len(hdr->off_board); + fru_parse_board(addr); + } + + env_set_hex("fru_addr", addr); + + return 0; +} + +static int fru_display_board(void) +{ + u32 time = 0; + u8 type; + int len; + u8 *data; + const char *typecode[] = { + "Binary/Unspecified", + "BCD plus", + "6-bit ASCII", + "8-bit ASCII", + "2-byte UNICODE" + }; + const char *boardinfo[] = { + "Manufacturer Name", + "Product Name", + "Serial No", + "Part Number", + "File ID" + }; + + printf("*****BOARD INFO*****\n"); + printf("Version:%d\n", fru_version(fru_data.brd.ver)); + printf("Board Area Length:%d\n", fru_cal_area_len(fru_data.brd.len)); + + if (fru_check_language(fru_data.brd.lang_code)) + return 0; + + time = fru_data.brd.time[2] << 16 | fru_data.brd.time[1] << 8 | + fru_data.brd.time[0]; + printf("Time in Minutes from 0:00hrs 1/1/96 %d\n", time); + + data = (u8 *)&fru_data.brd.manufacturer_type_len; + + for (u8 i = 0; ; i++) { + len = fru_check_type_len(*data++, fru_data.brd.lang_code, + &type); + if (len == -EINVAL) { + printf("**** EOF for Board Area ****\n"); + break; + } + + if (type <= FRU_TYPELEN_TYPE_ASCII8 && + (fru_data.brd.lang_code == FRU_LANG_CODE_ENGLISH || + fru_data.brd.lang_code == FRU_LANG_CODE_ENGLISH_1)) + printf("Type code: %s\n", typecode[type]); + else + printf("Type code: %s\n", typecode[type + 1]); + + if (type != FRU_TYPELEN_TYPE_ASCII8) { + printf("FRU_ERROR: Only ASCII8 type is supported\n"); + return 0; + } + if (!len) { + printf("%s not found\n", boardinfo[i]); + continue; + } + + printf("Length: %d\n", len); + printf("%s: %s\n", boardinfo[i], data); + data += FRU_BOARD_MAX_LEN; + } + + return 0; +} + +static void fru_display_common_hdr(void) +{ + struct fru_common_hdr *hdr = &fru_data.hdr; + + printf("*****COMMON HEADER*****\n"); + printf("Version:%d\n", fru_version(hdr->version)); + if (hdr->off_internal) + printf("Internal Use Area Offset:%d\n", + fru_cal_area_len(hdr->off_internal)); + else + printf("*** No Internal Area ***\n"); + + if (hdr->off_chassis) + printf("Chassis Info Area Offset:%d\n", + fru_cal_area_len(hdr->off_chassis)); + else + printf("*** No Chassis Info Area ***\n"); + + if (hdr->off_board) + printf("Board Area Offset:%d\n", + fru_cal_area_len(hdr->off_board)); + else + printf("*** No Board Area ***\n"); + + if (hdr->off_product) + printf("Product Info Area Offset:%d\n", + fru_cal_area_len(hdr->off_product)); + else + printf("*** No Product Info Area ***\n"); + + if (hdr->off_multirec) + printf("MultiRecord Area Offset:%d\n", + fru_cal_area_len(hdr->off_multirec)); + else + printf("*** No MultiRecord Area ***\n"); +} + +int fru_display(void) +{ + if (!fru_data.captured) { + printf("FRU data not available please run fru parse\n"); + return -EINVAL; + } + + fru_display_common_hdr(); + fru_display_board(); + + return 0; +} diff --git a/include/fru.h b/include/fru.h new file mode 100644 index 000000000000..38fdfad409c4 --- /dev/null +++ b/include/fru.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2019 Xilinx, Inc. + * Siva Durga Prasad Paladugu + */ + +#ifndef __FRU_H +#define __FRU_H + +struct fru_common_hdr { + u8 version; + u8 off_internal; + u8 off_chassis; + u8 off_board; + u8 off_product; + u8 off_multirec; + u8 pad; + u8 crc; +}; + +#define FRU_BOARD_MAX_LEN 32 + +struct fru_board_data { + u8 ver; + u8 len; + u8 lang_code; + u8 time[3]; + u8 manufacturer_type_len; + u8 manufacturer_name[FRU_BOARD_MAX_LEN]; + u8 product_name_type_len; + u8 product_name[FRU_BOARD_MAX_LEN]; + u8 serial_number_type_len; + u8 serial_number[FRU_BOARD_MAX_LEN]; + u8 part_number_type_len; + u8 part_number[FRU_BOARD_MAX_LEN]; + u8 file_id_type_len; + u8 file_id[FRU_BOARD_MAX_LEN]; +}; + +struct fru_table { + bool captured; + struct fru_common_hdr hdr; + struct fru_board_data brd; +}; + +#define FRU_TYPELEN_CODE_MASK 0xC0 +#define FRU_TYPELEN_LEN_MASK 0x3F +#define FRU_COMMON_HDR_VER_MASK 0xF +#define FRU_COMMON_HDR_LEN_MULTIPLIER 8 +#define FRU_LANG_CODE_ENGLISH 0 +#define FRU_LANG_CODE_ENGLISH_1 25 +#define FRU_TYPELEN_EOF 0xC1 + +/* This should be minimum of fields */ +#define FRU_BOARD_AREA_TOTAL_FIELDS 5 +#define FRU_TYPELEN_TYPE_SHIFT 6 +#define FRU_TYPELEN_TYPE_ASCII8 3 + +int fru_display(void); +int fru_capture(unsigned long addr); + +extern struct fru_table fru_data; + +#endif /* FRU_H */