+}
+
+/**
+ * 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')));
+}
+
+/**
+ * is_checksum_valid
+ *
+ * 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.
+ */
+static bool is_checksum_valid(u8 *eeprom)
+{
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+ tlvinfo_tlv_t * 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 = (tlvinfo_tlv_t *) &eeprom[sizeof(tlvinfo_header_t) +
+
be16_to_cpu(eeprom_hdr->totallen) - (sizeof(tlvinfo_tlv_t) + 4)];
+ if ((eeprom_crc->type != TLV_CODE_CRC_32) || (eeprom_crc->length != 4))
{
+ return(FALSE);
+ }
+
+ // Calculate the checksum
+ calc_crc = crc32(0, (void *)eeprom,
+ sizeof(tlvinfo_header_t) +
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(u8 *eeprom)
+{
+ int ret;
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+ tlvinfo_tlv_t * eeprom_tlv = (tlvinfo_tlv_t *)
&eeprom[sizeof(tlvinfo_header_t)];
+
+ if (has_been_read)
+ return 0;
+
+ /* Read the header */
+ ret = read_sys_eeprom((void *)eeprom_hdr, 0, sizeof(tlvinfo_header_t));
+ /* If the header was successfully read, read the TLVs */
+ if ((ret == 0) && is_valid_tlvinfo_header(eeprom_hdr)) {
+ ret = read_sys_eeprom((void *)eeprom_tlv,
sizeof(tlvinfo_header_t),
+ be16_to_cpu(eeprom_hdr->totallen));
+ }
+
+ // If the contents are invalid, start over with default contents
+ if ( !is_valid_tlvinfo_header(eeprom_hdr) || !is_checksum_valid(eeprom)
){
+ strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING);
+ eeprom_hdr->version = TLV_INFO_VERSION;
+ eeprom_hdr->totallen = cpu_to_be16(0);
+ update_crc(eeprom);
+ }
+
+ has_been_read = 1;
+
+#ifdef DEBUG
+ show_eeprom(eeprom);
+#endif
+
+ return ret;
+}
+
+/**
+ * show_eeprom
+ *
+ * Display the contents of the EEPROM
+ */
+static void show_eeprom(u8 *eeprom)
+{
+ int tlv_end;
+ int curr_tlv;
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+ tlvinfo_tlv_t * eeprom_tlv;
+
+ if ( !is_valid_tlvinfo_header(eeprom_hdr) ) {
+ printf("EEPROM does not contain data in a valid TlvInfo
format.\n");
+ return;
+ }
+
+ printf("TLV: %u\n", current_dev);
+ printf("TlvInfo Header:\n");
+ printf(" Id String: %s\n", eeprom_hdr->signature);
+ printf(" Version: %d\n", eeprom_hdr->version);
+ printf(" Total Length: %d\n", be16_to_cpu(eeprom_hdr->totallen));
+
+ printf("TLV Name Code Len Value\n");
+ printf("-------------------- ---- --- -----\n");
+ curr_tlv = sizeof(tlvinfo_header_t);
+ tlv_end = sizeof(tlvinfo_header_t) + be16_to_cpu(eeprom_hdr->totallen);
+ while (curr_tlv < tlv_end) {
+ eeprom_tlv = (tlvinfo_tlv_t *) &eeprom[curr_tlv];
+ if (!is_valid_tlv(eeprom_tlv)) {
+ printf("Invalid TLV field starting at EEPROM offset
%d\n", curr_tlv);
+ return;
+ }
+ decode_tlv(eeprom_tlv);
+ curr_tlv += sizeof(tlvinfo_tlv_t) + eeprom_tlv->length;
+ }
+
+ printf("Checksum is %s.\n", is_checksum_valid(eeprom) ? "valid" :
"invalid");
+
+#ifdef DEBUG
+ printf("EEPROM dump: (0x%x bytes)", TLV_INFO_MAX_LEN);
+ for (i = 0; i < TLV_INFO_MAX_LEN; i++) {
+ if ((i % 16) == 0)
+ printf("\n%02X: ", i);
+ printf("%02X ", eeprom[i]);
+ }
+ printf("\n");
+#endif
+
+ return;
+}
+
+/**
+ * Struct for displaying the TLV codes and names.
+ */
+struct tlv_code_desc {
+ u8 m_code;
+ char* m_name;
+};
+
+/**
+ * List of TLV codes and names.
+ */
+static struct tlv_code_desc tlv_code_list[] = {
+ { TLV_CODE_PRODUCT_NAME , "Product Name"},
+ { TLV_CODE_PART_NUMBER , "Part Number"},
+ { TLV_CODE_SERIAL_NUMBER , "Serial Number"},
+ { TLV_CODE_MAC_BASE , "Base MAC Address"},
+ { TLV_CODE_MANUF_DATE , "Manufacture Date"},
+ { TLV_CODE_DEVICE_VERSION, "Device Version"},
+ { TLV_CODE_LABEL_REVISION, "Label Revision"},
+ { TLV_CODE_PLATFORM_NAME , "Platform Name"},
+ { TLV_CODE_ONIE_VERSION , "ONIE Version"},
+ { TLV_CODE_MAC_SIZE , "MAC Addresses"},
+ { TLV_CODE_MANUF_NAME , "Manufacturer"},
+ { TLV_CODE_MANUF_COUNTRY , "Country Code"},
+ { TLV_CODE_VENDOR_NAME , "Vendor Name"},
+ { TLV_CODE_DIAG_VERSION , "Diag Version"},
+ { TLV_CODE_SERVICE_TAG , "Service Tag"},
+ { TLV_CODE_VENDOR_EXT , "Vendor Extension"},
+ { TLV_CODE_CRC_32 , "CRC-32"},
+};
+
+/**
+ * Look up a TLV name by its type.
+ */
+static inline const char* tlv_type2name(u8 type)
+{
+ char* name = "Unknown";
+ int i;
+
+ for (i = 0; i < sizeof(tlv_code_list)/sizeof(tlv_code_list[0]); i++) {
+ if (tlv_code_list[i].m_code == type) {
+ name = tlv_code_list[i].m_name;
+ break;
+ }
+ }
+
+ return name;
+}
+
+/*
+ * decode_tlv
+ *
+ * Print a string representing the contents of the TLV field. The format of
+ * the string is:
+ * 1. The name of the field left justified in 20 characters
+ * 2. The type code in hex right justified in 5 characters
+ * 3. The length in decimal right justified in 4 characters
+ * 4. The value, left justified in however many characters it takes
+ * The validity of EEPROM contents and the TLV field have been verified
+ * prior to calling this function.
+ */
+#define DECODE_NAME_MAX 20
+
+/*
+ * The max decode value is currently for the 'raw' type or the 'vendor
+ * extension' type, both of which have the same decode format. The
+ * max decode string size is computed as follows:
+ *
+ * strlen(" 0xFF") * TLV_VALUE_MAX_LEN + 1
+ *
+ */
+#define DECODE_VALUE_MAX ((5 * TLV_VALUE_MAX_LEN) + 1)
+
+static void decode_tlv(tlvinfo_tlv_t * tlv)
+{
+ char name[DECODE_NAME_MAX];
+ char value[DECODE_VALUE_MAX];
+ int i;
+
+ strncpy(name, tlv_type2name(tlv->type), DECODE_NAME_MAX);
+
+ switch (tlv->type) {
+ case TLV_CODE_PRODUCT_NAME:
+ case TLV_CODE_PART_NUMBER:
+ case TLV_CODE_SERIAL_NUMBER:
+ case TLV_CODE_MANUF_DATE:
+ 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:
+ memcpy(value, tlv->value, tlv->length);
+ value[tlv->length] = 0;
+ break;
+ case TLV_CODE_MAC_BASE:
+ sprintf(value, "%02X:%02X:%02X:%02X:%02X:%02X",
+ tlv->value[0], tlv->value[1], tlv->value[2],
+ tlv->value[3], tlv->value[4], tlv->value[5]);
+ break;
+ case TLV_CODE_DEVICE_VERSION:
+ sprintf(value, "%u", tlv->value[0]);
+ break;
+ case TLV_CODE_MAC_SIZE:
+ sprintf(value, "%u", (tlv->value[0] << 8) | tlv->value[1]);
+ break;
+ case TLV_CODE_VENDOR_EXT:
+ value[0] = 0;
+ for (i = 0; (i < (DECODE_VALUE_MAX/5)) && (i < tlv->length);
i++) {
+ sprintf(value, "%s 0x%02X", value, tlv->value[i]);
+ }
+ break;
+ case TLV_CODE_CRC_32:
+ sprintf(value, "0x%02X%02X%02X%02X",
+ tlv->value[0], tlv->value[1], tlv->value[2],
tlv->value[3]);
+ break;
+ default:
+ value[0] = 0;
+ for (i = 0; (i < (DECODE_VALUE_MAX/5)) && (i < tlv->length);
i++) {
+ sprintf(value, "%s 0x%02X", value, tlv->value[i]);
+ }
+ break;
+ }
+
+ name[DECODE_NAME_MAX-1] = 0;
+ printf("%-20s 0x%02X %3d %s\n", name, tlv->type, tlv->length, value);
+ return;
+}
+
+/**
+ * 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.
+ */
+static void update_crc(u8 *eeprom)
+{
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+ tlvinfo_tlv_t * eeprom_crc;
+ unsigned int calc_crc;
+ int eeprom_index;
+
+ // Discover the CRC TLV
+ if (!tlvinfo_find_tlv(eeprom, TLV_CODE_CRC_32, &eeprom_index)) {
+ if ((be16_to_cpu(eeprom_hdr->totallen) + sizeof(tlvinfo_tlv_t) +
4) > TLV_TOTAL_LEN_MAX) {
+ return;
+ }
+ eeprom_index = sizeof(tlvinfo_header_t) +
be16_to_cpu(eeprom_hdr->totallen);
+ eeprom_hdr->totallen =
cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) +
+ sizeof(tlvinfo_tlv_t) + 4);
+ }
+ eeprom_crc = (tlvinfo_tlv_t *) &eeprom[eeprom_index];
+ eeprom_crc->type = TLV_CODE_CRC_32;
+ eeprom_crc->length = 4;
+
+ // Calculate the checksum
+ calc_crc = crc32(0, (void *)eeprom,
+ sizeof(tlvinfo_header_t) +
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;
+
+ return;
+}
+
+/**
+ * prog_eeprom
+ *
+ * Write the EEPROM data from CPU memory to the hardware.
+ */
+static int prog_eeprom(u8 * eeprom)
+{
+ int ret = 0;
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+ int eeprom_len;
+
+ update_crc(eeprom);
+
+ eeprom_len = sizeof(tlvinfo_header_t) +
be16_to_cpu(eeprom_hdr->totallen);
+ ret = write_sys_eeprom(eeprom, eeprom_len);
+ 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)
+{
+ int i;
+
+ printf("TLV Code TLV Name\n");
+ printf("======== =================\n");
+ for (i = 0; i < sizeof(tlv_code_list)/sizeof(tlv_code_list[0]); i++) {
+ printf("0x%02X %s\n",
+ tlv_code_list[i].m_code,
+ tlv_code_list[i].m_name);
+ }
+}
+
+/**
+ * do_sys_eeprom
+ *
+ * This function implements the sys_eeprom command.
+ */
+int do_sys_eeprom(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ char cmd;
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+
+ // If no arguments, read the EERPOM and display its contents
+ if (argc == 1) {
+ read_eeprom(eeprom);
+ show_eeprom(eeprom);
+ return 0;
+ }
+
+ // We only look at the first character to the command, so "read" and
"reset"
+ // will both be treated as "read".
+ cmd = argv[1][0];
+
+ // Read the EEPROM contents
+ if (cmd == 'r') {
+ has_been_read = 0;
+ if (!read_eeprom(eeprom))
+ printf("EEPROM data loaded from device to memory.\n");
+ return 0;
+ }
+
+ // Subsequent commands require that the EEPROM has already been read.
+ if (!has_been_read) {
+ printf("Please read the EEPROM data first, using the 'sys_eeprom
read' command.\n");
+ return 0;
+ }
+
+ // Handle the commands that don't take parameters
+ if (argc == 2) {
+ switch (cmd) {
+ case 'w': /* write */
+ prog_eeprom(eeprom);
+ break;
+ case 'e': /* erase */
+ strcpy(eeprom_hdr->signature, TLV_INFO_ID_STRING);
+ eeprom_hdr->version = TLV_INFO_VERSION;
+ eeprom_hdr->totallen = cpu_to_be16(0);
+ update_crc(eeprom);
+ printf("EEPROM data in memory reset.\n");
+ break;
+ case 'l': /* list */
+ show_tlv_code_list();
+ break;
+ case 'd': /* dev */
+ show_tlv_devices();
+ break;
+ default:
+ cmd_usage(cmdtp);
+ break;
+ }
+ return 0;
+ }
+
+ // The set command takes one or two args.
+ if (argc > 4) {
+ cmd_usage(cmdtp);
+ return 0;
+ }
+
+ // Set command. If the TLV exists in the EEPROM, delete it. Then if
data was
+ // supplied for this TLV add the TLV with the new contents at the end.
+ if (cmd == 's') {
+ int tcode;
+ tcode = simple_strtoul(argv[2], NULL, 0);
+ tlvinfo_delete_tlv(eeprom, tcode);
+ if (argc == 4) {
+ tlvinfo_add_tlv(eeprom, tcode, argv[3]);
+ }
+ } else if (cmd == 'd') { /* 'dev' command */
+ unsigned devnum;
+ devnum = simple_strtoul(argv[2], NULL, 0);
+ if (devnum > MAX_TLV_DEVICES || tlv_devices[devnum] == NULL) {
+ printf("Invalid device number\n");
+ return 0;
+ }
+ current_dev = devnum;
+ has_been_read = 0;
+ } else {
+ cmd_usage(cmdtp);
+ }
+
+ return 0;
+}
+
+/**
+ * This macro defines the sys_eeprom command line command.
+ */
+U_BOOT_CMD(
+ sys_eeprom, 4, 1, do_sys_eeprom,
+ "Display and program the system EEPROM data block.",
+ "[read|write|set <type_code> <string_value>|erase|list]\n"
+ "sys_eeprom\n"
+ " - With no arguments display the current contents.\n"
+ "sys_eeprom dev [dev]\n"
+ " - List devices or set current EEPROM device.\n"
+ "sys_eeprom read\n"
+ " - Load EEPROM data from device to memory.\n"
+ "sys_eeprom write\n"
+ " - Write the EEPROM data to persistent storage.\n"
+ "sys_eeprom set <type_code> <string_value>\n"
+ " - Set a field to a value.\n"
+ " - If no string_value, field is deleted.\n"
+ " - Use 'sys_eeprom write' to make changes permanent.\n"
+ "sys_eeprom erase\n"
+ " - Reset the in memory EEPROM data.\n"
+ " - Use 'sys_eeprom read' to refresh the in memory EEPROM data.\n"
+ " - Use 'sys_eeprom write' to make changes permanent.\n"
+ "sys_eeprom list\n"
+ " - 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.
+ */
+static bool tlvinfo_find_tlv(u8 * eeprom, u8 tcode, int * eeprom_index)
+{
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+ tlvinfo_tlv_t * eeprom_tlv;
+ int eeprom_end;
+
+ // Search through the TLVs, looking for the first one which matches the
+ // supplied type code.
+ *eeprom_index = sizeof(tlvinfo_header_t);
+ eeprom_end = sizeof(tlvinfo_header_t) +
be16_to_cpu(eeprom_hdr->totallen);
+ while (*eeprom_index < eeprom_end) {
+ eeprom_tlv = (tlvinfo_tlv_t *) &eeprom[*eeprom_index];
+ if (!is_valid_tlv(eeprom_tlv)) {
+ return(FALSE);
+ }
+ if (eeprom_tlv->type == tcode) {
+ return(TRUE);
+ }
+ *eeprom_index += sizeof(tlvinfo_tlv_t) + eeprom_tlv->length;
+ }
+ return(FALSE);
+}
+
+/**
+ * tlvinfo_delete_tlv
+ *
+ * This function deletes the TLV with the specified type code from the
+ * EEPROM.
+ */
+static bool tlvinfo_delete_tlv(u8 * eeprom, u8 code)
+{
+ int eeprom_index;
+ int tlength;
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+ tlvinfo_tlv_t * eeprom_tlv;
+
+ // Find the TLV and then move all following TLVs "forward"
+ if (tlvinfo_find_tlv(eeprom, code, &eeprom_index)) {
+ eeprom_tlv = (tlvinfo_tlv_t *) &eeprom[eeprom_index];
+ tlength = sizeof(tlvinfo_tlv_t) + eeprom_tlv->length;
+ memcpy(&eeprom[eeprom_index], &eeprom[eeprom_index+tlength],
+ sizeof(tlvinfo_header_t) +
be16_to_cpu(eeprom_hdr->totallen) - eeprom_index - tlength);
+ eeprom_hdr->totallen =
cpu_to_be16(be16_to_cpu(eeprom_hdr->totallen) - tlength);
+ 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
+static bool tlvinfo_add_tlv(u8 * eeprom, int tcode, char * strval)
+{
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+ tlvinfo_tlv_t * 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) + sizeof(tlvinfo_tlv_t) +
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) - sizeof(tlvinfo_tlv_t) - 4);
+ }
+ else {
+ eeprom_index = sizeof(tlvinfo_header_t) +
be16_to_cpu(eeprom_hdr->totallen);
+ }
+ eeprom_tlv = (tlvinfo_tlv_t *) &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) +
sizeof(tlvinfo_tlv_t) + new_tlv_len);
+ 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 -- %d\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 ? simple_strtoul(p, &end, 16) : 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 -- %d\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(void)
+{
+ unsigned dev;
+
+ for (dev = 0; dev < MAX_TLV_DEVICES; dev++)
+ if (tlv_devices[dev])
+ printf("TLV: %u%s\n", dev,
+ (dev == current_dev) ? " (*)" : "");
+}
+
+static int find_tlv_devices(void)
+{
+ 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[count_dev++] = dev;
+ }
+
+ return (count_dev == 0) ? -ENODEV : 0;
+}
+
+/**
+ * read_sys_eeprom - read the hwinfo from i2c EEPROM
+ */
+int read_sys_eeprom(void *eeprom, int offset, int len)
+{
+ int ret;
+
+ if (tlv_devices[current_dev] == NULL) {
+ ret = find_tlv_devices();
+ if (ret)
+ return ret;
+ }
+
+ if (tlv_devices[current_dev] == NULL)
+ return -ENODEV;
+
+ return i2c_eeprom_read(tlv_devices[current_dev], offset, eeprom, len);
+}
+
+/**
+ * write_sys_eeprom - write the hwinfo to i2c EEPROM
+ */
+int write_sys_eeprom(void *eeprom, int len)
+{
+ if (tlv_devices[current_dev] == NULL)
+ return -ENODEV;
+
+ return i2c_eeprom_write(tlv_devices[current_dev], 0, eeprom, len);
+}
+
+int read_tlvinfo_sys_eeprom(void *eeprom, tlvinfo_header_t **hdr,
+ tlvinfo_tlv_t **first_entry)
+{
+ int ret;
+ tlvinfo_header_t *tlv_hdr;
+ tlvinfo_tlv_t *tlv_ent;
+
+ /* Read TLV header */
+ ret = read_sys_eeprom(eeprom, 0, sizeof(tlvinfo_header_t));
+ if (ret < 0)
+ return ret;
+
+ tlv_hdr = eeprom;
+ if (!is_valid_tlvinfo_header(tlv_hdr))
+ return -EINVAL;
+
+ /* Read TLV entries */
+ tlv_ent = (tlvinfo_tlv_t *) &tlv_hdr[1];
+ ret = read_sys_eeprom(tlv_ent, sizeof(tlvinfo_header_t),
+ be16_to_cpu(tlv_hdr->totallen));
+ if (ret < 0)
+ return ret;
+ if (!is_checksum_valid(eeprom))
+ return -EINVAL;
+
+ *hdr = tlv_hdr;
+ *first_entry = tlv_ent;
+
+ return 0;
+}
+
+int read_tlvinfo_sys_eeprom_dev(void *eeprom, tlvinfo_header_t **hdr,
+ tlvinfo_tlv_t **first_entry, unsigned devnum)
+{
+ int ret;
+ unsigned prev_dev = MAX_TLV_DEVICES + 1;
+
+ if (devnum > MAX_TLV_DEVICES)
+ return -EINVAL;
+
+ if (devnum != current_dev) {
+ prev_dev = current_dev;
+ current_dev = devnum;
+ has_been_read = 0;
+ }
+
+ ret = read_tlvinfo_sys_eeprom(eeprom, hdr, first_entry);
+ if (ret < 0 && prev_dev <= MAX_TLV_DEVICES)
+ current_dev = prev_dev;
+
+ return ret;
+}
+
+/**
+ * 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;
+ tlvinfo_tlv_t * eeprom_tlv;
+ int maccount;
+ u8 macbase[6];
+ tlvinfo_header_t * eeprom_hdr = (tlvinfo_header_t *) eeprom;
+
+ puts("EEPROM: ");
+
+ if (read_eeprom(eeprom)) {
+ printf("Read failed.\n");
+ return -1;
+ }
+
+ maccount = 1;
+ if (tlvinfo_find_tlv(eeprom, TLV_CODE_MAC_SIZE, &eeprom_index)) {
+ eeprom_tlv = (tlvinfo_tlv_t *) &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 = (tlvinfo_tlv_t *) &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(void)
+{
+ char serialstr[257];
+ int eeprom_index;
+ tlvinfo_tlv_t * eeprom_tlv;
+
+ if (env_get("serial#"))
+ return 0;
+
+ if (read_eeprom(eeprom)) {
+ printf("Read failed.\n");
+ return -1;
+ }
+
+ if (tlvinfo_find_tlv(eeprom, TLV_CODE_SERIAL_NUMBER, &eeprom_index)) {
+ eeprom_tlv = (tlvinfo_tlv_t *) &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/include/sys_eeprom.h b/include/sys_eeprom.h
new file mode 100644
index 000000000000..49a7ee87d9db
--- /dev/null
+++ b/include/sys_eeprom.h
@@ -0,0 +1,169 @@
+/*
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __SYS_EEPROM_H_
+#define __SYS_EEPROM_H_
+
+/*
+ * Without getting too philosophical, define truth, falsehood, and the
+ * boolean type, if they are not already defined.
+ */
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#ifndef bool
+typedef unsigned char bool;
+#endif
+
+#ifndef is_digit
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
+#endif
+
+/*
+ * The Definition of the TlvInfo EEPROM format can be found at onie.org or
+ * github.com/onie
+ */
+
+/*
+ * TlvInfo header: Layout of the header for the TlvInfo format
+ *
+ * See the end of this file for details of this eeprom format
+ */
+struct __attribute__ ((__packed__)) tlvinfo_header_s {
+ char signature[8]; /* 0x00 - 0x07 EEPROM Tag "TlvInfo" */
+ u8 version; /* 0x08 Structure version */
+ u16 totallen; /* 0x09 - 0x0A Length of all data which
follows */
+};
+typedef struct tlvinfo_header_s tlvinfo_header_t;
+
+// Header Field Constants
+#define TLV_INFO_ID_STRING "TlvInfo"
+#define TLV_INFO_VERSION 0x01
+#define TLV_INFO_MAX_LEN 2048
+#define TLV_TOTAL_LEN_MAX (TLV_INFO_MAX_LEN - sizeof(tlvinfo_header_t))
+
+/*
+ * TlvInfo TLV: Layout of a TLV field
+ */
+struct __attribute__ ((__packed__)) tlvinfo_tlv_s {
+ u8 type;
+ u8 length;
+ u8 value[0];
+};
+typedef struct tlvinfo_tlv_s tlvinfo_tlv_t;
+
+/* Maximum length of a TLV value in bytes */
+#define TLV_VALUE_MAX_LEN 255
+
+/**
+ * The TLV Types.
+ *
+ * Keep these in sync with tlv_code_list in cmd_sys_eeprom.c
+ */
+#define TLV_CODE_PRODUCT_NAME 0x21
+#define TLV_CODE_PART_NUMBER 0x22
+#define TLV_CODE_SERIAL_NUMBER 0x23
+#define TLV_CODE_MAC_BASE 0x24
+#define TLV_CODE_MANUF_DATE 0x25
+#define TLV_CODE_DEVICE_VERSION 0x26
+#define TLV_CODE_LABEL_REVISION 0x27
+#define TLV_CODE_PLATFORM_NAME 0x28
+#define TLV_CODE_ONIE_VERSION 0x29
+#define TLV_CODE_MAC_SIZE 0x2A
+#define TLV_CODE_MANUF_NAME 0x2B
+#define TLV_CODE_MANUF_COUNTRY 0x2C
+#define TLV_CODE_VENDOR_NAME 0x2D
+#define TLV_CODE_DIAG_VERSION 0x2E
+#define TLV_CODE_SERVICE_TAG 0x2F
+#define TLV_CODE_VENDOR_EXT 0xFD
+#define TLV_CODE_CRC_32 0xFE
+
+/**
+ * read_sys_eeprom - Read the EEPROM binary data from the hardware
+ * @eeprom: Pointer to buffer to hold the binary data
+ * @offset: Offset within EEPROM block to read data from
+ * @len : Maximum size of buffer
+ *
+ * Note: this routine does not validate the EEPROM data.
+ *
+ */
+
+extern int read_sys_eeprom(void *eeprom, int offset, int len);
+
+/**
+ * write_sys_eeprom - Write the entire EEPROM binary data to the hardware
+ * @eeprom: Pointer to buffer to hold the binary data
+ * @len : Maximum size of buffer
+ *
+ * Note: this routine does not validate the EEPROM data.
+ *
+ */
+extern int write_sys_eeprom(void *eeprom, int len);
+
+/**
+ * read_tlvinfo_sys_eeprom - Read the TLV from EEPROM, and validate
+ * @eeprom: Pointer to buffer to hold the binary data. Must point to a buffer
+ * of size at least TLV_INFO_MAX_LEN.
+ * @hdr : Points to pointer to TLV header (output)
+ * @first_entry : Points to pointer to first TLV entry (output)
+ *
+ * Store the raw EEPROM data in the @eeprom buffer. If TLV is valid set *@hdr
+ * and *@first_entry.
+ *
+ * Returns 0 when read from EEPROM is successful, and the data is valid.
+ * Returns <0 error value when EEPROM read fails. Return -EINVAL when TLV is
+ * invalid.
+ *
+ */
+
+extern int read_tlvinfo_sys_eeprom(void *eeprom, tlvinfo_header_t **hdr,
+ tlvinfo_tlv_t **first_entry);
+
+/**
+ * read_tlvinfo_sys_eeprom_dev - Read the TLV from specific EEPROM, and
validate
+ * @eeprom: Pointer to buffer to hold the binary data. Must point to a buffer
+ * of size at least TLV_INFO_MAX_LEN.
+ * @hdr : Points to pointer to TLV header (output)
+ * @first_entry : Points to pointer to first TLV entry (output)
+ * @dev : EEPROM device to read
+ *
+ * Store the raw EEPROM data from EEPROM @dev in the @eeprom buffer. If TLV is
+ * valid set *@hdr and *@first_entry.
+ *
+ * Returns 0 when read from EEPROM is successful, and the data is valid.
+ * Returns <0 error value when EEPROM read fails. Return -EINVAL when TLV is
+ * invalid.
+ *
+ */
+
+extern int read_tlvinfo_sys_eeprom_dev(void *eeprom, tlvinfo_header_t **hdr,
+ tlvinfo_tlv_t **first_entry, unsigned devnum);
+
+/**
+ * is_valid_tlvinfo_header
+ *
+ * Perform sanity checks on the first 11 bytes of the TlvInfo EEPROM
+ * data pointed to by the parameter:
+ * 1. First 8 bytes contain null-terminated ASCII string "TlvInfo"
+ * 2. Version byte is 1
+ * 3. Total length bytes contain value which is less than or equal
+ * to the allowed maximum (2048-11)
+ *
+ */
+static inline bool is_valid_tlvinfo_header(tlvinfo_header_t *hdr)
+{
+ return( (strcmp(hdr->signature, TLV_INFO_ID_STRING) == 0) &&
+ (hdr->version == TLV_INFO_VERSION) &&
+ (be16_to_cpu(hdr->totallen) <= TLV_TOTAL_LEN_MAX) );
+}
+
+#endif /* __SYS_EEPROM_H_ */