pds_core: add PLDM component info display

Add detailed component information display via devlink info. This
allows users to see individual firmware components and their versions.
Components are reported as fixed, running, or stored based on their
firmware-provided flags.

Example output:
  $ devlink dev info pci/0000:00:05.0
  versions:
    fixed:
      asic.id 0x0
      asic.rev 0x0
    running:
      fw.bootloader 1.2.3
      fw.uboot 1.60.0-73
      fw 1.60.0-73
      fw.cpld 3.18
    stored:
      fw.bootloader 1.2.3
      fw.uboot 1.60.0-73
      fw.uboot.gold 1.50.0-22
      fw.gold 1.50.0-22
      fw 1.60.0-73
      fw.cpld 3.18

Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Link: https://patch.msgid.link/20260730-upstream_v8-v12-4-136cd174ee85@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Brett Creeley
2026-07-30 04:25:21 +00:00
committed by Jakub Kicinski
parent fb918581d4
commit 5df545b3a5
2 changed files with 142 additions and 5 deletions

View File

@@ -596,6 +596,8 @@ void pdsc_fw_up(struct pdsc *pdsc)
return;
}
pdsc_fw_components_invalidate(pdsc);
err = pdsc_setup(pdsc, PDSC_SETUP_RECOVERY);
if (err)
goto err_out;

View File

@@ -93,14 +93,120 @@ int pdsc_dl_flash_update(struct devlink *dl,
return pdsc_firmware_update(pdsc, params, extack);
}
static int pdsc_dl_report_component(struct devlink_info_req *req,
struct pds_core_fw_component_info *info)
{
enum devlink_info_version_type ver_type;
u16 flags = le16_to_cpu(info->flags);
char *ver = info->version;
const char *name;
char buf[32];
/* Main firmware is reported as generic "fw" */
if (info->component_type == PDS_CORE_FW_TYPE_MAIN) {
if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
snprintf(buf, sizeof(buf), "fw.gold");
else
snprintf(buf, sizeof(buf), "fw");
} else {
name = pdsc_fw_type_to_name(info->component_type);
if (!name)
return 0;
if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
snprintf(buf, sizeof(buf), "fw.%s.gold", name);
else
snprintf(buf, sizeof(buf), "fw.%s", name);
}
ver_type = DEVLINK_INFO_VERSION_TYPE_NONE;
if (flags & PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME)
ver_type = DEVLINK_INFO_VERSION_TYPE_COMPONENT;
if (flags & PDS_CORE_FW_COMPONENT_INFO_F_FIXED) {
int err;
err = devlink_info_version_fixed_put(req, buf, ver);
if (err)
return err;
}
if (flags & PDS_CORE_FW_COMPONENT_INFO_F_RUNNING) {
int err;
err = devlink_info_version_running_put_ext(req, buf,
ver, ver_type);
if (err)
return err;
}
if (flags & PDS_CORE_FW_COMPONENT_INFO_F_STARTUP) {
int err;
err = devlink_info_version_stored_put_ext(req, buf,
ver, ver_type);
if (err)
return err;
}
return 0;
}
static int pdsc_dl_report_fw_ver(struct devlink_info_req *req, char *fw_ver)
{
return devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW,
fw_ver);
}
static int pdsc_dl_component_info_get(struct devlink *dl,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct pdsc *pdsc = devlink_priv(dl);
u8 num_components;
int err;
int i;
/* Pairs with WRITE_ONCE in pdsc_fw_components_invalidate().
* Use READ_ONCE to get a consistent snapshot of num_components.
* pdsc_fw_components_invalidate() can zero it concurrently during
* firmware recovery; using the local copy avoids iterating zero
* times when we already decided the cache was valid.
*/
num_components = READ_ONCE(pdsc->fw_components.num_components);
if (!num_components) {
err = pdsc_get_component_info(pdsc);
if (err)
return pdsc_dl_report_fw_ver(req,
pdsc->dev_info.fw_version);
num_components = READ_ONCE(pdsc->fw_components.num_components);
if (!num_components)
return pdsc_dl_report_fw_ver(req,
pdsc->dev_info.fw_version);
}
num_components = min_t(u16, num_components,
le16_to_cpu(pdsc->dev_ident.max_fw_slots));
for (i = 0; i < num_components; i++) {
err = pdsc_dl_report_component(req,
&pdsc->fw_components.info[i]);
if (err)
return err;
}
return 0;
}
static char *fw_slotnames[] = {
"fw.goldfw",
"fw.mainfwa",
"fw.mainfwb",
};
int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
static int pdsc_dl_fw_list_info_get(struct devlink *dl,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
union pds_core_dev_cmd cmd = {
.fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
@@ -134,12 +240,41 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
return err;
}
err = devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW,
pdsc->dev_info.fw_version);
return 0;
}
static int pdsc_dl_info_get_v1(struct devlink *dl,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct pdsc *pdsc = devlink_priv(dl);
int err;
err = pdsc_dl_fw_list_info_get(dl, req, extack);
if (err)
return err;
/* Version 1: report fw from dev_info (running only) */
return pdsc_dl_report_fw_ver(req, pdsc->dev_info.fw_version);
}
int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct pdsc *pdsc = devlink_priv(dl);
char buf[32];
int err;
if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2) {
err = pdsc_dl_component_info_get(dl, req, extack);
if (err)
return err;
} else {
err = pdsc_dl_info_get_v1(dl, req, extack);
if (err)
return err;
}
snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_type);
err = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,