Add unit tests for rte_eth_module_eeprom_parse(). The tests decode SFF-8079, SFF-8472 and SFF-8636 EEPROM images built in the test, check the identification and diagnostic fields, and check that the SFF-8472 diagnostics are skipped when page A2h is not complete. Invalid parameters, too short data and unknown module types must be rejected without invoking the callback.
Signed-off-by: Roman Khromenok <[email protected]> --- app/test/meson.build | 1 + app/test/test_ethdev_module_eeprom.c | 285 +++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 app/test/test_ethdev_module_eeprom.c diff --git a/app/test/meson.build b/app/test/meson.build index 9dcdb069e8..c6a808d63d 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -74,6 +74,7 @@ source_file_deps = { 'test_errno.c': [], 'test_ethdev_api.c': ['ethdev'], 'test_ethdev_link.c': ['ethdev'], + 'test_ethdev_module_eeprom.c': ['ethdev'], 'test_event_crypto_adapter.c': ['cryptodev', 'eventdev', 'bus_vdev'], 'test_event_dma_adapter.c': ['dmadev', 'eventdev', 'bus_vdev'], 'test_event_eth_rx_adapter.c': ['ethdev', 'eventdev', 'bus_vdev'], diff --git a/app/test/test_ethdev_module_eeprom.c b/app/test/test_ethdev_module_eeprom.c new file mode 100644 index 0000000000..8a09b870da --- /dev/null +++ b/app/test/test_ethdev_module_eeprom.c @@ -0,0 +1,285 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2026 Roman Khromenok + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include <rte_common.h> +#include <rte_ethdev.h> +#include <rte_string_fns.h> + +#include "test.h" + +#define MAX_FIELDS 128 +#define MAX_NAME_LEN 64 +#define MAX_VALUE_LEN 128 + +struct decoded_fields { + unsigned int count; + struct { + char name[MAX_NAME_LEN]; + char value[MAX_VALUE_LEN]; + } field[MAX_FIELDS]; +}; + +static struct decoded_fields fields; + +static void +collect_field(const char *name, const char *value, void *arg) +{ + struct decoded_fields *f = arg; + + if (f->count >= MAX_FIELDS) + return; + strlcpy(f->field[f->count].name, name, MAX_NAME_LEN); + strlcpy(f->field[f->count].value, value, MAX_VALUE_LEN); + f->count++; +} + +static const char * +find_field(const char *name) +{ + unsigned int i; + + for (i = 0; i < fields.count; i++) + if (strcmp(fields.field[i].name, name) == 0) + return fields.field[i].value; + return NULL; +} + +static int +parse(uint32_t type, const uint8_t *data, uint32_t length) +{ + memset(&fields, 0, sizeof(fields)); + return rte_eth_module_eeprom_parse(type, data, length, collect_field, &fields); +} + +#define CHECK_FIELD(name, expected) do { \ + const char *value = find_field(name); \ + TEST_ASSERT_NOT_NULL(value, "Field \"%s\" not found", name); \ + TEST_ASSERT(strcmp(value, expected) == 0, \ + "Field \"%s\": expected \"%s\", got \"%s\"", name, expected, value); \ +} while (0) + +#define CHECK_NO_FIELD(name) \ + TEST_ASSERT_NULL(find_field(name), "Unexpected field \"%s\"", name) + +static void +put_u16(uint8_t *data, unsigned int offset, uint16_t value) +{ + data[offset] = value >> 8; + data[offset + 1] = value & 0xff; +} + +/* SFP+ 10GBASE-LR module, SFF-8472 layout: page A0h then page A2h */ +static void +fill_sfp(uint8_t *data) +{ + uint8_t *a2 = data + RTE_ETH_MODULE_SFF_8079_LEN; + + memset(data, 0, RTE_ETH_MODULE_SFF_8472_LEN); + data[0] = 0x03; /* identifier: SFP */ + data[1] = 0x04; /* extended identifier */ + data[2] = 0x07; /* connector: LC */ + data[3] = 0x20; /* 10G Base-LR */ + data[11] = 0x06; /* encoding: 64B/66B */ + data[12] = 103; /* nominal bit rate, units of 100 MBd */ + memcpy(&data[20], "STARRY ", 16); + data[37] = 0x00; + data[38] = 0x1b; + data[39] = 0x21; + memcpy(&data[40], "SFP-10G-LR-20 ", 16); + memcpy(&data[56], "A ", 4); + memcpy(&data[68], "2024082600001 ", 16); + memcpy(&data[84], "240902 ", 8); + /* diagnostics implemented, internally calibrated, average RX power */ + data[92] = 0x68; + + put_u16(a2, 96, 0x2940); /* temperature: 41.25 C */ + put_u16(a2, 98, 33205); /* voltage: 3.3205 V */ + put_u16(a2, 100, 19790); /* TX bias: 39.580 mA */ + put_u16(a2, 102, 7754); /* TX power: 0.7754 mW */ + put_u16(a2, 104, 6724); /* RX power: 0.6724 mW */ +} + +/* QSFP28 module, SFF-8636 layout: lower page then upper page 00h */ +static void +fill_qsfp(uint8_t *data) +{ + memset(data, 0, RTE_ETH_MODULE_SFF_8636_LEN); + data[0] = 0x11; /* identifier: QSFP28 */ + put_u16(data, 22, 0x3040); /* temperature: 48.25 C */ + put_u16(data, 26, 32638); /* voltage: 3.2638 V */ + put_u16(data, 34, 1); /* channel 1 RX power: 0.0001 mW */ + put_u16(data, 42, 21525); /* channel 1 TX bias: 43.050 mA */ + put_u16(data, 50, 12763); /* channel 1 TX power: 1.2763 mW */ + + data[128] = 0x11; /* identifier: QSFP28 */ + data[130] = 0x07; /* connector: LC */ + memcpy(&data[148], "FINISAR CORP. ", 16); + data[165] = 0x00; + data[166] = 0x90; + data[167] = 0x65; + memcpy(&data[168], "FTLC1157RGPL6-FB", 16); + memcpy(&data[184], "A0", 2); + memcpy(&data[196], "X24A15P ", 16); + memcpy(&data[212], "190826 ", 8); + data[220] = 0x08; /* average RX power */ +} + +static int +check_sfp_identity(void) +{ + CHECK_FIELD("Identifier", "0x03 (SFP)"); + CHECK_FIELD("Connector", "0x07 (LC)"); + CHECK_FIELD("Vendor name", "STARRY"); + CHECK_FIELD("Vendor OUI", "00:1b:21"); + CHECK_FIELD("Vendor PN", "SFP-10G-LR-20"); + CHECK_FIELD("Vendor rev", "A"); + CHECK_FIELD("Vendor SN", "2024082600001"); + CHECK_FIELD("Date code", "240902"); + return TEST_SUCCESS; +} + +static int +test_module_eeprom_sfp_8079(void) +{ + uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN]; + + fill_sfp(data); + TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8079, data, RTE_ETH_MODULE_SFF_8079_LEN), + "Failed to parse SFF-8079 data"); + TEST_ASSERT_SUCCESS(check_sfp_identity(), "Wrong SFF-8079 base information"); + CHECK_NO_FIELD("Optical diagnostics support"); + return TEST_SUCCESS; +} + +static int +test_module_eeprom_sfp_8472(void) +{ + uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN]; + + fill_sfp(data); + TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8472, data, sizeof(data)), + "Failed to parse SFF-8472 data"); + TEST_ASSERT_SUCCESS(check_sfp_identity(), "Wrong SFF-8472 base information"); + CHECK_FIELD("Optical diagnostics support", "Yes"); + CHECK_FIELD("Laser bias current", "39.580 mA"); + CHECK_FIELD("Laser output power", "0.7754 mW / -1.10 dBm"); + CHECK_FIELD("Receiver signal average optical power", "0.6724 mW / -1.72 dBm"); + CHECK_FIELD("Module temperature", "41.25 degrees C / 106.25 degrees F"); + CHECK_FIELD("Module voltage", "3.3205 V"); + return TEST_SUCCESS; +} + +static int +test_module_eeprom_sfp_8472_short(void) +{ + uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN]; + const uint32_t lengths[] = { + RTE_ETH_MODULE_SFF_8079_LEN, + RTE_ETH_MODULE_SFF_8472_LEN - 1, + }; + unsigned int i; + + fill_sfp(data); + for (i = 0; i < RTE_DIM(lengths); i++) { + TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8472, data, lengths[i]), + "Failed to parse SFF-8472 data of %u bytes", lengths[i]); + TEST_ASSERT_SUCCESS(check_sfp_identity(), + "Wrong SFF-8472 base information from %u bytes", lengths[i]); + /* page A2h is not complete, diagnostics must be skipped */ + CHECK_NO_FIELD("Optical diagnostics support"); + CHECK_NO_FIELD("Laser bias current"); + } + return TEST_SUCCESS; +} + +static int +test_module_eeprom_qsfp_8636(void) +{ + uint8_t data[RTE_ETH_MODULE_SFF_8636_LEN]; + + fill_qsfp(data); + TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8636, data, sizeof(data)), + "Failed to parse SFF-8636 data"); + CHECK_FIELD("Identifier", "0x11 (QSFP28)"); + CHECK_FIELD("Connector", "0x07 (LC)"); + CHECK_FIELD("Vendor name", "FINISAR CORP."); + CHECK_FIELD("Vendor OUI", "00:90:65"); + CHECK_FIELD("Vendor PN", "FTLC1157RGPL6-FB"); + CHECK_FIELD("Vendor rev", "A0"); + CHECK_FIELD("Vendor SN", "X24A15P"); + CHECK_FIELD("Module temperature", "48.25 degrees C / 118.85 degrees F"); + CHECK_FIELD("Module voltage", "3.2638 V"); + /* page 03h is not provided */ + CHECK_FIELD("Alarm/warning flags implemented", "No"); + CHECK_FIELD("Laser tx bias current (Channel 1)", "43.050 mA"); + CHECK_FIELD("Transmit avg optical power (Channel 1)", "1.2763 mW / 1.06 dBm"); + CHECK_FIELD("Rcvr signal avg optical power(Channel 1)", "0.0001 mW / -40.00 dBm"); + return TEST_SUCCESS; +} + +static int +test_module_eeprom_invalid(void) +{ + uint8_t data[RTE_ETH_MODULE_SFF_8636_MAX_LEN] = {0}; + const uint32_t types[] = { + RTE_ETH_MODULE_SFF_8079, + RTE_ETH_MODULE_SFF_8472, + RTE_ETH_MODULE_SFF_8436, + RTE_ETH_MODULE_SFF_8636, + }; + unsigned int i; + int ret; + + /* all supported types require at least one full page */ + for (i = 0; i < RTE_DIM(types); i++) { + ret = parse(types[i], data, 255); + TEST_ASSERT_EQUAL(ret, -EINVAL, + "Type %u with 255 bytes: expected %d, got %d", types[i], -EINVAL, ret); + TEST_ASSERT_EQUAL(fields.count, 0, + "Type %u with 255 bytes: callback called %u times", + types[i], fields.count); + } + + ret = parse(0, data, sizeof(data)); + TEST_ASSERT_EQUAL(ret, -ENOTSUP, "Type 0: expected %d, got %d", -ENOTSUP, ret); + ret = parse(RTE_ETH_MODULE_SFF_8436 + 1, data, sizeof(data)); + TEST_ASSERT_EQUAL(ret, -ENOTSUP, "Unknown type: expected %d, got %d", -ENOTSUP, ret); + TEST_ASSERT_EQUAL(fields.count, 0, "Unknown type: callback called %u times", + fields.count); + + ret = rte_eth_module_eeprom_parse(RTE_ETH_MODULE_SFF_8079, NULL, + RTE_ETH_MODULE_SFF_8079_LEN, collect_field, &fields); + TEST_ASSERT_EQUAL(ret, -EINVAL, "NULL data: expected %d, got %d", -EINVAL, ret); + ret = rte_eth_module_eeprom_parse(RTE_ETH_MODULE_SFF_8079, data, + RTE_ETH_MODULE_SFF_8079_LEN, NULL, NULL); + TEST_ASSERT_EQUAL(ret, -EINVAL, "NULL callback: expected %d, got %d", -EINVAL, ret); + + return TEST_SUCCESS; +} + +static struct unit_test_suite module_eeprom_testsuite = { + .suite_name = "ethdev module EEPROM decoding", + .setup = NULL, + .teardown = NULL, + .unit_test_cases = { + TEST_CASE(test_module_eeprom_sfp_8079), + TEST_CASE(test_module_eeprom_sfp_8472), + TEST_CASE(test_module_eeprom_sfp_8472_short), + TEST_CASE(test_module_eeprom_qsfp_8636), + TEST_CASE(test_module_eeprom_invalid), + TEST_CASES_END() + } +}; + +static int +test_module_eeprom(void) +{ + return unit_test_suite_runner(&module_eeprom_testsuite); +} + +REGISTER_FAST_TEST(ethdev_module_eeprom, NOHUGE_OK, ASAN_OK, test_module_eeprom); -- 2.47.3

