diff options
author | Michael Auchter <michael.auchter@ni.com> | 2019-09-09 16:46:14 -0500 |
---|---|---|
committer | Martin Braun <martin@gnuradio.org> | 2021-05-31 05:28:47 -0700 |
commit | 530cb0a8aaaac906fe124086e780d6651241b0ad (patch) | |
tree | 8a5301163392b3d0c1642da0cb9ba2a9ea176401 /mpm/tools/tlv_eeprom | |
parent | fa997d52cc62c3c39f5b7560890e31d41e4eb9e9 (diff) | |
download | uhd-530cb0a8aaaac906fe124086e780d6651241b0ad.tar.gz uhd-530cb0a8aaaac906fe124086e780d6651241b0ad.tar.bz2 uhd-530cb0a8aaaac906fe124086e780d6651241b0ad.zip |
mpm: add tlv_eeprom
Add support for parsing an eeprom that uses tag-length-value to store
contents.
Co-authored-by: Michael Auchter <michael.auchter@ni.com>
Co-authored-by: Virendra Kakade <virendra.kakade@ni.com>
Co-authored-by: Cristina Fuentes <cristina.fuentes@ni.com>
Diffstat (limited to 'mpm/tools/tlv_eeprom')
-rw-r--r-- | mpm/tools/tlv_eeprom/CMakeLists.txt | 40 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/crc.c | 71 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/eeprom-dump.c | 34 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/eeprom-id.c | 66 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/eeprom-init.c | 376 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/eeprom-path | 36 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/eeprom-pids.c | 50 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/eeprom-pids.h | 21 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/eeprom-wrapper | 13 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/tlv_eeprom.c | 98 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/tlv_eeprom.h | 104 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/tlv_eeprom_io.c | 88 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/tlv_eeprom_io.h | 31 | ||||
-rw-r--r-- | mpm/tools/tlv_eeprom/usrp_eeprom.h | 206 |
14 files changed, 1234 insertions, 0 deletions
diff --git a/mpm/tools/tlv_eeprom/CMakeLists.txt b/mpm/tools/tlv_eeprom/CMakeLists.txt new file mode 100644 index 000000000..75cf3c9cc --- /dev/null +++ b/mpm/tools/tlv_eeprom/CMakeLists.txt @@ -0,0 +1,40 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +set(eeprom_tool_sources) +set(eeprom_tool_libs) + +if(ENABLE_LIBMPM AND ENABLE_X400) + message(STATUS "Adding MPM EEPROM tools...") + set(eeprom_tool_libs tlv_eeprom.c tlv_eeprom_io.c crc.c eeprom-pids.c) + list(APPEND eeprom_tool_sources + eeprom-dump.c + eeprom-id.c + eeprom-init.c + ) + + add_executable(eeprom-update-core eeprom-init.c ${eeprom_tool_libs}) + target_compile_definitions(eeprom-update-core PRIVATE -DTLV_EEPROM_UPDATE) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/eeprom-update-core DESTINATION ${RUNTIME_DIR}) + add_custom_target(eeprom-update ALL COMMAND ${CMAKE_COMMAND} -E create_symlink eeprom-wrapper eeprom-update) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/eeprom-update DESTINATION ${RUNTIME_DIR}) +endif(ENABLE_LIBMPM AND ENABLE_X400) + +foreach(eeprom_tool_source ${eeprom_tool_sources}) + get_filename_component(eeprom_tool ${eeprom_tool_source} NAME_WE) + # install eeprom-id, eeprom-dump, etc. with "-core" appendix + add_executable(${eeprom_tool}-core ${eeprom_tool_source} ${eeprom_tool_libs}) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${eeprom_tool}-core DESTINATION ${RUNTIME_DIR}) + # install eeprom-id, eeprom-dump, etc. wrappers + add_custom_target(${eeprom_tool} ALL COMMAND ${CMAKE_COMMAND} -E create_symlink eeprom-wrapper ${eeprom_tool}) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${eeprom_tool} DESTINATION ${RUNTIME_DIR}) +endforeach(eeprom_tool_source ${eeprom_tool_sources}) + +install(PROGRAMS + eeprom-path + eeprom-wrapper + DESTINATION ${RUNTIME_DIR} +) diff --git a/mpm/tools/tlv_eeprom/crc.c b/mpm/tools/tlv_eeprom/crc.c new file mode 100644 index 000000000..df16b71db --- /dev/null +++ b/mpm/tools/tlv_eeprom/crc.c @@ -0,0 +1,71 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <stdint.h> +#include "tlv_eeprom.h" + +static const uint32_t crc32_tab[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +static uint32_t crc32(uint32_t crc, const void *buf, size_t size) +{ + const uint8_t *p; + + p = buf; + + while (size--) + crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + + return crc; +} + +uint32_t tlv_eeprom_crc(const struct tlv_eeprom *e) +{ + return crc32(0, &e->size, sizeof(e->size) + e->size); +} diff --git a/mpm/tools/tlv_eeprom/eeprom-dump.c b/mpm/tools/tlv_eeprom/eeprom-dump.c new file mode 100644 index 000000000..9afcef431 --- /dev/null +++ b/mpm/tools/tlv_eeprom/eeprom-dump.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Ettus Research, a National Instruments Brand + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "tlv_eeprom.h" +#include "tlv_eeprom_io.h" +#include "usrp_eeprom.h" + +int main(int argc, char **argv) +{ + struct tlv_eeprom *eeprom; + + if (argc != 2) { + fprintf(stderr, "usage: %s <eeprom>\n", argv[0]); + return 1; + } + + eeprom = tlv_eeprom_read_from_file(argv[1]); + if (!eeprom) + return 1; + + if (!tlv_eeprom_validate(eeprom, USRP_EEPROM_MAGIC)) + tlv_for_each(eeprom->tlv, eeprom->size, usrp_eeprom_trace); + else + fprintf(stderr, "eeprom contents invalid!\n"); + + free(eeprom); + + return 0; +} diff --git a/mpm/tools/tlv_eeprom/eeprom-id.c b/mpm/tools/tlv_eeprom/eeprom-id.c new file mode 100644 index 000000000..e00732f0a --- /dev/null +++ b/mpm/tools/tlv_eeprom/eeprom-id.c @@ -0,0 +1,66 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <stdio.h> +#include <stdlib.h> + +#include "tlv_eeprom.h" +#include "tlv_eeprom_io.h" +#include "usrp_eeprom.h" +#include "eeprom-pids.h" + +int main(int argc, char **argv) +{ + struct tlv_eeprom *eeprom; + const struct usrp_eeprom_module_info *module_info; + const struct usrp_eeprom_board_info *board_info; + const struct pid_info *pid_info; + unsigned int pid, rev; + const char *serial; + + if (argc != 2) { + fprintf(stderr, "usage: %s <eeprom>\n", argv[0]); + return 1; + } + + eeprom = tlv_eeprom_read_from_file(argv[1]); + if (tlv_eeprom_validate(eeprom, USRP_EEPROM_MAGIC)) { + fprintf(stderr, "failed to validate eeprom\n"); + return 1; + } + + module_info = tlv_lookup(eeprom->tlv, eeprom->size, + USRP_EEPROM_MODULE_INFO_TAG); + board_info = tlv_lookup(eeprom->tlv, eeprom->size, + USRP_EEPROM_BOARD_INFO_TAG); + + if (module_info) { + serial = module_info->serial; + pid = module_info->pid; + rev = module_info->rev; + } else if (board_info) { + serial = board_info->serial; + pid = board_info->pid; + rev = board_info->rev; + } else { + fprintf(stderr, "couldn't find module_info or board_info\n"); + return 1; + } + + pid_info = get_info_from_pid(pid); + + if (pid_info && pid_info->name) { + printf("product=ni-%s-rev%u\n", pid_info->name, rev + pid_info->rev_offset); + } else if (pid_info && pid_info->description) { + printf("product=ni-(%s)-rev%u\n", pid_info->description, rev + pid_info->rev_offset); + } else { + printf("product=unknown-(%04x)\n", pid); + } + printf("serial=%s\n", serial); + + free(eeprom); + return 0; +} diff --git a/mpm/tools/tlv_eeprom/eeprom-init.c b/mpm/tools/tlv_eeprom/eeprom-init.c new file mode 100644 index 000000000..6cca434fc --- /dev/null +++ b/mpm/tools/tlv_eeprom/eeprom-init.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Ettus Research, a National Instruments Brand + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tlv_eeprom.h" +#include "tlv_eeprom_io.h" +#include "usrp_eeprom.h" + +#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((*x))) + +int parse_board_info(int argc, char **argv, void *data) +{ + struct usrp_eeprom_board_info *info = data; + + if (!argc) { + sprintf(data, "<pid> <rev> <compat_rev> <serial>"); + return 0; + } + + assert(argc >= 4); + + info->pid = strtoul(argv[0], NULL, 0); + info->rev = strtoul(argv[1], NULL, 0); + info->compat_rev = strtoul(argv[2], NULL, 0); + strncpy(info->serial, argv[3], 8); + info->serial[7] = '\0'; + + return 4; +} +static struct usrp_eeprom_board_info board_info; + +int parse_module_info(int argc, char **argv, void *data) +{ + struct usrp_eeprom_module_info *info = data; + + if (!argc) { + sprintf(data, "<pid> <rev> <serial>"); + return 0; + } + + assert(argc >= 3); + + info->pid = strtoul(argv[0], NULL, 0); + info->rev = strtoul(argv[1], NULL, 0); + strncpy(info->serial, argv[2], 8); + info->serial[7] = '\0'; + + return 3; +} +static struct usrp_eeprom_module_info module_info; + +int parse_mac(int argc, char **argv, void *data) +{ + struct usrp_eeprom_mac_addr *mac = data; + int ret; + + if (!argc) { + sprintf(data, "<mac_addr>"); + return 0; + } + + assert(argc >= 1); + + ret = sscanf(*argv, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + mac->addr + 0, mac->addr + 1, mac->addr + 2, + mac->addr + 3, mac->addr + 4, mac->addr + 5); + assert(ret == 6); + + return 1; +} +static struct usrp_eeprom_mac_addr mac_addrs[USRP_EEPROM_MAX_MAC_ADDRS]; + +int parse_db_pwr_seq(int argc, char **argv, void *data) +{ + struct usrp_eeprom_db_pwr_seq *seq = data; + uint8_t i; + + if (!argc) { + sprintf(data, "<nsteps> [delay supply_mask]..."); + return 0; + } + + assert(argc >= 1); + seq->nsteps = strtoul(argv[0], NULL, 0); + assert(argc >= 1 + (2 * seq->nsteps)); + + argv++; + for (i = 0; i < seq->nsteps; i++) { + seq->steps[i].delay = strtoul(*argv++, NULL, 0); + seq->steps[i].supply_mask = strtoul(*argv++, NULL, 0); + } + + return 1 + 2 * i; +} +static struct usrp_eeprom_db_pwr_seq db_pwr_seq; + +int parse_mcu_flags(int argc, char **argv, void *data) +{ + struct usrp_eeprom_mcu_flags *flags = data; + unsigned long tmp; + int i; + + if (!argc) { + sprintf(data, "<flags[0]> <flags[1]> <flags[2]> " + "<flags[3]> <flags[4]> <flags[5]>"); + return 0; + } + + assert(argc >= ARRAY_SIZE(flags->flags)); + for (i = 0; i < ARRAY_SIZE(flags->flags); i++) { + tmp = strtoul(argv[i], NULL, 0); + assert(tmp <= 0xFF); + + flags->flags[i] = tmp; + } + + return i; +} +static struct usrp_eeprom_mcu_flags mcu_flags; + +int parse_fan_limits(int argc, char **argv, void *data) +{ + struct usrp_eeprom_fan_limits *fan_limits = data; + int i; + + if (!argc) { + sprintf(data, "<fan min rpm> <fan start rpm> <fan max rpm>"); + return 0; + } + + assert(argc >= 3); + fan_limits->min = strtoul(argv[0], NULL, 0); + fan_limits->start = strtoul(argv[1], NULL, 0); + fan_limits->max = strtoul(argv[2], NULL, 0); + + return 3; +} +static struct usrp_eeprom_fan_limits fan_limits; + +int parse_fan_fixed_capacity(int argc, char **argv, void *data) +{ + struct usrp_eeprom_fan_fixed_capacity *fan_fixed_capacity = data; + + if (!argc) { + sprintf(data, "<fan fixed capacity>"); + return 0; + } + + fan_fixed_capacity->capacity = strtoul(argv[0], NULL, 0); + + return 1; +} +static struct usrp_eeprom_fan_fixed_capacity fan_fixed_capacity; + +int parse_clkaux_tuning_word(int argc, char **argv, void *data) +{ + struct usrp_eeprom_clkaux_tuning_word *clkaux_tuning_word = data; + + if (!argc) { + sprintf(data, "<dac tuning word>"); + return 0; + } + + clkaux_tuning_word->tuning_word = strtoul(argv[0], NULL, 0); + + return 1; +} +static struct usrp_eeprom_clkaux_tuning_word clkaux_tuning_word; + +struct arg_parser { + const char *arg; + const char *alias; + void *store; + size_t size; + uint8_t tag; + int (*parse)(int, char **, void *); + int dirty; +}; + +#define PARSER_(name_, alias_, data_, tag_, parser_) { \ + .arg = #name_, \ + .alias = alias_, \ + .store = &(data_), \ + .size = sizeof((data_)), \ + .tag = (tag_), \ + .parse = (parser_), \ +} + +#define PARSER(param_, tag_) PARSER_(param_, NULL, param_, tag_, parse_ ##param_) +#define MAC_PARSER_ALIAS(idx_, alias_) PARSER_( mac_addr_ ## idx_, (alias_), mac_addrs[(idx_)], USRP_EEPROM_MAC_ADDR_TAG((idx_)), parse_mac) +#define MAC_PARSER(idx_) MAC_PARSER_ALIAS(idx_, NULL) + +static struct arg_parser parsers[] = { + PARSER(board_info, USRP_EEPROM_BOARD_INFO_TAG), + PARSER(module_info, USRP_EEPROM_MODULE_INFO_TAG), + MAC_PARSER_ALIAS(0, "eth0_mac"), + MAC_PARSER_ALIAS(1, "qsfp0_mac"), + MAC_PARSER_ALIAS(2, "qsfp1_mac"), + MAC_PARSER(3), + MAC_PARSER(4), + MAC_PARSER(5), + MAC_PARSER(6), + MAC_PARSER(7), + MAC_PARSER(8), + MAC_PARSER(9), + MAC_PARSER(10), + MAC_PARSER(11), + MAC_PARSER(12), + PARSER(db_pwr_seq, USRP_EEPROM_DB_PWR_SEQ_TAG), + PARSER(mcu_flags, USRP_EEPROM_MCU_FLAGS), + PARSER(fan_limits, USRP_EEPROM_FAN_LIMITS), + PARSER(fan_fixed_capacity, USRP_EEPROM_FAN_FIXED_CAPACITY), + PARSER(clkaux_tuning_word, USRP_EEPROM_CLKAUX_TUNING_WORD), +}; + +static struct arg_parser *parser_lookup(const char *arg) +{ + for (size_t i = 0; i < ARRAY_SIZE(parsers); i++) { + if (!strcmp(parsers[i].arg, arg)) + return parsers + i; + if (parsers[i].alias && !strcmp(parsers[i].alias, arg)) + return parsers + i; + } + + return NULL; +} + +#ifdef TLV_EEPROM_UPDATE +static void tlv_update(uint8_t tag, uint8_t len, const void *val) +{ + struct arg_parser *parser = NULL; + + for (size_t i = 0; i < ARRAY_SIZE(parsers); i++) { + if (parsers[i].tag == tag) + parser = parsers + i; + } + + if (!parser) { + fprintf(stderr, "found unknown tag %02x len %u", tag, len); + fprintf(stderr, "cannot use this utility to update\n"); + exit(EXIT_FAILURE); + } + + assert(len == parser->size); + memcpy(parser->store, val, len); + parser->dirty = 1; +} + +static void handle_update(const char *filename) +{ + struct tlv_eeprom *eeprom; + + eeprom = tlv_eeprom_read_from_file(filename); + if (!eeprom) { + perror("failed to read"); + exit(EXIT_FAILURE); + } + + if (tlv_eeprom_validate(eeprom, USRP_EEPROM_MAGIC)) { + fprintf(stderr, "contents invalid, cannot update\n"); + exit(EXIT_FAILURE); + } + + tlv_for_each(eeprom->tlv, eeprom->size, tlv_update); + free(eeprom); +} +#else +static void handle_update(const char *filename) +{ +} +#endif + +static void validate_file_matches(const struct tlv_eeprom *eeprom, + const char *filename) +{ + struct tlv_eeprom *file_eeprom; + + file_eeprom = tlv_eeprom_read_from_file(filename); + if (!file_eeprom) { + perror("failed to read for validate"); + exit(EXIT_FAILURE); + } + + if (memcmp(file_eeprom, eeprom, sizeof(*eeprom))) { + fprintf(stderr, "eeprom validation failed! " + "values read do not match what was written. " + "perhaps the eeprom was write-protected?\n"); + exit(EXIT_FAILURE); + } + + free(file_eeprom); +} + +static void usage(const char *argv0) +{ + const struct arg_parser *p; + char buffer[4096]; + + fprintf(stderr, "usage: %s <output_file>\n", argv0); + for (size_t i = 0; i < ARRAY_SIZE(parsers); i++) { + p = parsers + i; + p->parse(0, NULL, buffer); + fprintf(stderr, "\t--%s %s\n", p->arg, buffer); + } +} + +int main(int argc, char **argv) +{ + struct arg_parser *parser; + const char *output_file; + struct tlv_eeprom eeprom; + uint8_t *ptr; + int ret; + + if (argc < 2) { + usage(argv[0]); + return 1; + } + + output_file = argv[1]; + handle_update(output_file); + + argc -= 2; + argv += 2; + + while (argc > 0) { + if (strncmp("--", *argv, 2)) { + fprintf(stderr, "unexpected arg\n"); + return 1; + } + + *argv += 2; + parser = parser_lookup(*argv); + if (!parser) { + fprintf(stderr, "unknown type: %s\n", *argv); + return 1; + } + + argc--; + argv++; + + ret = parser->parse(argc, argv, parser->store); + if (ret < 0) { + fprintf(stderr, "parsing failed\n"); + return 1; + } + parser->dirty = 1; + + argc -= ret; + argv += ret; + } + + /* + * Now that the data's been parsed, write back any changed fields + */ + memset(&eeprom, 0, sizeof(eeprom)); + ptr = eeprom.tlv; + for (size_t i = 0; i < ARRAY_SIZE(parsers); i++) { + parser = parsers + i; + if (!parser->dirty) + continue; + ptr += tlv_write(ptr, parser->tag, parser->size, parser->store); + } + + tlv_eeprom_seal(&eeprom, USRP_EEPROM_MAGIC, ptr - eeprom.tlv); + tlv_eeprom_write_to_file(&eeprom, output_file); + validate_file_matches(&eeprom, output_file); + + return 0; +} diff --git a/mpm/tools/tlv_eeprom/eeprom-path b/mpm/tools/tlv_eeprom/eeprom-path new file mode 100644 index 000000000..123c3b7f7 --- /dev/null +++ b/mpm/tools/tlv_eeprom/eeprom-path @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +from pathlib import Path +import sys + +DT_BASE = Path("/sys/firmware/devicetree/base") + +def find_eeprom(name): + with open(DT_BASE / "__symbols__" / name) as f: + dt_path = DT_BASE / f.read()[1:-1] + + for dev in Path("/sys/bus/nvmem/devices").glob("*"): + of_node_path = dev / "of_node" + if of_node_path.exists() and dt_path.samefile(of_node_path): + return dev / "nvmem" + +def list_eeproms(): + return [dev.name for dev in Path(DT_BASE / "__symbols__").glob("*_eeprom")] + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Return the path to an eeprom") + print("usage: %s <eeprom_name>" % sys.argv[0]) + print("valid eeprom_names: " + ', '.join(list_eeproms())) + sys.exit(1) + name = sys.argv[1] + try: + path = find_eeprom(name) + if path is None: + print("%s: found symbol, but nvmem device not present"% sys.argv[0], file=sys.stderr) + sys.exit(1) + except: + print("%s: could not find nvmem device for devicetree symbol %s" % (sys.argv[0], name), file=sys.stderr) + sys.exit(1) + + print(path) diff --git a/mpm/tools/tlv_eeprom/eeprom-pids.c b/mpm/tools/tlv_eeprom/eeprom-pids.c new file mode 100644 index 000000000..d181133d7 --- /dev/null +++ b/mpm/tools/tlv_eeprom/eeprom-pids.c @@ -0,0 +1,50 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "eeprom-pids.h" +#include <stddef.h> + +static struct pid_info pid_list[] = { + { 0x0410, "titanium", "X410 Motherboard", 0 }, + { 0x4000, NULL, "Power Aux Board", 0 }, + { 0x4001, NULL, "Debug RF DB", 0 }, + { 0x4002, "zbx", "Zirconium RF DB", 0}, + { 0x4003, NULL, "HDMI SE DIO Aux Board", 0}, + { 0x4004, NULL, "Clocking Aux Board with GPSDO", 0}, + { 0x4005, NULL, "Clocking Aux Board (no GPSDO)", 0}, + { 0x4006, NULL, "IF Test Manufacturing CCA", 0}, +}; + +#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((*x))) + +const struct pid_info* get_info_from_pid(uint16_t pid) { + for (size_t i = 0; i < ARRAY_SIZE(pid_list); i++) + if (pid_list[i].pid == pid) + return &pid_list[i]; + + return NULL; +} + +const char* get_name_from_pid(uint16_t pid) { + const struct pid_info *info = get_info_from_pid(pid); + if (!info) + return NULL; + return info->name; +} + +const char* get_description_from_pid(uint16_t pid) { + const struct pid_info *info = get_info_from_pid(pid); + if (!info) + return NULL; + return info->description; +} + +uint16_t get_rev_offset_from_pid(uint16_t pid) { + const struct pid_info *info = get_info_from_pid(pid); + if (!info) + return -1; + return info->rev_offset; +} diff --git a/mpm/tools/tlv_eeprom/eeprom-pids.h b/mpm/tools/tlv_eeprom/eeprom-pids.h new file mode 100644 index 000000000..8358d6384 --- /dev/null +++ b/mpm/tools/tlv_eeprom/eeprom-pids.h @@ -0,0 +1,21 @@ +// +// Copyright 2021 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <stdint.h> + +struct pid_info { + uint16_t pid; + const char* name; + const char* description; + uint16_t rev_offset; +}; + +const struct pid_info* get_info_from_pid(uint16_t pid); +const char* get_name_from_pid(uint16_t pid); +const char* get_description_from_pid(uint16_t pid); +uint16_t get_rev_offset_from_pid(uint16_t pid); diff --git a/mpm/tools/tlv_eeprom/eeprom-wrapper b/mpm/tools/tlv_eeprom/eeprom-wrapper new file mode 100644 index 000000000..4e7c7dbfa --- /dev/null +++ b/mpm/tools/tlv_eeprom/eeprom-wrapper @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +util=$(basename $0) +if [ $# -ge 1 ]; then + symbol=$1 + if [ -n "$symbol" ] && [ "${symbol:(-7)}" != "_eeprom" ]; then + symbol="${symbol}_eeprom" + fi + path=$(eeprom-path $symbol) +fi +$util-core $path ${*:2} 2> >( sed "s!-core <\(eeprom\|output_file\)>! <symbol>!" >&2) diff --git a/mpm/tools/tlv_eeprom/tlv_eeprom.c b/mpm/tools/tlv_eeprom/tlv_eeprom.c new file mode 100644 index 000000000..8b2e2d9c0 --- /dev/null +++ b/mpm/tools/tlv_eeprom/tlv_eeprom.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Ettus Research, a National Instruments Brand + */ + +#include <string.h> +#include "tlv_eeprom.h" + +struct tlv_eeprom *tlv_eeprom_init(void *data) +{ + struct tlv_eeprom *e = data; + + return e; +} + +void tlv_eeprom_seal(struct tlv_eeprom *e, uint32_t magic, size_t len) +{ + e->magic = magic; + e->size = len; + e->crc = tlv_eeprom_crc(e); +} + +int tlv_eeprom_validate(const struct tlv_eeprom *e, uint32_t magic) +{ + uint32_t crc; + + if (e->magic != magic) + return -1; + + if (e->size > TLV_EEPROM_SIZE) + return -2; + + crc = tlv_eeprom_crc(e); + if (e->crc != crc) + return -3; + + return 0; +} + +struct tlv { + uint8_t tag; + uint8_t len; + uint8_t val[0]; +}; + + +static inline const struct tlv *tlv_next(const struct tlv *tlv, const void *end) +{ + const void *p = &tlv->val; + + p += tlv->len; + if (p >= end) + return NULL; + return p; +} + +static inline const struct tlv *tlv_find(const struct tlv *tlv, + const void *end, uint8_t tag) +{ + do { + if (tlv->tag == tag) + break; + } while ((tlv = tlv_next(tlv, end))); + + return tlv; +} + +void tlv_for_each(const void *buf, size_t bufsz, + void (*fn)(uint8_t tag, uint8_t len, const void *val)) +{ + const struct tlv *tlv = buf; + + do { + fn(tlv->tag, tlv->len, tlv->val); + } while ((tlv = tlv_next(tlv, buf + bufsz))); +} + +const void *tlv_lookup(const void *buf, size_t bufsz, uint8_t tag) +{ + const struct tlv *tlv; + + tlv = tlv_find(buf, buf + bufsz, tag); + if (!tlv) + return tlv; + + return tlv->val; +} + +size_t tlv_write(void *buf, uint8_t tag, uint8_t len, const void *val) +{ + struct tlv *tlv = buf; + + tlv->tag = tag; + tlv->len = len; + memcpy(tlv->val, val, len); + + return sizeof(*tlv) + len; +} diff --git a/mpm/tools/tlv_eeprom/tlv_eeprom.h b/mpm/tools/tlv_eeprom/tlv_eeprom.h new file mode 100644 index 000000000..319a3cc29 --- /dev/null +++ b/mpm/tools/tlv_eeprom/tlv_eeprom.h @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Ettus Research, a National Instruments Brand + */ + +#pragma once + +#include <stddef.h> +#include <stdint.h> + +/* + * All eeproms on this platform are 256 bytes long + */ +#define TLV_EEPROM_SIZE (256) + +/** + * struct tlv_eeprom - structure describing the eeprom layout + * + * NOTE: All fields stored to eeprom are little endian, and this library + * assumes the processor running it is also little endian (i.e., no endianess + * conversion is performed). + * + * @magic: magic to identify the eeprom + * @crc: crc over the size field plus the following size bytes + * @size: size of remaining data in bytes + * @tlv: up to 244 bytes of tlv data + */ +struct tlv_eeprom { + uint32_t magic; + uint32_t crc; + uint32_t size; + uint8_t tlv[TLV_EEPROM_SIZE - 3 * sizeof(uint32_t)]; +} __attribute__((packed)); + +/** + * tlv_eeprom_init - create tlv_eeprom from backing store + * @data: backing store, which must be at least TLV_EEPROM_SIZE bytes + */ +struct tlv_eeprom *tlv_eeprom_init(void *data); + +/** + * tlv_eeprom_validate - check if an eeprom is valid + * @e: eeprom to validate + * @magic: expected magic for this eeprom + * + * Checks to see whether the eeprom is valid. This validates the magic, the + * size, and the CRC of the data. + * + * Return: zero on success, -1 for a magic mismatch, -2 for an invalid size, -3 + * for an invalid crc + */ +int tlv_eeprom_validate(const struct tlv_eeprom *e, uint32_t magic); + +/** + * tlv_eeprom_seal - seal an eeprom + * @e: eeprom to seal + * @magic: magic to use for this eeprom + * @len: size of tlv data stored in eeprom + * + * Updates the magic, size, and crc of the eeprom. This should be called prior + * to writing an eeprom. + */ +void tlv_eeprom_seal(struct tlv_eeprom *e, uint32_t magic, size_t len); + +/* + * tlv_eeprom_crc - return the CRC for the eeprom + * @eeprom: eeprom to compute the crc + * + * Note: e->size must be valid! + * + * Returns: the crc + */ +extern uint32_t tlv_eeprom_crc(const struct tlv_eeprom *e); + +/** + * tlv_write - writes a tlv tuple to the buffer + * @buf: buffer to write to + * @tag: tag for this data + * @len: length of value + * @val: value to write + * + * Return: number of bytes written to buffer, including tag and size + */ +size_t tlv_write(void *buf, uint8_t tag, uint8_t len, const void *val); + +/** + * tlv_lookup - lookup the value associated with a tag + * @buf: buffer containing tlv data + * @bufsz: size of buffer containing tlv data + * @tag: tag to lookup + * + * Return: pointer to value at tag, or NULL if tag was not found + */ +const void *tlv_lookup(const void *buf, size_t bufsz, uint8_t tag); + +/* + * tlv_for_each - call fn for each value in buffer + * @buf: buffer containing tlv data + * @bufsz: size of buffer containing tlv data + * @fn: function to invoke for each value + */ +void tlv_for_each(const void *buf, size_t bufsz, + void (*fn)(uint8_t tag, uint8_t len, const void *val)); + diff --git a/mpm/tools/tlv_eeprom/tlv_eeprom_io.c b/mpm/tools/tlv_eeprom/tlv_eeprom_io.c new file mode 100644 index 000000000..83586e4f6 --- /dev/null +++ b/mpm/tools/tlv_eeprom/tlv_eeprom_io.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Ettus Research, a National Instruments Brand + */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "tlv_eeprom.h" + +struct tlv_eeprom *tlv_eeprom_read_from_file(const char *path) +{ + struct tlv_eeprom *ep; + int fd; + uint8_t *ptr; + size_t len; + ssize_t rd; + + fd = open(path, O_RDONLY); + if (fd < 0) { + perror("couldn't open file"); + return NULL; + } + + + ep = malloc(sizeof(struct tlv_eeprom)); + if (!ep) { + perror("alloc failed"); + return NULL; + } + + len = sizeof(struct tlv_eeprom); + ptr = (uint8_t *)ep; + + while (len) { + rd = read(fd, ptr, len); + if (rd < 0) { + perror("read failed"); + goto out; + } + + len -= rd; + ptr += rd; + + if (!rd) + break; + } + + return ep; +out: + free(ep); + return NULL; +} + +int tlv_eeprom_write_to_file(const struct tlv_eeprom *e, const char *path) +{ + int fd, rv = 0; + uint8_t *ptr; + size_t len; + ssize_t wr; + + fd = open(path, O_WRONLY | O_CREAT, 0666); + if (fd < 0) { + perror("couldn't open file"); + return -1; + } + + len = sizeof(*e); + ptr = (uint8_t *)e; + + while (len) { + wr = write(fd, ptr, len); + if (wr < 0) { + perror("error writing file"); + goto out; + } + len -= wr; + ptr += wr; + } + +out: + close(fd); + return rv; +} diff --git a/mpm/tools/tlv_eeprom/tlv_eeprom_io.h b/mpm/tools/tlv_eeprom/tlv_eeprom_io.h new file mode 100644 index 000000000..348bc6207 --- /dev/null +++ b/mpm/tools/tlv_eeprom/tlv_eeprom_io.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Ettus Research, a National Instruments Brand + */ + +#pragma once + +#include "tlv_eeprom.h" + +/** + * tlv_eeprom_write_to_file - write an eeprom to a file + * @e: eeprom to write + * @path: destination to write to + * + * Writes the eeprom to the specified path + * + * Return: zero on success, else negative error code + */ +int tlv_eeprom_write_to_file(const struct tlv_eeprom *e, const char *path); + +/** + * tlv_eeprom_read_from_file - read an eeprom from file + * @path: path to read from + * + * Reads from the path into an allocated eeprom object. No validation of the + * data is performed, and caller is responsible for free'ing this. + * + * Return: eeprom, or NULL if error + */ +struct tlv_eeprom *tlv_eeprom_read_from_file(const char *path); + diff --git a/mpm/tools/tlv_eeprom/usrp_eeprom.h b/mpm/tools/tlv_eeprom/usrp_eeprom.h new file mode 100644 index 000000000..224ce8c1b --- /dev/null +++ b/mpm/tools/tlv_eeprom/usrp_eeprom.h @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Ettus Research, a National Instruments Brand + */ + +#pragma once + +#include <stdint.h> + +#define USRP_EEPROM_MAGIC 0x55535250 + +/** + * struct usrp_eeprom_board_info - common board info + * + * @pid: product id for the board + * @rev: current hardware revision, starting at 1 + * @compat_rev: last hardware revision this is compatible with + * @serial: NUL-terminated serial number string + */ +#define USRP_EEPROM_BOARD_INFO_TAG (0x10) +struct usrp_eeprom_board_info { + uint16_t pid; + uint16_t rev; + uint16_t compat_rev; + char serial[8]; +} __attribute__((packed)); + +/** + * struct usrp_eeprom_module_info - common module info + * + * @pid: product id for the module + * @rev: module revision, starting at 1 + * @serial: NUL-terminated serial number string for the module + */ +#define USRP_EEPROM_MODULE_INFO_TAG (0x11) +struct usrp_eeprom_module_info { + uint16_t pid; + uint16_t rev; + char serial[8]; +} __attribute__((packed)); + +/** + * struct usrp_eeprom_mac_addr - mac address + * + * @addr: 6-byte MAC address + */ + +/* tags 0xA0 through 0xAF are reserved for MAC addresses */ +#define USRP_EEPROM_MAX_MAC_ADDRS 0xF +#define USRP_EEPROM_MAC_ADDR_TAG(x) (0xA0 + ((x) & USRP_EEPROM_MAX_MAC_ADDRS)) +#define USRP_EEPROM_ETH0_ADDR_TAG USRP_EEPROM_MAC_ADDR_TAG(0) +#define USRP_EEPROM_QSFP0_ADDR_TAG USRP_EEPROM_MAC_ADDR_TAG(1) +#define USRP_EEPROM_QSFP1_ADDR_TAG USRP_EEPROM_MAC_ADDR_TAG(2) +struct usrp_eeprom_mac_addr { + uint8_t addr[6]; +} __attribute__((packed)); + +/** + * struct usrp_eeprom_db_pwr_seq - daughterboard power sequence + * + * @nsteps: the number of steps in the sequence + * @steps.delay: delay in milliseconds to wait after enabling supplies + * @steps.supply_mask: bitmask of supplies to enable + * For X410: bit 0 = 1.8, bit 1 = 2.5, 3.3, 3.7, 12 + */ +#define USRP_EEPROM_DB_PWR_SEQ_TAG (0x12) +struct usrp_eeprom_db_pwr_seq { + uint8_t nsteps; + struct { + uint16_t delay; + uint8_t supply_mask; + } steps[8]; +}; + +#define USRP_EEPROM_MCU_FLAGS (0x20) +struct usrp_eeprom_mcu_flags { + uint8_t flags[6]; +} __attribute__((packed)); + +/** + * struct usrp_eeprom_fan_limits - fan speed (rpm) limits + * + * @min: minimum configurable speed + * @start: necessary start speed + * @max: maximum configurable speed + */ +#define USRP_EEPROM_FAN_LIMITS (0x21) +struct usrp_eeprom_fan_limits { + uint16_t min; + uint16_t start; + uint16_t max; +} __attribute__((packed)); + +/** + * struct usrp_eeprom_fan_fixed_capacity - fixed cooling capacity + * + * @capacity: fixed cooling capacity percentage. Range 0-100. + * @reserved: extra byte for alignment + */ +#define USRP_EEPROM_FAN_FIXED_CAPACITY (0x22) +struct usrp_eeprom_fan_fixed_capacity { + uint8_t capacity; + uint8_t reserved; /* for natural alignment */ +} __attribute__((packed)); + +/** + * struct usrp_eeprom_clkaux_tuning_word - clk aux dac tuning word + * + * @tuning_word: clocking aux board dac tuning word. Range 0-1023. + */ +#define USRP_EEPROM_CLKAUX_TUNING_WORD (0x23) +struct usrp_eeprom_clkaux_tuning_word { + uint16_t tuning_word; +} __attribute__((packed)); + +#include <stdio.h> +#include <assert.h> + +static void usrp_eeprom_trace(uint8_t tag, uint8_t len, const void *val) +{ + uint8_t i; + + switch (tag) { + case USRP_EEPROM_BOARD_INFO_TAG: + { + const struct usrp_eeprom_board_info *v = val; + assert(sizeof(*v) == len); + printf("%s (0x%02x) ", "usrp_eeprom_board_info", tag); + printf("pid: 0x%04x, rev: 0x%04x, compat_rev: 0x%04x, serial: %s\n", + v->pid, v->rev, v->compat_rev, v->serial); + } + break; + case USRP_EEPROM_MODULE_INFO_TAG: + { + const struct usrp_eeprom_module_info *v = val; + assert(sizeof(*v) == len); + printf("%s (0x%02x) ", "usrp_eeprom_module_info", tag); + printf("pid: 0x%04x, rev: 0x%04x, serial: %s\n", + v->pid, v->rev, v->serial); + } + break; + case USRP_EEPROM_MAC_ADDR_TAG(0) ... USRP_EEPROM_MAC_ADDR_TAG(USRP_EEPROM_MAX_MAC_ADDRS): + { + const struct usrp_eeprom_mac_addr *v = val; + assert(sizeof(*v) == len); + printf("%s mac_addr_%d (0x%02x) ", "usrp_eeprom_mac_addr", + tag - USRP_EEPROM_MAC_ADDR_TAG(0), tag); + for (i = 0; i < 6; i++) + printf("%02x%c", v->addr[i], i == 5 ? ' ' : ':'); + printf("\n"); + } + break; + case USRP_EEPROM_DB_PWR_SEQ_TAG: + { + const struct usrp_eeprom_db_pwr_seq *v = val; + assert(sizeof(*v) == len); + printf("%s (0x%02x) ", "usrp_eeprom_db_pwr_seq", tag); + for (i = 0; i < 8; i++) + printf("(%u, 0x%02x) ", v->steps[i].delay, v->steps[i].supply_mask); + printf("\n"); + } + break; + case USRP_EEPROM_MCU_FLAGS: + { + const struct usrp_eeprom_mcu_flags *v = val; + printf("%s (0x%02x) ", "usrp_eeprom_mcu_flags", tag); + for (i = 0; i < 6; i++) + printf("0x%02x ", v->flags[i]); + printf("\n"); + } + break; + case USRP_EEPROM_FAN_LIMITS: + { + const struct usrp_eeprom_fan_limits *v = val; + printf("%s (0x%02x) ", "usrp_eeprom_fan_limits", tag); + printf("min: %d, start: %d, max: %d", v->min, v->start, v->max); + printf("\n"); + } + break; + case USRP_EEPROM_FAN_FIXED_CAPACITY: + { + const struct usrp_eeprom_fan_fixed_capacity *v = val; + printf("%s (0x%02x) ", "usrp_eeprom_fan_fixed_capacity", tag); + printf("%d", v->capacity); + printf("\n"); + } + break; + case USRP_EEPROM_CLKAUX_TUNING_WORD: + { + const struct usrp_eeprom_clkaux_tuning_word *v = val; + printf("%s (0x%02x) ", "usrp_eeprom_clkaux_tuning_word", tag); + printf("%d", v->tuning_word); + printf("\n"); + } + break; + default: + { + const uint8_t *ptr = val; + printf("%s (0x%02x) len: %hhu, val: ", "unknown", tag, len); + for (i = 0; i < len; i++) + printf("%02x ", ptr[i]); + printf("\n"); + break; + } + } +} |