vnet: add eeprom_read_function 50/43550/6
authorPim van Pelt <[email protected]>
Thu, 14 Aug 2025 16:56:57 +0000 (18:56 +0200)
committerDamjan Marion <[email protected]>
Fri, 15 Aug 2025 20:36:30 +0000 (20:36 +0000)
Introduce a device-class function to read EEPROM from interfaces.
Implement it for the DPDK plugin.

Type: improvement
Change-Id: I8723937a5618fada2d87d7bded33bf9fc85836f3
Signed-off-by: [email protected]
src/plugins/dpdk/device/common.c
src/plugins/dpdk/device/device.c
src/plugins/dpdk/device/dpdk.h
src/vnet/interface.h
src/vnet/interface_cli.c

index f496226..9de39b0 100644 (file)
@@ -515,6 +515,70 @@ dpdk_get_vmbus_device (const struct rte_eth_dev_info *info)
 }
 #endif /* __linux__ */
 
+clib_error_t *
+dpdk_read_eeprom (vnet_main_t *vnm, vnet_hw_interface_t *hi,
+                 vnet_interface_eeprom_t **eeprom)
+{
+  dpdk_main_t *dm = &dpdk_main;
+  vnet_interface_main_t *im = &vnm->interface_main;
+  dpdk_device_t *xd;
+  vnet_device_class_t *dc;
+  struct rte_eth_dev_module_info mi = { 0 };
+  struct rte_dev_eeprom_info ei = { 0 };
+
+  dc = vec_elt_at_index (im->device_classes, hi->dev_class_index);
+  *eeprom = NULL;
+
+  if (dc->index != dpdk_device_class.index)
+    {
+      return clib_error_return (0, "Interface %v is not a DPDK interface",
+                               hi->name);
+    }
+
+  if (hi->dev_instance >= vec_len (dm->devices))
+    {
+      return clib_error_return (0, "Invalid device instance %u",
+                               hi->dev_instance);
+    }
+
+  xd = vec_elt_at_index (dm->devices, hi->dev_instance);
+
+  /* Get module info */
+  if (rte_eth_dev_get_module_info (xd->port_id, &mi) != 0)
+    {
+      return clib_error_return (
+       0, "Module info not available for interface %v", hi->name);
+    }
+  if (mi.eeprom_len > 1024)
+    {
+      return clib_error_return (0, "EEPROM invalid length: %u bytes",
+                               mi.eeprom_len);
+    }
+
+  /* Allocate EEPROM structure */
+  *eeprom = clib_mem_alloc (sizeof (vnet_interface_eeprom_t));
+  if (!*eeprom)
+    {
+      return clib_error_return (0, "Memory allocation failed");
+    }
+
+  /* Get EEPROM data */
+  ei.length = mi.eeprom_len;
+  ei.data = (*eeprom)->eeprom_raw;
+
+  if (rte_eth_dev_get_module_eeprom (xd->port_id, &ei) != 0)
+    {
+      clib_mem_free (*eeprom);
+      *eeprom = NULL;
+      return clib_error_return (0, "EEPROM read error for interface %v",
+                               hi->name);
+    }
+
+  (*eeprom)->eeprom_len = mi.eeprom_len;
+  (*eeprom)->eeprom_type = mi.type;
+  return 0;
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index ffa1d7a..6b3edb0 100644 (file)
@@ -778,6 +778,7 @@ VNET_DEVICE_CLASS (dpdk_device_class) = {
   .flow_ops_function = dpdk_flow_ops_fn,
   .set_rss_queues_function = dpdk_interface_set_rss_queues,
   .rx_mode_change_function = dpdk_interface_rx_mode_change,
+  .eeprom_read_function = dpdk_read_eeprom,
 };
 
 #define UP_DOWN_FLAG_EVENT 1
index 70d9cc7..347418c 100644 (file)
@@ -461,6 +461,9 @@ vnet_flow_dev_ops_function_t dpdk_flow_ops_fn;
 
 clib_error_t *unformat_rss_fn (unformat_input_t * input, uword * rss_fn);
 
+clib_error_t *dpdk_read_eeprom (vnet_main_t *vnm, vnet_hw_interface_t *hi,
+                               vnet_interface_eeprom_t **eeprom);
+
 struct rte_pci_device *dpdk_get_pci_device (const struct rte_eth_dev_info
                                            *info);
 struct rte_vmbus_device *
index c6d0185..c9778f9 100644 (file)
@@ -98,6 +98,20 @@ typedef clib_error_t *(vnet_interface_rss_queues_set_t)
   (struct vnet_main_t * vnm, struct vnet_hw_interface_t * hi,
    clib_bitmap_t * bitmap);
 
+/* EEPROM structure for physical network devices */
+typedef struct
+{
+  u32 eeprom_type; /* from linux/ethtool.h */
+  u32 eeprom_len;
+  u8 eeprom_raw[1024];
+} vnet_interface_eeprom_t;
+
+/* Interface EEPROM read function */
+typedef clib_error_t *(
+  vnet_interface_eeprom_read_t) (struct vnet_main_t *vnm,
+                                struct vnet_hw_interface_t *hi,
+                                vnet_interface_eeprom_t **eeprom);
+
 typedef enum
 {
   VNET_FLOW_DEV_OP_ADD_FLOW,
@@ -306,6 +320,9 @@ typedef struct _vnet_device_class
   /* Interface to set rss queues of the interface */
   vnet_interface_rss_queues_set_t *set_rss_queues_function;
 
+  /* Function to read EEPROM data from physical network device */
+  vnet_interface_eeprom_read_t *eeprom_read_function;
+
 } vnet_device_class_t;
 
 u32 vnet_register_device_class (vlib_main_t *, vnet_device_class_t *);
index 4d3c98f..d0b6630 100644 (file)
@@ -2658,6 +2658,160 @@ VLIB_CLI_COMMAND (cmd_show_tx_hash, static) = {
   .function = show_tx_hash,
 };
 
+static void
+show_interface_transceiver_output (vlib_main_t *vm, vnet_hw_interface_t *hi,
+                                  u8 show_module, u8 show_diag,
+                                  u8 show_eeprom, u8 is_terse)
+{
+  clib_error_t *error = 0;
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_interface_eeprom_t *eeprom = 0;
+  vnet_interface_main_t *im = &vnm->interface_main;
+  vnet_device_class_t *dc =
+    vec_elt_at_index (im->device_classes, hi->dev_class_index);
+
+  if (!dc->eeprom_read_function)
+    {
+      error = clib_error_return (
+       0, "interface %v does not support EEPROM reading", hi->name);
+      goto done;
+    }
+
+  error = dc->eeprom_read_function (vnm, hi, &eeprom);
+  if (error)
+    goto done;
+
+  if (!eeprom)
+    {
+      error = clib_error_return (
+       0, "no EEPROM data available for interface %v", hi->name);
+      goto done;
+    }
+
+  vlib_cli_output (vm, "Interface: %v", hi->name);
+
+  /* Default to module if none are set */
+  if (!show_module && !show_diag && !show_eeprom)
+    show_module = 1;
+
+  if (show_eeprom)
+    {
+      vlib_cli_output (vm, "  EEPROM Type: 0x%x", eeprom->eeprom_type);
+      vlib_cli_output (vm, "  EEPROM Length: %u bytes", eeprom->eeprom_len);
+      vlib_cli_output (vm, "  EEPROM Data:");
+
+      /* Print hexdump */
+      for (u32 offset = 0; offset < eeprom->eeprom_len; offset += 16)
+       {
+         u8 *line = format (0, "    %04x: ", offset);
+
+         /* Print hex bytes */
+         for (u32 j = 0; j < 16 && (offset + j) < eeprom->eeprom_len; j++)
+           {
+             line = format (line, "%02x ", eeprom->eeprom_raw[offset + j]);
+           }
+
+         /* Pad to align ASCII section */
+         for (u32 j = (offset + 16 > eeprom->eeprom_len) ?
+                        eeprom->eeprom_len - offset :
+                        16;
+              j < 16; j++)
+           {
+             line = format (line, "   ");
+           }
+
+         line = format (line, " |");
+
+         /* Print ASCII representation */
+         for (u32 j = 0; j < 16 && (offset + j) < eeprom->eeprom_len; j++)
+           {
+             u8 c = eeprom->eeprom_raw[offset + j];
+             line = format (line, "%c", (c >= 32 && c <= 126) ? c : '.');
+           }
+
+         line = format (line, "|");
+         vlib_cli_output (vm, "%v", line);
+         vec_free (line);
+       }
+
+      vlib_cli_output (vm, "");
+    }
+
+  if (show_module)
+    {
+      vlib_cli_output (vm, "  module: not implemented yet");
+    }
+
+  if (show_diag)
+    {
+      vlib_cli_output (vm, "  diag: not implemented yet");
+    }
+
+done:
+  if (eeprom)
+    clib_mem_free (eeprom);
+}
+
+static clib_error_t *
+show_interface_transceiver (vlib_main_t *vm, unformat_input_t *input,
+                           vlib_cli_command_t *cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_interface_main_t *im = &vnm->interface_main;
+  u32 hw_if_index = (u32) ~0;
+  vnet_hw_interface_t *hi;
+  u8 is_terse = 1;
+  u8 show_diag = 0;
+  u8 show_module = 0;
+  u8 show_eeprom = 0;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "diag"))
+       show_diag = 1;
+      else if (unformat (input, "module"))
+       show_module = 1;
+      else if (unformat (input, "eeprom"))
+       show_eeprom = 1;
+      else if (unformat (input, "verbose"))
+       is_terse = 0;
+      else if (unformat (input, "%U", unformat_vnet_hw_interface, vnm,
+                        &hw_if_index))
+       ;
+      else
+       {
+         return clib_error_return (0, "parse error: '%U'",
+                                   format_unformat_error, input);
+       }
+    }
+
+  pool_foreach (hi, im->hw_interfaces)
+    {
+      if (hw_if_index == ~0 || hw_if_index == hi->hw_if_index)
+       {
+         show_interface_transceiver_output (vm, hi, show_module, show_diag,
+                                            show_eeprom, is_terse);
+       }
+    }
+  return 0;
+}
+
+/*?
+ * This command displays the transceiver EEPROM data for a given interface.
+ * The EEPROM data is read from the physical transceiver module (SFP, QSFP,
+ * etc.) and displayed as a hexadecimal dump.
+ *
+ * @cliexpar
+ * Example of how to display transceiver EEPROM data:
+ * @cliexcmd{show interface transceiver GigabitEthernet0/8/0 module diag}
+ ?*/
+VLIB_CLI_COMMAND (cmd_show_interface_transceiver, static) = {
+  .path = "show interface transceiver",
+  .short_help = "show interface transceiver [<interface>] [module] [diag] "
+               "[eeprom] [verbose]",
+  .function = show_interface_transceiver,
+};
+
 /*
  * fd.io coding-style-patch-verification: ON
  *