On Wednesday 27 October 2021 12:08:12 Robert Marko wrote: > From: Luka Kovacic <[email protected]> > > The mac command is implemented to enable parsing Marvell hw_info formatted > environments. This format is often used on Marvell Armada devices to store > parameters like the board serial number, factory MAC addresses and some > other information. > These parameters are usually written to the flash in the factory. > > Currently the mac command supports reading/writing parameters and dumping > the current hw_info parameters. > EEPROM config pattern and checksum aren't supported. > > This functionality has been tested on the GST ESPRESSOBin-Ultra board > successfully, both reading the stock U-Boot parameters in mainline U-Boot > and reading the parameters written by this command in the stock U-Boot. > > Support for this command is added for Marvell Armada A37XX and 7K/8K > devices. > > Usage example: > => mac read > => saveenv > > Signed-off-by: Luka Kovacic <[email protected]> > Cc: Luka Perkov <[email protected]> > Cc: Robert Marko <[email protected]>
Acked-by: Pali Rohár <[email protected]> > --- > Changes in v2: > * Use DTS to find the hw_info partition offset instead of using KConfig > --- > arch/arm/mach-mvebu/Kconfig | 1 + > board/Marvell/common/Kconfig | 20 ++ > board/Marvell/common/Makefile | 5 + > board/Marvell/common/mac.c | 418 ++++++++++++++++++++++++++++ > include/configs/mvebu_armada-37xx.h | 7 + > include/configs/mvebu_armada-8k.h | 7 + > lib/hashtable.c | 2 +- > 7 files changed, 459 insertions(+), 1 deletion(-) > create mode 100644 board/Marvell/common/Kconfig > create mode 100644 board/Marvell/common/Makefile > create mode 100644 board/Marvell/common/mac.c > > diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig > index d23cc0c760f..39a91dd41c9 100644 > --- a/arch/arm/mach-mvebu/Kconfig > +++ b/arch/arm/mach-mvebu/Kconfig > @@ -339,6 +339,7 @@ config SECURED_MODE_CSK_INDEX > default 0 > depends on SECURED_MODE_IMAGE > > +source "board/Marvell/common/Kconfig" > source "board/solidrun/clearfog/Kconfig" > source "board/kobol/helios4/Kconfig" > > diff --git a/board/Marvell/common/Kconfig b/board/Marvell/common/Kconfig > new file mode 100644 > index 00000000000..9887e25fb1f > --- /dev/null > +++ b/board/Marvell/common/Kconfig > @@ -0,0 +1,20 @@ > +menu "Marvell Armada common configuration" > +depends on TARGET_MVEBU_ARMADA_37XX || TARGET_MVEBU_ARMADA_8K > + > +config MVEBU_MAC_HW_INFO > + bool "Marvell hw_info (mac) support" > + depends on SPI_FLASH && ENV_IS_IN_SPI_FLASH && ARCH_MVEBU > + default n > + help > + Enable loading of the Marvell hw_info parameters from the > + SPI flash hw_info area. Parameters (usually the board serial > + number and MAC addresses) are then imported into the > + existing U-Boot environment. > + Implementation of this command is compatible with the > + original Marvell U-Boot command. Reading and writing is > + supported. > + EEPROM config pattern and checksum aren't supported. > + After enabled, these parameters are managed from the common > + U-Boot mac command. > + > +endmenu > diff --git a/board/Marvell/common/Makefile b/board/Marvell/common/Makefile > new file mode 100644 > index 00000000000..072c3e49de7 > --- /dev/null > +++ b/board/Marvell/common/Makefile > @@ -0,0 +1,5 @@ > +# SPDX-License-Identifier: GPL-2.0+ > +# > +# Copyright (c) 2021 Sartura Ltd. > + > +obj-$(CONFIG_ID_EEPROM) += mac.o > diff --git a/board/Marvell/common/mac.c b/board/Marvell/common/mac.c > new file mode 100644 > index 00000000000..590c44c882e > --- /dev/null > +++ b/board/Marvell/common/mac.c > @@ -0,0 +1,418 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Marvell hw_info (mac) command implementation > + * Helper command for interfacing with the Marvell hw_info parameters > + * > + * Copyright (c) 2021 Sartura Ltd. > + * Copyright (c) 2018 Marvell International Ltd. > + * > + * Author: Luka Kovacic <[email protected]> > + */ > + > +#include <command.h> > +#include <common.h> > +#include <env.h> > +#include <env_internal.h> > +#include <spi.h> > +#include <spi_flash.h> > + > +#define HW_INFO_MAX_ENV_SIZE 0x1F0 > +#define HW_INFO_ENV_OFFSET 0xA > +#define HW_INFO_ENV_SEP 0x20 > + > +#define HW_INFO_MAX_NAME_LEN 32 > + > +#define HW_INFO_MERGED_VARIABLE "read_board_hw_info" > + > +static char hw_info_allowed_parameters[][HW_INFO_MAX_NAME_LEN] = { > + "pcb_slm", > + "pcb_rev", > + "eco_rev", > + "pcb_sn", > + "ethaddr", > + "eth1addr", > + "eth2addr", > + "eth3addr", > + "eth4addr", > + "eth5addr", > + "eth6addr", > + "eth7addr", > + "eth8addr", > + "eth9addr", > +}; > + > +static int hw_info_allowed_param_count = (sizeof(hw_info_allowed_parameters) > / > + sizeof(hw_info_allowed_parameters[0])); > + > +static int hw_info_check_parameter(char *name) > +{ > + int idx; > + > + for (idx = 0; idx < hw_info_allowed_param_count; idx++) { > + if (strcmp(name, hw_info_allowed_parameters[idx]) == 0) > + return 0; > + } > + > + return -EINVAL; > +} > + > +/** > + * read_spi_flash_offset() - Read data from the SPI flash > + * @buf: Buffer to write in > + * @offset: Offset from the flash start > + * > + * Read SPI flash data into the buffer from offset to HW_INFO_MAX_ENV_SIZE. > + */ > +static int read_spi_flash_offset(char *buf, int offset) > +{ > + struct spi_flash *flash; > + int ret; > + > + flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, > + CONFIG_SF_DEFAULT_CS, > + CONFIG_SF_DEFAULT_SPEED, > + CONFIG_SF_DEFAULT_MODE); > + > + if (!flash) { > + printf("Error - unable to probe SPI flash.\n"); > + return -EIO; > + } > + > + ret = spi_flash_read(flash, offset, HW_INFO_MAX_ENV_SIZE, buf); > + if (ret) { > + printf("Error - unable to read hw_info environment from SPI > flash.\n"); > + return ret; > + } > + > + return ret; > +} > + > +/** > + * write_spi_flash_offset() - Write a buffer to SPI flash > + * @buf: Buffer to write to SPI flash > + * @offset: Offset from the flash start > + * @size: Size of the buffer content > + * > + * This function probes the SPI flash and updates the specified flash > location > + * with new data from the buffer. > + */ > +static int write_spi_flash_offset(char *buf, fdt_addr_t flash_offset, int > env_offset, ssize_t size) > +{ > + ssize_t safe_size, erase_size; > + struct spi_flash *flash; > + int ret; > + > + flash = spi_flash_probe(CONFIG_SF_DEFAULT_BUS, > + CONFIG_SF_DEFAULT_CS, > + CONFIG_SF_DEFAULT_SPEED, > + CONFIG_SF_DEFAULT_MODE); > + > + if (!flash) { > + printf("Error - unable to probe SPI flash.\n"); > + return -EIO; > + } > + > + safe_size = size > HW_INFO_MAX_ENV_SIZE ? HW_INFO_MAX_ENV_SIZE : size; > + erase_size = safe_size + > + (flash->erase_size - safe_size % flash->erase_size); > + ret = spi_flash_erase(flash, flash_offset, erase_size); > + if (ret) { > + printf("Error - unable to erase the hw_info area on SPI > flash.\n"); > + return ret; > + } > + ret = spi_flash_write(flash, flash_offset + env_offset, safe_size, buf); > + if (ret) { > + printf("Error - unable to write hw_info parameters to SPI > flash.\n"); > + return ret; > + } > + > + return ret; > +} > + > +/** > + * cmd_hw_info_dump() - Dump the hw_info parameters > + * > + * This function prints all Marvell hw_info parameters, which are stored in > + * the SPI flash. > + */ > +static int cmd_hw_info_dump(fdt_addr_t flash_offset) > +{ > + char buffer[HW_INFO_MAX_ENV_SIZE]; > + struct hsearch_data htab; > + char *res = NULL; > + ssize_t len; > + int ret = 0; > + > + ret = read_spi_flash_offset(buffer, flash_offset + > + HW_INFO_ENV_OFFSET); > + if (ret) > + goto err; > + memset(&htab, 0, sizeof(htab)); > + if (!hcreate_r(HW_INFO_MAX_ENV_SIZE, &htab)) { > + ret = -ENOMEM; > + goto err; > + } > + if (!himport_r(&htab, buffer, HW_INFO_MAX_ENV_SIZE, > + HW_INFO_ENV_SEP, H_NOCLEAR, 0, 0, NULL)) { > + ret = -EFAULT; > + goto err_htab; > + } > + > + len = hexport_r(&htab, '\n', H_HIDE_DOT, &res, 0, 0, NULL); > + if (len > 0) { > + printf("Parameters (hw_info):\n"); > + puts(res); > + free(res); > + ret = 0; > + goto ret_htab; > + } > +ret_htab: > + hdestroy_r(&htab); > + return ret; > +err_htab: > + hdestroy_r(&htab); > +err: > + printf("## Error: cannot store hw_info parameters to SPI flash\n"); > + return ret; > +} > + > +/** > + * cmd_hw_info_read() - Import the hw_info parameters into U-Boot env > + * @print_env: Print U-Boot environment after new parameters are imported > + * > + * This function reads the Marvell hw_info parameters from SPI flash and > + * imports them into the U-Boot env. > + */ > +static int cmd_hw_info_read(fdt_addr_t flash_offset, bool print_env) > +{ > + char buffer[HW_INFO_MAX_ENV_SIZE]; > + char *res = NULL; > + ssize_t len; > + int ret = 0; > + > + ret = read_spi_flash_offset(buffer, flash_offset + > + HW_INFO_ENV_OFFSET); > + if (ret) > + goto err; > + if (!himport_r(&env_htab, buffer, HW_INFO_MAX_ENV_SIZE, > + HW_INFO_ENV_SEP, H_NOCLEAR, 0, 0, NULL)) { > + ret = -EFAULT; > + goto err; > + } > + > + printf("Successfully imported the Marvell hw_info parameters.\n"); > + if (!print_env) > + return 0; > + > + len = hexport_r(&env_htab, '\n', H_HIDE_DOT, &res, 0, 0, NULL); > + if (len > 0) { > + printf("Updated environment:\n"); > + puts(res); > + free(res); > + return 0; > + } > +err: > + printf("## Error: cannot import hw_info parameters\n"); > + return ret; > +} > + > +/** > + * cmd_hw_info_save() - Save a parameter from U-Boot env to hw_info > parameters > + * @name: Name of the U-Boot env parameter to save > + * > + * This function finds the specified parameter by name in the U-Boot env > + * and then updates the Marvell hw_info parameters with the new value. > + */ > +static int cmd_hw_info_save(fdt_addr_t flash_offset, char *name) > +{ > + char buffer[HW_INFO_MAX_ENV_SIZE]; > + struct env_entry e, *ep, *rv; > + struct hsearch_data htab; > + char *res = NULL; > + ssize_t len; > + int ret = 0; > + > + ret = hw_info_check_parameter(name); > + if (ret) { > + printf("Invalid parameter %s, stopping.\n", name); > + goto err; > + } > + > + ret = read_spi_flash_offset(buffer, flash_offset + > + HW_INFO_ENV_OFFSET); > + if (ret) > + goto err; > + memset(&htab, 0, sizeof(htab)); > + if (!hcreate_r(HW_INFO_MAX_ENV_SIZE, &htab)) { > + ret = -ENOMEM; > + goto err; > + } > + if (!himport_r(&htab, buffer, HW_INFO_MAX_ENV_SIZE, > + HW_INFO_ENV_SEP, H_NOCLEAR, 0, 0, NULL)) { > + ret = -EFAULT; > + goto err_htab; > + } > + > + e.key = name; > + e.data = NULL; > + if (!hsearch_r(e, ENV_FIND, &ep, &env_htab, H_HIDE_DOT)) { > + ret = -ENOENT; > + goto err_htab; > + } > + if (!ep) { > + ret = -ENOENT; > + goto err_htab; > + } > + > + printf("Storing %s=%s to hw_info...\n", ep->key, ep->data); > + > + e.key = ep->key; > + e.data = ep->data; > + if (!hsearch_r(e, ENV_ENTER, &rv, &htab, H_HIDE_DOT)) { > + ret = -EINVAL; > + goto err_htab; > + } > + if (!rv) { > + ret = -EINVAL; > + goto err_htab; > + } > + len = hexport_r(&htab, HW_INFO_ENV_SEP, H_MATCH_KEY | H_MATCH_IDENT, > + &res, 0, 0, NULL); > + if (len <= 0) { > + free(res); > + goto ret_htab; > + } > + ret = write_spi_flash_offset(res, flash_offset, > + HW_INFO_ENV_OFFSET, len); > + free(res); > + if (ret) > + goto err_htab; > + > + printf("Successfully stored the Marvell hw_info parameters.\n"); > + return 0; > +ret_htab: > + hdestroy_r(&htab); > + return ret; > +err_htab: > + hdestroy_r(&htab); > +err: > + printf("## Error: cannot store hw_info parameters to SPI flash\n"); > + return ret; > +} > + > +/** > + * mac_read_from_eeprom() - Read the parameters from SPI flash. > + * > + * This function reads the content of the Marvell hw_info parameters from the > + * SPI flash and imports them into the U-Boot environment. > + * This includes MAC addresses and board serial numbers. > + * > + * The import is performed only once. > + * > + * This function is a part of the U-Boot mac command and must be executed > + * after SPI flash initialization. > + */ > +int mac_read_from_eeprom(void) > +{ > + fdt_addr_t flash_offset, size; > + ofnode hw_info_node; > + > + hw_info_node = ofnode_by_compatible(ofnode_null(), "marvell,hw-info"); > + if (!ofnode_valid(hw_info_node)) { > + printf("Missing hw-info DT node!\n"); > + return -ENODEV; > + } > + > + flash_offset = ofnode_get_addr_size_index_notrans(hw_info_node, 0, > &size); > + if (flash_offset == FDT_ADDR_T_NONE || !size) { > + printf("Missing hw-info offset or size!\n"); > + return -EINVAL; > + } > + > + if (env_get_ulong(HW_INFO_MERGED_VARIABLE, 10, 0) == 0) { > + if (env_set_ulong(HW_INFO_MERGED_VARIABLE, 1)) > + return -ENOENT; > + return cmd_hw_info_read(flash_offset, false); > + } > + return 0; > +} > + > +/** > + * print_platform_help() - Print the platform specific help > + * > + * Extend the existing U-Boot mac command description by also printing > + * platform specific help text. > + */ > +static void print_platform_help(void) > +{ > + printf("\nNote: arguments mac [id|num|errata|date|ports|port_number]\n" > + "are unavailable on Marvell Armada A37xx platforms.\n" > + "Use mac [read|save {parameter}] instead.\n" > + "Available parameters:\n" > + "pcb_slm\tPCB SLM number\n" > + "pcb_rev\tPCB revision number\n" > + "eco_rev\tECO revision number\n" > + "pcb_sn\tPCB SN\n" > + "ethaddr\tfirst MAC address\n" > + "eth[1-9]addr\tsecond-ninth MAC address\n"); > +} > + > +/** > + * do_mac() - Standard U-Boot mac command implementation > + * @cmdtp: U-Boot command table > + * @flag: Execution flags > + * @argc: Count of arguments > + * @argv: Arguments > + * > + * This function implements the standard U-Boot mac command in a mostly > + * compatible way. > + * To conform to the general command structure as much as possible, the > + * command description from cmd/mac.c is followed. > + * Where not possible, convenient or sensible additional comments for the > user > + * are added. > + */ > +int do_mac(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) > +{ > + const char *cmd = argv[1]; > + fdt_addr_t flash_offset, size; > + ofnode hw_info_node; > + int ret = 0; > + > + hw_info_node = ofnode_by_compatible(ofnode_null(), "marvell,hw-info"); > + if (!ofnode_valid(hw_info_node)) { > + printf("Missing hw-info DT node!\n"); > + return -ENODEV; > + } > + > + flash_offset = ofnode_get_addr_size_index_notrans(hw_info_node, 0, > &size); > + if (flash_offset == FDT_ADDR_T_NONE || !size) { > + printf("Missing hw-info offset or size!\n"); > + return -EINVAL; > + } > + > + if (argc == 1) { > + ret = cmd_hw_info_dump(flash_offset); > + if (ret) > + return -EINVAL; > + return CMD_RET_SUCCESS; > + } > + > + if (!strcmp(cmd, "read")) { > + if (cmd_hw_info_read(flash_offset, true)) > + return -EINVAL; > + } else if (!strcmp(cmd, "save")) { > + if (argc != 3) { > + printf("Please pass an additional argument to specify, " > + "which env parameter to save.\n"); > + return -EINVAL; > + } > + if (cmd_hw_info_save(flash_offset, argv[2])) > + return -EINVAL; > + } else { > + ret = cmd_usage(cmdtp); > + print_platform_help(); > + return ret; > + } > + > + return CMD_RET_SUCCESS; > +} > diff --git a/include/configs/mvebu_armada-37xx.h > b/include/configs/mvebu_armada-37xx.h > index e7f7e772fc7..62e9a8b684f 100644 > --- a/include/configs/mvebu_armada-37xx.h > +++ b/include/configs/mvebu_armada-37xx.h > @@ -40,6 +40,13 @@ > */ > #define DEFAULT_ENV_IS_RW /* required for configuring default > fdtfile= */ > > +/* > + * Platform identification (Marvell hw_info parameters) > + */ > +#ifdef CONFIG_MVEBU_MAC_HW_INFO > +#define CONFIG_ID_EEPROM /* U-Boot mac command */ > +#endif > + > /* > * Ethernet Driver configuration > */ > diff --git a/include/configs/mvebu_armada-8k.h > b/include/configs/mvebu_armada-8k.h > index 886f44c9030..34e757cb177 100644 > --- a/include/configs/mvebu_armada-8k.h > +++ b/include/configs/mvebu_armada-8k.h > @@ -29,6 +29,13 @@ > /* End of 16M scrubbed by training in bootrom */ > #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_TEXT_BASE + 0xFF0000) > > +/* > + * Platform identification (Marvell hw_info parameters) > + */ > +#ifdef CONFIG_MVEBU_MAC_HW_INFO > +#define CONFIG_ID_EEPROM /* U-Boot mac command */ > +#endif > + > /* When runtime detection fails this is the default */ > > #define CONFIG_SYS_MAX_NAND_DEVICE 1 > diff --git a/lib/hashtable.c b/lib/hashtable.c > index ff5ff726394..06322e3304a 100644 > --- a/lib/hashtable.c > +++ b/lib/hashtable.c > @@ -794,7 +794,7 @@ static int drop_var_from_set(const char *name, int nvars, > char * vars[]) > * multi-line values. > * > * In theory, arbitrary separator characters can be used, but only > - * '\0' and '\n' have really been tested. > + * '\0', '\n' and 0x20 have been tested. > */ > > int himport_r(struct hsearch_data *htab, > -- > 2.33.1 >

