From patchwork Mon May 2 14:18:36 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josua Mayer X-Patchwork-Id: 1625127 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=solid-run-com.20210112.gappssmtp.com header.i=@solid-run-com.20210112.gappssmtp.com header.a=rsa-sha256 header.s=20210112 header.b=XZPqqreu; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4KsQJm0FjBz9sFr for ; Tue, 3 May 2022 00:21:59 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id C5C7184004; Mon, 2 May 2022 16:19:55 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=solid-run.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=solid-run-com.20210112.gappssmtp.com header.i=@solid-run-com.20210112.gappssmtp.com header.b="XZPqqreu"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id AC1AC83937; Mon, 2 May 2022 16:19:38 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-wr1-x434.google.com (mail-wr1-x434.google.com [IPv6:2a00:1450:4864:20::434]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 8F5C783D07 for ; Mon, 2 May 2022 16:19:05 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=solid-run.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=josua@solid-run.com Received: by mail-wr1-x434.google.com with SMTP id t6so19790556wra.4 for ; Mon, 02 May 2022 07:19:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=solid-run-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=bVbHu4IFzWF4uHWOz6QY5f5gSWEloC4ft+MncTcpDyM=; b=XZPqqreuu2nN54cLb2v+2lf5V60UxFpPWzooypHTrGwq4UPSqGrOUpWPFsqXbdPNRV PxxX1jXlsqEjDC6c1lInoY8U4qixg52JLm+NrqtPJU7igkra24GxKSPIsht7p8IYBp4/ pDuo1fZ8trch6qoej7Yvfu0rE2kbLzaobuIaXIssvwRyRbApLq+PhS2du2DYPpLkvgns K+oAbqpGH4zQOkWM+I2+tMMUSMjjAdm+mIyues6BBE6vtnVjcqu9flOJKxidlnx8RUho /d4GfgsdQR0cLN5tVaEz+IVDhywWC5A1/jsNH7LJxdJXa+lFEHtefxflwYBIMwGqkxME YV0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=bVbHu4IFzWF4uHWOz6QY5f5gSWEloC4ft+MncTcpDyM=; b=rvIPNpGh4/KB7l6lSiNK/5zZYsw0WBaczLjEcCkivasANqLcYl/KeMZXAbT9LQYoMk zF1j2iQzzwjCJowlFX4pnK4dxDnmZ3EwCBaYb5PdXDQbGTXuYMrEcp+ydNr/JVazFuDE Bvyffn9yIb4InGmrpBYflQPxU5Nu7+hTWVLnIEmWUanUJ0cHH0MnWgvgqXWocAgeHONV /8Kqsx5Ak88L4aMaBPYHjlnQ65fvuhU0Qxt4ZIkdtTgpKaJTdT6ibzMeAWk2wX2/FOt3 UxPFxxCFco6+UPYPKtzSyyj3K4iXL+jgseyKiiBTGazpPwq1hMDUhiW1hkiAjTAG7L7F scnQ== X-Gm-Message-State: AOAM531eST+8HOMBP2JbUi9jXXpo2xwHu5lXAXQzmzs8VIktOezeBJXz P4zfEP4K9YlTmzr870cYyE1j3SiKofF2NgNa X-Google-Smtp-Source: ABdhPJwSFH5uGM4XuIyaOBGLzfDUqP62B+BHoakGHZJytbikviRk1ZP25OderRXDJlhZ6l5Z686cMQ== X-Received: by 2002:a5d:47a6:0:b0:20c:5f3d:44b8 with SMTP id 6-20020a5d47a6000000b0020c5f3d44b8mr5006087wrb.216.1651501143665; Mon, 02 May 2022 07:19:03 -0700 (PDT) Received: from josua-work.lan (bzq-82-81-222-124.cablep.bezeqint.net. [82.81.222.124]) by smtp.gmail.com with ESMTPSA id t1-20020adfba41000000b0020c6fa5a797sm940035wrg.91.2022.05.02.07.19.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 May 2022 07:19:03 -0700 (PDT) From: Josua Mayer To: u-boot@lists.denx.de Cc: Josua Mayer , Simon Glass , Michal Simek , Kory Maincent , Ovidiu Panait , Ashok Reddy Soma , Stefan Roese , Sven Auhagen , Heinrich Schuchardt , Alexandru Gagniuc , Philippe Reynes , Bin Meng , Eugen Hristev , Masahisa Kojima , =?utf-8?q?Pali_Roh=C3=A1r?= , Loic Poulain , Patrick Delaunay Subject: [PATCH 10/12] cmd: tlv_eeprom: split off tlv library from command Date: Mon, 2 May 2022 17:18:36 +0300 Message-Id: <20220502141838.15912-11-josua@solid-run.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220502141838.15912-1-josua@solid-run.com> References: <20220502141838.15912-1-josua@solid-run.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.5 at phobos.denx.de X-Virus-Status: Clean The eeprom command includes functions for reading and writing tlv-formatted data from an eeprom, as well as an implementation of the cli command tlv_eeprom. Split off the parsing, read and write into a standalone tlv library. Signed-off-by: Josua Mayer --- cmd/Kconfig | 2 + cmd/tlv_eeprom.c | 742 +----------------------------------------- lib/Kconfig | 2 + lib/Makefile | 2 + lib/tlv/Kconfig | 15 + lib/tlv/Makefile | 5 + lib/tlv/tlv_eeprom.c | 750 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 786 insertions(+), 732 deletions(-) create mode 100644 lib/tlv/Kconfig create mode 100644 lib/tlv/Makefile create mode 100644 lib/tlv/tlv_eeprom.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 2b575a2b42..821b5e9d6b 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -166,6 +166,7 @@ config CMD_REGINFO config CMD_TLV_EEPROM bool "tlv_eeprom" + select EEPROM_TLV_LIB depends on I2C_EEPROM help Display and program the system EEPROM data block in ONIE Tlvinfo @@ -173,6 +174,7 @@ config CMD_TLV_EEPROM config SPL_CMD_TLV_EEPROM bool "tlv_eeprom for SPL" + select SPL_EEPROM_TLV_LIB depends on SPL_I2C_EEPROM select SPL_DRIVERS_MISC help diff --git a/cmd/tlv_eeprom.c b/cmd/tlv_eeprom.c index c66116b2c4..99b79cad8b 100644 --- a/cmd/tlv_eeprom.c +++ b/cmd/tlv_eeprom.c @@ -10,131 +10,26 @@ * Copyright (C) 2022 Josua Mayer */ -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tlv_eeprom.h" - -DECLARE_GLOBAL_DATA_PTR; +#include +#include +#include +#include /* File scope function prototypes */ -static int read_eeprom(int devnum, u8 *eeprom); static void show_eeprom(int devnum, u8 *eeprom); -static void decode_tlv(struct tlvinfo_tlv *tlv); -static int set_mac(char *buf, const char *string); -static int set_date(char *buf, const char *string); -static int set_bytes(char *buf, const char *string, int *converted_accum); static void show_tlv_devices(int current_dev); +static inline const char *tlv_type2name(u8 type); +static void decode_tlv(struct tlvinfo_tlv *tlv); +static int do_tlv_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); +static void show_tlv_code_list(void); /* The EERPOM contents after being read into memory */ static u8 eeprom[TLV_INFO_MAX_LEN]; -static struct udevice *tlv_devices[TLV_MAX_DEVICES]; - #define to_header(p) ((struct tlvinfo_header *)p) #define to_entry(p) ((struct tlvinfo_tlv *)p) -/** - * Check whether eeprom device exists. - */ -bool exists_tlv_eeprom(int dev) -{ - return dev < TLV_MAX_DEVICES && tlv_devices[dev] != 0; -} - -static inline bool is_digit(char c) -{ - return (c >= '0' && c <= '9'); -} - -/** - * is_hex - * - * Tests if character is an ASCII hex digit - */ -static inline u8 is_hex(char p) -{ - return (((p >= '0') && (p <= '9')) || - ((p >= 'A') && (p <= 'F')) || - ((p >= 'a') && (p <= 'f'))); -} - -/** - * Validate the checksum in the provided TlvInfo EEPROM data. First, - * verify that the TlvInfo header is valid, then make sure the last - * TLV is a CRC-32 TLV. Then calculate the CRC over the EEPROM data - * and compare it to the value stored in the EEPROM CRC-32 TLV. - */ -bool tlvinfo_check_crc(u8 *eeprom) -{ - struct tlvinfo_header *eeprom_hdr = to_header(eeprom); - struct tlvinfo_tlv *eeprom_crc; - unsigned int calc_crc; - unsigned int stored_crc; - - // Is the eeprom header valid? - if (!is_valid_tlvinfo_header(eeprom_hdr)) - return false; - - // Is the last TLV a CRC? - eeprom_crc = to_entry(&eeprom[TLV_INFO_HEADER_SIZE + - be16_to_cpu(eeprom_hdr->totallen) - (TLV_INFO_ENTRY_SIZE + 4)]); - if (eeprom_crc->type != TLV_CODE_CRC_32 || eeprom_crc->length != 4) - return false; - - // Calculate the checksum - calc_crc = crc32(0, (void *)eeprom, - TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen) - 4); - stored_crc = (eeprom_crc->value[0] << 24) | - (eeprom_crc->value[1] << 16) | - (eeprom_crc->value[2] << 8) | - eeprom_crc->value[3]; - return calc_crc == stored_crc; -} - -/** - * read_eeprom - * - * Read the EEPROM into memory, if it hasn't already been read. - */ -static int read_eeprom(int devnum, u8 *eeprom) -{ - int ret; - struct tlvinfo_header *eeprom_hdr = to_header(eeprom); - struct tlvinfo_tlv *eeprom_tlv = to_entry(&eeprom[TLV_INFO_HEADER_SIZE]); - - /* Read the header */ - ret = read_tlv_eeprom((void *)eeprom_hdr, 0, TLV_INFO_HEADER_SIZE, devnum); - /* If the header was successfully read, read the TLVs */ - if (ret == 0 && is_valid_tlvinfo_header(eeprom_hdr)) - ret = read_tlv_eeprom((void *)eeprom_tlv, TLV_INFO_HEADER_SIZE, - be16_to_cpu(eeprom_hdr->totallen), devnum); - - // If the contents are invalid, start over with default contents - if (!is_valid_tlvinfo_header(eeprom_hdr) || - !tlvinfo_check_crc(eeprom)) { - strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING); - eeprom_hdr->version = TLV_INFO_VERSION; - eeprom_hdr->totallen = cpu_to_be16(0); - tlvinfo_update_crc(eeprom); - } - -#ifdef DEBUG - show_eeprom(devnum, eeprom); -#endif - - return ret; -} - /** * show_eeprom * @@ -323,70 +218,10 @@ static void decode_tlv(struct tlvinfo_tlv *tlv) printf("%-20s 0x%02X %3d %s\n", name, tlv->type, tlv->length, value); } -/** - * tlvinfo_update_crc - * - * This function updates the CRC-32 TLV. If there is no CRC-32 TLV, then - * one is added. This function should be called after each update to the - * EEPROM structure, to make sure the CRC is always correct. - */ -void tlvinfo_update_crc(u8 *eeprom) -{ - struct tlvinfo_header *eeprom_hdr = to_header(eeprom); - struct tlvinfo_tlv *eeprom_crc; - unsigned int calc_crc; - int eeprom_index; - - // Discover the CRC TLV - if (!tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) { - unsigned int totallen = be16_to_cpu(eeprom_hdr->totallen); - - if ((totallen + TLV_INFO_ENTRY_SIZE + 4) > TLV_TOTAL_LEN_MAX) - return; - eeprom_index = TLV_INFO_HEADER_SIZE + totallen; - eeprom_hdr->totallen = cpu_to_be16(totallen + TLV_INFO_ENTRY_SIZE + 4); - } - eeprom_crc = to_entry(&eeprom[eeprom_index]); - eeprom_crc->type = TLV_CODE_CRC_32; - eeprom_crc->length = 4; - - // Calculate the checksum - calc_crc = crc32(0, (void *)eeprom, - TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen) - 4); - eeprom_crc->value[0] = (calc_crc >> 24) & 0xFF; - eeprom_crc->value[1] = (calc_crc >> 16) & 0xFF; - eeprom_crc->value[2] = (calc_crc >> 8) & 0xFF; - eeprom_crc->value[3] = (calc_crc >> 0) & 0xFF; -} - -/** - * write_tlvinfo_tlv_eeprom - * - * Write the TLV data from CPU memory to the hardware. - */ -int write_tlvinfo_tlv_eeprom(void *eeprom, int dev) -{ - int ret = 0; - struct tlvinfo_header *eeprom_hdr = to_header(eeprom); - int eeprom_len; - - tlvinfo_update_crc(eeprom); - - eeprom_len = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen); - ret = write_tlv_eeprom(eeprom, eeprom_len, dev); - if (ret) { - printf("Programming failed.\n"); - return -1; - } - - printf("Programming passed.\n"); - return 0; -} - /** * show_tlv_code_list - Display the list of TLV codes and names */ -void show_tlv_code_list(void) +static void show_tlv_code_list(void) { int i; @@ -404,7 +239,7 @@ void show_tlv_code_list(void) * * This function implements the tlv_eeprom command. */ -int do_tlv_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +static int do_tlv_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { char cmd; struct tlvinfo_header *eeprom_hdr = to_header(eeprom); @@ -526,348 +361,6 @@ U_BOOT_CMD(tlv_eeprom, 4, 1, do_tlv_eeprom, " - List the understood TLV codes and names.\n" ); -/** - * tlvinfo_find_tlv - * - * This function finds the TLV with the supplied code in the EERPOM. - * An offset from the beginning of the EEPROM is returned in the - * eeprom_index parameter if the TLV is found. - */ -bool tlvinfo_find_tlv(u8 *eeprom, u8 tcode, int *eeprom_index) -{ - struct tlvinfo_header *eeprom_hdr = to_header(eeprom); - struct tlvinfo_tlv *eeprom_tlv; - int eeprom_end; - - // Search through the TLVs, looking for the first one which matches the - // supplied type code. - *eeprom_index = TLV_INFO_HEADER_SIZE; - eeprom_end = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen); - while (*eeprom_index < eeprom_end) { - eeprom_tlv = to_entry(&eeprom[*eeprom_index]); - if (!is_valid_tlvinfo_entry(eeprom_tlv)) - return false; - if (eeprom_tlv->type == tcode) - return true; - *eeprom_index += TLV_INFO_ENTRY_SIZE + eeprom_tlv->length; - } - return(false); -} - -/** - * tlvinfo_delete_tlv - * - * This function deletes the TLV with the specified type code from the - * EEPROM. - */ -bool tlvinfo_delete_tlv(u8 *eeprom, u8 code) -{ - int eeprom_index; - int tlength; - struct tlvinfo_header *eeprom_hdr = to_header(eeprom); - struct tlvinfo_tlv *eeprom_tlv; - - // Find the TLV and then move all following TLVs "forward" - if (tlvinfo_find_tlv(eeprom, code, &eeprom_index)) { - eeprom_tlv = to_entry(&eeprom[eeprom_index]); - tlength = TLV_INFO_ENTRY_SIZE + eeprom_tlv->length; - memcpy(&eeprom[eeprom_index], &eeprom[eeprom_index + tlength], - TLV_INFO_HEADER_SIZE + - be16_to_cpu(eeprom_hdr->totallen) - eeprom_index - - tlength); - eeprom_hdr->totallen = - cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) - - tlength); - tlvinfo_update_crc(eeprom); - return true; - } - return false; -} - -/** - * tlvinfo_add_tlv - * - * This function adds a TLV to the EEPROM, converting the value (a string) to - * the format in which it will be stored in the EEPROM. - */ -#define MAX_TLV_VALUE_LEN 256 -bool tlvinfo_add_tlv(u8 *eeprom, int tcode, char *strval) -{ - struct tlvinfo_header *eeprom_hdr = to_header(eeprom); - struct tlvinfo_tlv *eeprom_tlv; - int new_tlv_len = 0; - u32 value; - char data[MAX_TLV_VALUE_LEN]; - int eeprom_index; - - // Encode each TLV type into the format to be stored in the EERPOM - switch (tcode) { - case TLV_CODE_PRODUCT_NAME: - case TLV_CODE_PART_NUMBER: - case TLV_CODE_SERIAL_NUMBER: - case TLV_CODE_LABEL_REVISION: - case TLV_CODE_PLATFORM_NAME: - case TLV_CODE_ONIE_VERSION: - case TLV_CODE_MANUF_NAME: - case TLV_CODE_MANUF_COUNTRY: - case TLV_CODE_VENDOR_NAME: - case TLV_CODE_DIAG_VERSION: - case TLV_CODE_SERVICE_TAG: - strncpy(data, strval, MAX_TLV_VALUE_LEN); - new_tlv_len = min_t(size_t, MAX_TLV_VALUE_LEN, strlen(strval)); - break; - case TLV_CODE_DEVICE_VERSION: - value = simple_strtoul(strval, NULL, 0); - if (value >= 256) { - printf("ERROR: Device version must be 255 or less. Value supplied: %u", - value); - return false; - } - data[0] = value & 0xFF; - new_tlv_len = 1; - break; - case TLV_CODE_MAC_SIZE: - value = simple_strtoul(strval, NULL, 0); - if (value >= 65536) { - printf("ERROR: MAC Size must be 65535 or less. Value supplied: %u", - value); - return false; - } - data[0] = (value >> 8) & 0xFF; - data[1] = value & 0xFF; - new_tlv_len = 2; - break; - case TLV_CODE_MANUF_DATE: - if (set_date(data, strval) != 0) - return false; - new_tlv_len = 19; - break; - case TLV_CODE_MAC_BASE: - if (set_mac(data, strval) != 0) - return false; - new_tlv_len = 6; - break; - case TLV_CODE_CRC_32: - printf("WARNING: The CRC TLV is set automatically and cannot be set manually.\n"); - return false; - case TLV_CODE_VENDOR_EXT: - default: - if (set_bytes(data, strval, &new_tlv_len) != 0) - return false; - break; - } - - // Is there room for this TLV? - if ((be16_to_cpu(eeprom_hdr->totallen) + TLV_INFO_ENTRY_SIZE + new_tlv_len) > - TLV_TOTAL_LEN_MAX) { - printf("ERROR: There is not enough room in the EERPOM to save data.\n"); - return false; - } - - // Add TLV at the end, overwriting CRC TLV if it exists - if (tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) - eeprom_hdr->totallen = - cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) - - TLV_INFO_ENTRY_SIZE - 4); - else - eeprom_index = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen); - eeprom_tlv = to_entry(&eeprom[eeprom_index]); - eeprom_tlv->type = tcode; - eeprom_tlv->length = new_tlv_len; - memcpy(eeprom_tlv->value, data, new_tlv_len); - - // Update the total length and calculate (add) a new CRC-32 TLV - eeprom_hdr->totallen = cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) + - TLV_INFO_ENTRY_SIZE + new_tlv_len); - tlvinfo_update_crc(eeprom); - - return true; -} - -/** - * set_mac - * - * Converts a string MAC address into a binary buffer. - * - * This function takes a pointer to a MAC address string - * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number). - * The string format is verified and then converted to binary and - * stored in a buffer. - */ -static int set_mac(char *buf, const char *string) -{ - char *p = (char *)string; - int i; - int err = 0; - char *end; - - if (!p) { - printf("ERROR: NULL mac addr string passed in.\n"); - return -1; - } - - if (strlen(p) != 17) { - printf("ERROR: MAC address strlen() != 17 -- %zu\n", strlen(p)); - printf("ERROR: Bad MAC address format: %s\n", string); - return -1; - } - - for (i = 0; i < 17; i++) { - if ((i % 3) == 2) { - if (p[i] != ':') { - err++; - printf("ERROR: mac: p[%i] != :, found: `%c'\n", - i, p[i]); - break; - } - continue; - } else if (!is_hex(p[i])) { - err++; - printf("ERROR: mac: p[%i] != hex digit, found: `%c'\n", - i, p[i]); - break; - } - } - - if (err != 0) { - printf("ERROR: Bad MAC address format: %s\n", string); - return -1; - } - - /* Convert string to binary */ - for (i = 0, p = (char *)string; i < 6; i++) { - buf[i] = p ? hextoul(p, &end) : 0; - if (p) - p = (*end) ? end + 1 : end; - } - - if (!is_valid_ethaddr((u8 *)buf)) { - printf("ERROR: MAC address must not be 00:00:00:00:00:00, a multicast address or FF:FF:FF:FF:FF:FF.\n"); - printf("ERROR: Bad MAC address format: %s\n", string); - return -1; - } - - return 0; -} - -/** - * set_date - * - * Validates the format of the data string - * - * This function takes a pointer to a date string (i.e. MM/DD/YYYY hh:mm:ss) - * and validates that the format is correct. If so the string is copied - * to the supplied buffer. - */ -static int set_date(char *buf, const char *string) -{ - int i; - - if (!string) { - printf("ERROR: NULL date string passed in.\n"); - return -1; - } - - if (strlen(string) != 19) { - printf("ERROR: Date strlen() != 19 -- %zu\n", strlen(string)); - printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", - string); - return -1; - } - - for (i = 0; string[i] != 0; i++) { - switch (i) { - case 2: - case 5: - if (string[i] != '/') { - printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", - string); - return -1; - } - break; - case 10: - if (string[i] != ' ') { - printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", - string); - return -1; - } - break; - case 13: - case 16: - if (string[i] != ':') { - printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", - string); - return -1; - } - break; - default: - if (!is_digit(string[i])) { - printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", - string); - return -1; - } - break; - } - } - - strcpy(buf, string); - return 0; -} - -/** - * set_bytes - * - * Converts a space-separated string of decimal numbers into a - * buffer of bytes. - * - * This function takes a pointer to a space-separated string of decimal - * numbers (i.e. "128 0x55 0321") with "C" standard radix specifiers - * and converts them to an array of bytes. - */ -static int set_bytes(char *buf, const char *string, int *converted_accum) -{ - char *p = (char *)string; - int i; - uint byte; - - if (!p) { - printf("ERROR: NULL string passed in.\n"); - return -1; - } - - /* Convert string to bytes */ - for (i = 0, p = (char *)string; (i < TLV_VALUE_MAX_LEN) && (*p != 0); - i++) { - while ((*p == ' ') || (*p == '\t') || (*p == ',') || - (*p == ';')) { - p++; - } - if (*p != 0) { - if (!is_digit(*p)) { - printf("ERROR: Non-digit found in byte string: (%s)\n", - string); - return -1; - } - byte = simple_strtoul(p, &p, 0); - if (byte >= 256) { - printf("ERROR: The value specified is greater than 255: (%u) in string: %s\n", - byte, string); - return -1; - } - buf[i] = byte & 0xFF; - } - } - - if (i == TLV_VALUE_MAX_LEN && (*p != 0)) { - printf("ERROR: Trying to assign too many bytes (max: %d) in string: %s\n", - TLV_VALUE_MAX_LEN, string); - return -1; - } - - *converted_accum = i; - return 0; -} - static void show_tlv_devices(int current_dev) { unsigned int dev; @@ -877,218 +370,3 @@ static void show_tlv_devices(int current_dev) printf("TLV: %u%s\n", dev, (dev == current_dev) ? " (*)" : ""); } - -static int find_tlv_devices(struct udevice **tlv_devices_p) -{ - int ret; - int count_dev = 0; - struct udevice *dev; - - for (ret = uclass_first_device_check(UCLASS_I2C_EEPROM, &dev); - dev; - ret = uclass_next_device_check(&dev)) { - if (ret == 0) - tlv_devices_p[count_dev++] = dev; - if (count_dev >= TLV_MAX_DEVICES) - break; - } - - return (count_dev == 0) ? -ENODEV : 0; -} - -static struct udevice *find_tlv_device_by_index(int dev_num) -{ - struct udevice *local_tlv_devices[TLV_MAX_DEVICES] = {}; - struct udevice **tlv_devices_p; - int ret; - - if (gd->flags & (GD_FLG_RELOC | GD_FLG_SPL_INIT)) { - /* Assume BSS is initialized; use static data */ - if (tlv_devices[dev_num]) - return tlv_devices[dev_num]; - tlv_devices_p = tlv_devices; - } else { - tlv_devices_p = local_tlv_devices; - } - - ret = find_tlv_devices(tlv_devices_p); - if (ret == 0 && tlv_devices_p[dev_num]) - return tlv_devices_p[dev_num]; - - return NULL; -} - -/** - * read_tlv_eeprom - read the hwinfo from i2c EEPROM - */ -int read_tlv_eeprom(void *eeprom, int offset, int len, int dev_num) -{ - struct udevice *dev; - - if (dev_num >= TLV_MAX_DEVICES) - return -EINVAL; - - dev = find_tlv_device_by_index(dev_num); - if (!dev) - return -ENODEV; - - return i2c_eeprom_read(dev, offset, eeprom, len); -} - -/** - * write_tlv_eeprom - write the hwinfo to i2c EEPROM - */ -int write_tlv_eeprom(void *eeprom, int len, int dev) -{ - if (!(gd->flags & GD_FLG_RELOC)) - return -ENODEV; - if (!tlv_devices[dev]) - return -ENODEV; - - return i2c_eeprom_write(tlv_devices[dev], 0, eeprom, len); -} - -int read_tlvinfo_tlv_eeprom(void *eeprom, struct tlvinfo_header **hdr, - struct tlvinfo_tlv **first_entry, int dev_num) -{ - int ret; - struct tlvinfo_header *tlv_hdr; - struct tlvinfo_tlv *tlv_ent; - - /* Read TLV header */ - ret = read_tlv_eeprom(eeprom, 0, TLV_INFO_HEADER_SIZE, dev_num); - if (ret < 0) - return ret; - - tlv_hdr = eeprom; - if (!is_valid_tlvinfo_header(tlv_hdr)) - return -EINVAL; - - /* Read TLV entries */ - tlv_ent = to_entry(&tlv_hdr[1]); - ret = read_tlv_eeprom(tlv_ent, TLV_INFO_HEADER_SIZE, - be16_to_cpu(tlv_hdr->totallen), dev_num); - if (ret < 0) - return ret; - if (!tlvinfo_check_crc(eeprom)) - return -EINVAL; - - *hdr = tlv_hdr; - *first_entry = tlv_ent; - - return 0; -} - -/** - * mac_read_from_eeprom - * - * Read the MAC addresses from EEPROM - * - * This function reads the MAC addresses from EEPROM and sets the - * appropriate environment variables for each one read. - * - * The environment variables are only set if they haven't been set already. - * This ensures that any user-saved variables are never overwritten. - * - * This function must be called after relocation. - */ -int mac_read_from_eeprom(void) -{ - unsigned int i; - int eeprom_index; - struct tlvinfo_tlv *eeprom_tlv; - int maccount; - u8 macbase[6]; - struct tlvinfo_header *eeprom_hdr = to_header(eeprom); - int devnum = 0; // TODO: support multiple EEPROMs - - puts("EEPROM: "); - - if (read_eeprom(devnum, eeprom)) { - printf("Read failed.\n"); - return -1; - } - - maccount = 1; - if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_SIZE, &eeprom_index)) { - eeprom_tlv = to_entry(&eeprom[eeprom_index]); - maccount = (eeprom_tlv->value[0] << 8) | eeprom_tlv->value[1]; - } - - memcpy(macbase, "\0\0\0\0\0\0", 6); - if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_BASE, &eeprom_index)) { - eeprom_tlv = to_entry(&eeprom[eeprom_index]); - memcpy(macbase, eeprom_tlv->value, 6); - } - - for (i = 0; i < maccount; i++) { - if (is_valid_ethaddr(macbase)) { - char ethaddr[18]; - char enetvar[11]; - - sprintf(ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X", - macbase[0], macbase[1], macbase[2], - macbase[3], macbase[4], macbase[5]); - sprintf(enetvar, i ? "eth%daddr" : "ethaddr", i); - /* Only initialize environment variables that are blank - * (i.e. have not yet been set) - */ - if (!env_get(enetvar)) - env_set(enetvar, ethaddr); - - macbase[5]++; - if (macbase[5] == 0) { - macbase[4]++; - if (macbase[4] == 0) { - macbase[3]++; - if (macbase[3] == 0) { - macbase[0] = 0; - macbase[1] = 0; - macbase[2] = 0; - } - } - } - } - } - - printf("%s v%u len=%u\n", eeprom_hdr->signature, eeprom_hdr->version, - be16_to_cpu(eeprom_hdr->totallen)); - - return 0; -} - -/** - * populate_serial_number - read the serial number from EEPROM - * - * This function reads the serial number from the EEPROM and sets the - * appropriate environment variable. - * - * The environment variable is only set if it has not been set - * already. This ensures that any user-saved variables are never - * overwritten. - * - * This function must be called after relocation. - */ -int populate_serial_number(int devnum) -{ - char serialstr[257]; - int eeprom_index; - struct tlvinfo_tlv *eeprom_tlv; - - if (env_get("serial#")) - return 0; - - if (read_eeprom(devnum, eeprom)) { - printf("Read failed.\n"); - return -1; - } - - if (tlvinfo_find_tlv(eeprom, TLV_CODE_SERIAL_NUMBER, &eeprom_index)) { - eeprom_tlv = to_entry(&eeprom[eeprom_index]); - memcpy(serialstr, eeprom_tlv->value, eeprom_tlv->length); - serialstr[eeprom_tlv->length] = 0; - env_set("serial#", serialstr); - } - - return 0; -} diff --git a/lib/Kconfig b/lib/Kconfig index 858be14f09..4497efbbf4 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -909,3 +909,5 @@ config PHANDLE_CHECK_SEQ phandles in fdtdec_get_alias_seq() function. endmenu + +source lib/tlv/Kconfig diff --git a/lib/Makefile b/lib/Makefile index d9b1811f75..1bc6dad8ac 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_LIBAVB) += libavb/ obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += libfdt/ obj-$(CONFIG_$(SPL_TPL_)OF_REAL) += fdtdec_common.o fdtdec.o +obj-$(CONFIG_$(SPL_)EEPROM_TLV_LIB) += tlv/ + ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_YMODEM_SUPPORT) += crc16-ccitt.o obj-$(CONFIG_$(SPL_TPL_)HASH) += crc16-ccitt.o diff --git a/lib/tlv/Kconfig b/lib/tlv/Kconfig new file mode 100644 index 0000000000..b3912ada78 --- /dev/null +++ b/lib/tlv/Kconfig @@ -0,0 +1,15 @@ +config EEPROM_TLV_LIB + bool "Enable EEPROM TLV library" + depends on I2C_EEPROM + help + Selecting this option will enable the shared EEPROM TLV library code. + It provides functions for reading, writing and parsing of + TLV-encoded data from EEPROMs. + +config SPL_EEPROM_TLV_LIB + bool "Enable EEPROM TLV library for SPL" + depends on SPL_I2C_EEPROM + help + Selecting this option will enable the shared EEPROM TLV library code. + It provides functions for reading, writing and parsing of + TLV-encoded data from EEPROMs. diff --git a/lib/tlv/Makefile b/lib/tlv/Makefile new file mode 100644 index 0000000000..8e96752e75 --- /dev/null +++ b/lib/tlv/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2017 Linaro + +obj-$(CONFIG_EEPROM_TLV_LIB) += tlv_eeprom.o diff --git a/lib/tlv/tlv_eeprom.c b/lib/tlv/tlv_eeprom.c new file mode 100644 index 0000000000..464f0aa1fa --- /dev/null +++ b/lib/tlv/tlv_eeprom.c @@ -0,0 +1,750 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * See file CREDITS for list of people who contributed to this + * project. + * + * Copyright (C) 2013 Curt Brune + * Copyright (C) 2014 Srideep + * Copyright (C) 2013 Miles Tseng + * Copyright (C) 2014,2016 david_yang + * Copyright (C) 2022 Josua Mayer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv_eeprom.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* File scope function prototypes */ +static int read_eeprom(int devnum, u8 *eeprom); +static int set_mac(char *buf, const char *string); +static int set_date(char *buf, const char *string); +static int set_bytes(char *buf, const char *string, int *converted_accum); + +/* The EERPOM contents after being read into memory */ +static u8 eeprom[TLV_INFO_MAX_LEN]; + +static struct udevice *tlv_devices[TLV_MAX_DEVICES]; + +#define to_header(p) ((struct tlvinfo_header *)p) +#define to_entry(p) ((struct tlvinfo_tlv *)p) + +/** + * Check whether eeprom device exists. + */ +bool exists_tlv_eeprom(int dev) +{ + return dev < TLV_MAX_DEVICES && tlv_devices[dev] != 0; +} + +static inline bool is_digit(char c) +{ + return (c >= '0' && c <= '9'); +} + +/** + * is_hex + * + * Tests if character is an ASCII hex digit + */ +static inline u8 is_hex(char p) +{ + return (((p >= '0') && (p <= '9')) || + ((p >= 'A') && (p <= 'F')) || + ((p >= 'a') && (p <= 'f'))); +} + +/** + * Validate the checksum in the provided TlvInfo EEPROM data. First, + * verify that the TlvInfo header is valid, then make sure the last + * TLV is a CRC-32 TLV. Then calculate the CRC over the EEPROM data + * and compare it to the value stored in the EEPROM CRC-32 TLV. + */ +bool tlvinfo_check_crc(u8 *eeprom) +{ + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_crc; + unsigned int calc_crc; + unsigned int stored_crc; + + // Is the eeprom header valid? + if (!is_valid_tlvinfo_header(eeprom_hdr)) + return false; + + // Is the last TLV a CRC? + eeprom_crc = to_entry(&eeprom[TLV_INFO_HEADER_SIZE + + be16_to_cpu(eeprom_hdr->totallen) - (TLV_INFO_ENTRY_SIZE + 4)]); + if (eeprom_crc->type != TLV_CODE_CRC_32 || eeprom_crc->length != 4) + return false; + + // Calculate the checksum + calc_crc = crc32(0, (void *)eeprom, + TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen) - 4); + stored_crc = (eeprom_crc->value[0] << 24) | + (eeprom_crc->value[1] << 16) | + (eeprom_crc->value[2] << 8) | + eeprom_crc->value[3]; + return calc_crc == stored_crc; +} + +/** + * read_eeprom + * + * Read the EEPROM into memory, if it hasn't already been read. + */ +static int read_eeprom(int devnum, u8 *eeprom) +{ + int ret; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv = to_entry(&eeprom[TLV_INFO_HEADER_SIZE]); + + /* Read the header */ + ret = read_tlv_eeprom((void *)eeprom_hdr, 0, TLV_INFO_HEADER_SIZE, devnum); + /* If the header was successfully read, read the TLVs */ + if (ret == 0 && is_valid_tlvinfo_header(eeprom_hdr)) + ret = read_tlv_eeprom((void *)eeprom_tlv, TLV_INFO_HEADER_SIZE, + be16_to_cpu(eeprom_hdr->totallen), devnum); + + // If the contents are invalid, start over with default contents + if (!is_valid_tlvinfo_header(eeprom_hdr) || + !tlvinfo_check_crc(eeprom)) { + strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING); + eeprom_hdr->version = TLV_INFO_VERSION; + eeprom_hdr->totallen = cpu_to_be16(0); + tlvinfo_update_crc(eeprom); + } + +#ifdef DEBUG + show_eeprom(devnum, eeprom); +#endif + + return ret; +} + +/** + * tlvinfo_update_crc + * + * This function updates the CRC-32 TLV. If there is no CRC-32 TLV, then + * one is added. This function should be called after each update to the + * EEPROM structure, to make sure the CRC is always correct. + */ +void tlvinfo_update_crc(u8 *eeprom) +{ + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_crc; + unsigned int calc_crc; + int eeprom_index; + + // Discover the CRC TLV + if (!tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) { + unsigned int totallen = be16_to_cpu(eeprom_hdr->totallen); + + if ((totallen + TLV_INFO_ENTRY_SIZE + 4) > TLV_TOTAL_LEN_MAX) + return; + eeprom_index = TLV_INFO_HEADER_SIZE + totallen; + eeprom_hdr->totallen = cpu_to_be16(totallen + TLV_INFO_ENTRY_SIZE + 4); + } + eeprom_crc = to_entry(&eeprom[eeprom_index]); + eeprom_crc->type = TLV_CODE_CRC_32; + eeprom_crc->length = 4; + + // Calculate the checksum + calc_crc = crc32(0, (void *)eeprom, + TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen) - 4); + eeprom_crc->value[0] = (calc_crc >> 24) & 0xFF; + eeprom_crc->value[1] = (calc_crc >> 16) & 0xFF; + eeprom_crc->value[2] = (calc_crc >> 8) & 0xFF; + eeprom_crc->value[3] = (calc_crc >> 0) & 0xFF; +} + +/** + * write_tlvinfo_tlv_eeprom + * + * Write the TLV data from CPU memory to the hardware. + */ +int write_tlvinfo_tlv_eeprom(void *eeprom, int dev) +{ + int ret = 0; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + int eeprom_len; + + tlvinfo_update_crc(eeprom); + + eeprom_len = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen); + ret = write_tlv_eeprom(eeprom, eeprom_len, dev); + if (ret) { + printf("Programming failed.\n"); + return -1; + } + + printf("Programming passed.\n"); + return 0; +} + +/** + * tlvinfo_find_tlv + * + * This function finds the TLV with the supplied code in the EERPOM. + * An offset from the beginning of the EEPROM is returned in the + * eeprom_index parameter if the TLV is found. + */ +bool tlvinfo_find_tlv(u8 *eeprom, u8 tcode, int *eeprom_index) +{ + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv; + int eeprom_end; + + // Search through the TLVs, looking for the first one which matches the + // supplied type code. + *eeprom_index = TLV_INFO_HEADER_SIZE; + eeprom_end = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen); + while (*eeprom_index < eeprom_end) { + eeprom_tlv = to_entry(&eeprom[*eeprom_index]); + if (!is_valid_tlvinfo_entry(eeprom_tlv)) + return false; + if (eeprom_tlv->type == tcode) + return true; + *eeprom_index += TLV_INFO_ENTRY_SIZE + eeprom_tlv->length; + } + return(false); +} + +/** + * tlvinfo_delete_tlv + * + * This function deletes the TLV with the specified type code from the + * EEPROM. + */ +bool tlvinfo_delete_tlv(u8 *eeprom, u8 code) +{ + int eeprom_index; + int tlength; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv; + + // Find the TLV and then move all following TLVs "forward" + if (tlvinfo_find_tlv(eeprom, code, &eeprom_index)) { + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + tlength = TLV_INFO_ENTRY_SIZE + eeprom_tlv->length; + memcpy(&eeprom[eeprom_index], &eeprom[eeprom_index + tlength], + TLV_INFO_HEADER_SIZE + + be16_to_cpu(eeprom_hdr->totallen) - eeprom_index - + tlength); + eeprom_hdr->totallen = + cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) - + tlength); + tlvinfo_update_crc(eeprom); + return true; + } + return false; +} + +/** + * tlvinfo_add_tlv + * + * This function adds a TLV to the EEPROM, converting the value (a string) to + * the format in which it will be stored in the EEPROM. + */ +#define MAX_TLV_VALUE_LEN 256 +bool tlvinfo_add_tlv(u8 *eeprom, int tcode, char *strval) +{ + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + struct tlvinfo_tlv *eeprom_tlv; + int new_tlv_len = 0; + u32 value; + char data[MAX_TLV_VALUE_LEN]; + int eeprom_index; + + // Encode each TLV type into the format to be stored in the EERPOM + switch (tcode) { + case TLV_CODE_PRODUCT_NAME: + case TLV_CODE_PART_NUMBER: + case TLV_CODE_SERIAL_NUMBER: + case TLV_CODE_LABEL_REVISION: + case TLV_CODE_PLATFORM_NAME: + case TLV_CODE_ONIE_VERSION: + case TLV_CODE_MANUF_NAME: + case TLV_CODE_MANUF_COUNTRY: + case TLV_CODE_VENDOR_NAME: + case TLV_CODE_DIAG_VERSION: + case TLV_CODE_SERVICE_TAG: + strncpy(data, strval, MAX_TLV_VALUE_LEN); + new_tlv_len = min_t(size_t, MAX_TLV_VALUE_LEN, strlen(strval)); + break; + case TLV_CODE_DEVICE_VERSION: + value = simple_strtoul(strval, NULL, 0); + if (value >= 256) { + printf("ERROR: Device version must be 255 or less. Value supplied: %u", + value); + return false; + } + data[0] = value & 0xFF; + new_tlv_len = 1; + break; + case TLV_CODE_MAC_SIZE: + value = simple_strtoul(strval, NULL, 0); + if (value >= 65536) { + printf("ERROR: MAC Size must be 65535 or less. Value supplied: %u", + value); + return false; + } + data[0] = (value >> 8) & 0xFF; + data[1] = value & 0xFF; + new_tlv_len = 2; + break; + case TLV_CODE_MANUF_DATE: + if (set_date(data, strval) != 0) + return false; + new_tlv_len = 19; + break; + case TLV_CODE_MAC_BASE: + if (set_mac(data, strval) != 0) + return false; + new_tlv_len = 6; + break; + case TLV_CODE_CRC_32: + printf("WARNING: The CRC TLV is set automatically and cannot be set manually.\n"); + return false; + case TLV_CODE_VENDOR_EXT: + default: + if (set_bytes(data, strval, &new_tlv_len) != 0) + return false; + break; + } + + // Is there room for this TLV? + if ((be16_to_cpu(eeprom_hdr->totallen) + TLV_INFO_ENTRY_SIZE + new_tlv_len) > + TLV_TOTAL_LEN_MAX) { + printf("ERROR: There is not enough room in the EERPOM to save data.\n"); + return false; + } + + // Add TLV at the end, overwriting CRC TLV if it exists + if (tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) + eeprom_hdr->totallen = + cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) - + TLV_INFO_ENTRY_SIZE - 4); + else + eeprom_index = TLV_INFO_HEADER_SIZE + be16_to_cpu(eeprom_hdr->totallen); + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + eeprom_tlv->type = tcode; + eeprom_tlv->length = new_tlv_len; + memcpy(eeprom_tlv->value, data, new_tlv_len); + + // Update the total length and calculate (add) a new CRC-32 TLV + eeprom_hdr->totallen = cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) + + TLV_INFO_ENTRY_SIZE + new_tlv_len); + tlvinfo_update_crc(eeprom); + + return true; +} + +/** + * set_mac + * + * Converts a string MAC address into a binary buffer. + * + * This function takes a pointer to a MAC address string + * (i.e."XX:XX:XX:XX:XX:XX", where "XX" is a two-digit hex number). + * The string format is verified and then converted to binary and + * stored in a buffer. + */ +static int set_mac(char *buf, const char *string) +{ + char *p = (char *)string; + int i; + int err = 0; + char *end; + + if (!p) { + printf("ERROR: NULL mac addr string passed in.\n"); + return -1; + } + + if (strlen(p) != 17) { + printf("ERROR: MAC address strlen() != 17 -- %zu\n", strlen(p)); + printf("ERROR: Bad MAC address format: %s\n", string); + return -1; + } + + for (i = 0; i < 17; i++) { + if ((i % 3) == 2) { + if (p[i] != ':') { + err++; + printf("ERROR: mac: p[%i] != :, found: `%c'\n", + i, p[i]); + break; + } + continue; + } else if (!is_hex(p[i])) { + err++; + printf("ERROR: mac: p[%i] != hex digit, found: `%c'\n", + i, p[i]); + break; + } + } + + if (err != 0) { + printf("ERROR: Bad MAC address format: %s\n", string); + return -1; + } + + /* Convert string to binary */ + for (i = 0, p = (char *)string; i < 6; i++) { + buf[i] = p ? hextoul(p, &end) : 0; + if (p) + p = (*end) ? end + 1 : end; + } + + if (!is_valid_ethaddr((u8 *)buf)) { + printf("ERROR: MAC address must not be 00:00:00:00:00:00, a multicast address or FF:FF:FF:FF:FF:FF.\n"); + printf("ERROR: Bad MAC address format: %s\n", string); + return -1; + } + + return 0; +} + +/** + * set_date + * + * Validates the format of the data string + * + * This function takes a pointer to a date string (i.e. MM/DD/YYYY hh:mm:ss) + * and validates that the format is correct. If so the string is copied + * to the supplied buffer. + */ +static int set_date(char *buf, const char *string) +{ + int i; + + if (!string) { + printf("ERROR: NULL date string passed in.\n"); + return -1; + } + + if (strlen(string) != 19) { + printf("ERROR: Date strlen() != 19 -- %zu\n", strlen(string)); + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + + for (i = 0; string[i] != 0; i++) { + switch (i) { + case 2: + case 5: + if (string[i] != '/') { + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + break; + case 10: + if (string[i] != ' ') { + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + break; + case 13: + case 16: + if (string[i] != ':') { + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + break; + default: + if (!is_digit(string[i])) { + printf("ERROR: Bad date format (MM/DD/YYYY hh:mm:ss): %s\n", + string); + return -1; + } + break; + } + } + + strcpy(buf, string); + return 0; +} + +/** + * set_bytes + * + * Converts a space-separated string of decimal numbers into a + * buffer of bytes. + * + * This function takes a pointer to a space-separated string of decimal + * numbers (i.e. "128 0x55 0321") with "C" standard radix specifiers + * and converts them to an array of bytes. + */ +static int set_bytes(char *buf, const char *string, int *converted_accum) +{ + char *p = (char *)string; + int i; + uint byte; + + if (!p) { + printf("ERROR: NULL string passed in.\n"); + return -1; + } + + /* Convert string to bytes */ + for (i = 0, p = (char *)string; (i < TLV_VALUE_MAX_LEN) && (*p != 0); + i++) { + while ((*p == ' ') || (*p == '\t') || (*p == ',') || + (*p == ';')) { + p++; + } + if (*p != 0) { + if (!is_digit(*p)) { + printf("ERROR: Non-digit found in byte string: (%s)\n", + string); + return -1; + } + byte = simple_strtoul(p, &p, 0); + if (byte >= 256) { + printf("ERROR: The value specified is greater than 255: (%u) in string: %s\n", + byte, string); + return -1; + } + buf[i] = byte & 0xFF; + } + } + + if (i == TLV_VALUE_MAX_LEN && (*p != 0)) { + printf("ERROR: Trying to assign too many bytes (max: %d) in string: %s\n", + TLV_VALUE_MAX_LEN, string); + return -1; + } + + *converted_accum = i; + return 0; +} + +static int find_tlv_devices(struct udevice **tlv_devices_p) +{ + int ret; + int count_dev = 0; + struct udevice *dev; + + for (ret = uclass_first_device_check(UCLASS_I2C_EEPROM, &dev); + dev; + ret = uclass_next_device_check(&dev)) { + if (ret == 0) + tlv_devices_p[count_dev++] = dev; + if (count_dev >= TLV_MAX_DEVICES) + break; + } + + return (count_dev == 0) ? -ENODEV : 0; +} + +static struct udevice *find_tlv_device_by_index(int dev_num) +{ + struct udevice *local_tlv_devices[TLV_MAX_DEVICES] = {}; + struct udevice **tlv_devices_p; + int ret; + + if (gd->flags & (GD_FLG_RELOC | GD_FLG_SPL_INIT)) { + /* Assume BSS is initialized; use static data */ + if (tlv_devices[dev_num]) + return tlv_devices[dev_num]; + tlv_devices_p = tlv_devices; + } else { + tlv_devices_p = local_tlv_devices; + } + + ret = find_tlv_devices(tlv_devices_p); + if (ret == 0 && tlv_devices_p[dev_num]) + return tlv_devices_p[dev_num]; + + return NULL; +} + +/** + * read_tlv_eeprom - read the hwinfo from i2c EEPROM + */ +int read_tlv_eeprom(void *eeprom, int offset, int len, int dev_num) +{ + struct udevice *dev; + + if (dev_num >= TLV_MAX_DEVICES) + return -EINVAL; + + dev = find_tlv_device_by_index(dev_num); + if (!dev) + return -ENODEV; + + return i2c_eeprom_read(dev, offset, eeprom, len); +} + +/** + * write_tlv_eeprom - write the hwinfo to i2c EEPROM + */ +int write_tlv_eeprom(void *eeprom, int len, int dev) +{ + if (!(gd->flags & GD_FLG_RELOC)) + return -ENODEV; + if (!tlv_devices[dev]) + return -ENODEV; + + return i2c_eeprom_write(tlv_devices[dev], 0, eeprom, len); +} + +int read_tlvinfo_tlv_eeprom(void *eeprom, struct tlvinfo_header **hdr, + struct tlvinfo_tlv **first_entry, int dev_num) +{ + int ret; + struct tlvinfo_header *tlv_hdr; + struct tlvinfo_tlv *tlv_ent; + + /* Read TLV header */ + ret = read_tlv_eeprom(eeprom, 0, TLV_INFO_HEADER_SIZE, dev_num); + if (ret < 0) + return ret; + + tlv_hdr = eeprom; + if (!is_valid_tlvinfo_header(tlv_hdr)) + return -EINVAL; + + /* Read TLV entries */ + tlv_ent = to_entry(&tlv_hdr[1]); + ret = read_tlv_eeprom(tlv_ent, TLV_INFO_HEADER_SIZE, + be16_to_cpu(tlv_hdr->totallen), dev_num); + if (ret < 0) + return ret; + if (!tlvinfo_check_crc(eeprom)) + return -EINVAL; + + *hdr = tlv_hdr; + *first_entry = tlv_ent; + + return 0; +} + +/** + * mac_read_from_eeprom + * + * Read the MAC addresses from EEPROM + * + * This function reads the MAC addresses from EEPROM and sets the + * appropriate environment variables for each one read. + * + * The environment variables are only set if they haven't been set already. + * This ensures that any user-saved variables are never overwritten. + * + * This function must be called after relocation. + */ +int mac_read_from_eeprom(void) +{ + unsigned int i; + int eeprom_index; + struct tlvinfo_tlv *eeprom_tlv; + int maccount; + u8 macbase[6]; + struct tlvinfo_header *eeprom_hdr = to_header(eeprom); + int devnum = 0; // TODO: support multiple EEPROMs + + puts("EEPROM: "); + + if (read_eeprom(devnum, eeprom)) { + printf("Read failed.\n"); + return -1; + } + + maccount = 1; + if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_SIZE, &eeprom_index)) { + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + maccount = (eeprom_tlv->value[0] << 8) | eeprom_tlv->value[1]; + } + + memcpy(macbase, "\0\0\0\0\0\0", 6); + if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_BASE, &eeprom_index)) { + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + memcpy(macbase, eeprom_tlv->value, 6); + } + + for (i = 0; i < maccount; i++) { + if (is_valid_ethaddr(macbase)) { + char ethaddr[18]; + char enetvar[11]; + + sprintf(ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X", + macbase[0], macbase[1], macbase[2], + macbase[3], macbase[4], macbase[5]); + sprintf(enetvar, i ? "eth%daddr" : "ethaddr", i); + /* Only initialize environment variables that are blank + * (i.e. have not yet been set) + */ + if (!env_get(enetvar)) + env_set(enetvar, ethaddr); + + macbase[5]++; + if (macbase[5] == 0) { + macbase[4]++; + if (macbase[4] == 0) { + macbase[3]++; + if (macbase[3] == 0) { + macbase[0] = 0; + macbase[1] = 0; + macbase[2] = 0; + } + } + } + } + } + + printf("%s v%u len=%u\n", eeprom_hdr->signature, eeprom_hdr->version, + be16_to_cpu(eeprom_hdr->totallen)); + + return 0; +} + +/** + * populate_serial_number - read the serial number from EEPROM + * + * This function reads the serial number from the EEPROM and sets the + * appropriate environment variable. + * + * The environment variable is only set if it has not been set + * already. This ensures that any user-saved variables are never + * overwritten. + * + * This function must be called after relocation. + */ +int populate_serial_number(int devnum) +{ + char serialstr[257]; + int eeprom_index; + struct tlvinfo_tlv *eeprom_tlv; + + if (env_get("serial#")) + return 0; + + if (read_eeprom(devnum, eeprom)) { + printf("Read failed.\n"); + return -1; + } + + if (tlvinfo_find_tlv(eeprom, TLV_CODE_SERIAL_NUMBER, &eeprom_index)) { + eeprom_tlv = to_entry(&eeprom[eeprom_index]); + memcpy(serialstr, eeprom_tlv->value, eeprom_tlv->length); + serialstr[eeprom_tlv->length] = 0; + env_set("serial#", serialstr); + } + + return 0; +}