* [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support
@ 2026-07-29 16:40 Muralidhara M K
2026-07-29 16:40 ` [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah Muralidhara M K
` (6 more replies)
0 siblings, 7 replies; 22+ messages in thread
From: Muralidhara M K @ 2026-07-29 16:40 UTC (permalink / raw)
To: ilpo.jarvinen
Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam, Muralidhara M K
This series enables the HSMP driver on the Family 1Ah client platforms
- Medusa1 (Models 80h-87h), Olympic Ridge (Models 88h-8Fh) and Medusa2
(Models E0h-E3h) - and adds the two read-only ioctls that bring-up on
those parts needs.
The client parts drive a different mailbox from the server HSMP
interface. Its SMN addresses are fixed rather than discoverable through
firmware, and the parts speak the Ryzen Master SMC message set instead
of the server message IDs. Patches 1-4 teach the driver both, leaving
server behaviour unchanged:
- Patch 1 describes each platform once in struct hsmp_plat_desc,
which carries the mailbox addresses, the message descriptor table
and its size, and the IDs of the messages the driver issues itself.
Every lookup then picks the table matching the running platform
rather than testing the platform at each site. It also adds the
client message set to the UAPI header, and lets the client probe
survive an interface-version query that some client SMU builds
reject with "invalid message" even though the mailbox works.
- Patch 2 routes the metric table through the client message IDs,
which unlike the test and version queries are not numbered alike in
the two sets: 24h/25h on the server against 05h/06h on the client.
- Patch 3 handles the ACPI device (HID AMDI0097), which on these
parts exposes neither a _CRS window nor a _DSD mailbox package and
carries a bare socket number in _UID rather than the server "IDXX"
form. Reads go through the read-only SMNR ACPI method, the same
accessor the AMDHSMP Windows driver uses.
- Patch 4 leaves the hwmon power sensors unregistered on the client.
hwmon.c issues the server power message IDs unconditionally, and on
the client those numbers are metric-table queries that pass
validate_message(), so power1_input and power1_cap would report a
table version and the low half of a DRAM address as microwatts.
Patches 5-7 add HSMP_IOCTL_SMN_CMD and HSMP_IOCTL_MSR_CMD and document
them. Bringing a client platform up means reading SMN registers and
core MSRs that the message set does not cover, which today means either
debugfs, absent on production kernels, or an out-of-tree module. Both
ioctls are read-only and require CAP_SYS_RAWIO. Each request struct
carries a @write flag so write support could be added later without
changing the layout, but a request that sets it is refused with -EPERM
regardless of the caller's privilege or open mode.
Each patch builds individually at W=1 and is clean under
checkpatch.pl --strict --codespell.
Muralidhara M K (7):
platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah
platform/x86/amd/hsmp: Route metric table through the client messages
platform/x86/amd/hsmp: Add ACPI client support via the SMNR method
platform/x86/amd/hsmp: Leave the hwmon power sensors off on client
platforms
platform/x86/amd/hsmp: Add SMN read IOCTL support
platform/x86/amd/hsmp: Add MSR read IOCTL support
Documentation/arch/x86/amd_hsmp: Document the SMN and MSR IOCTLs
Documentation/arch/x86/amd_hsmp.rst | 62 +++++-
arch/x86/include/uapi/asm/amd_hsmp.h | 210 ++++++++++++++++++++
drivers/platform/x86/amd/hsmp/acpi.c | 138 +++++++++++--
drivers/platform/x86/amd/hsmp/hsmp.c | 285 ++++++++++++++++++++++++---
drivers/platform/x86/amd/hsmp/hsmp.h | 77 ++++++++
drivers/platform/x86/amd/hsmp/plat.c | 61 +++---
6 files changed, 774 insertions(+), 59 deletions(-)
base-commit: 775f2078b38ff2121410a35302497a1e81b8e459
--
2.34.1
^ permalink raw reply [flat|nested] 22+ messages in thread* [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah 2026-07-29 16:40 [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support Muralidhara M K @ 2026-07-29 16:40 ` Muralidhara M K 2026-07-29 21:27 ` Mario Limonciello 2026-07-30 16:43 ` Armin Wolf 2026-07-29 16:40 ` [PATCH 2/7] platform/x86/amd/hsmp: Route metric table through the client messages Muralidhara M K ` (5 subsequent siblings) 6 siblings, 2 replies; 22+ messages in thread From: Muralidhara M K @ 2026-07-29 16:40 UTC (permalink / raw) To: ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam, Muralidhara M K Add non-ACPI HSMP mailbox support, Ryzen Master SMC client message descriptors, and platform routing for the Family 1Ah client platforms: Models 80h-87h Medusa1 Models 88h-8Fh Olympic Ridge Models E0h-E3h Medusa2 All three share the same client mailbox addresses and message set, so a single is_client_platform() predicate covers them, and the two adjacent Model 8xh ranges are tested as one. The client parts drive a different mailbox from the server HSMP interface. They expose no _DSD/_CRS to discover the mailbox, so the SMN addresses are fixed in hsmp.h and shared by the platform and ACPI probes, and they speak the Ryzen Master SMC (RMSMC) message set rather than the server message IDs. Add that message set and its descriptor table to the UAPI header. Two message descriptor tables now exist, so every lookup has to pick the one matching the running platform. Rather than test the platform at each lookup, describe the platform once: struct hsmp_plat_desc holds the mailbox addresses, the descriptor table and its size, and the IDs of the messages the driver issues itself. get_hsmp_pdev() resolves it, which both drivers call before they bring any socket up and which runs on a CPU that is fixed long before either can load. The server descriptor is also the static initialiser, so the message plumbing cannot reach a NULL descriptor even if a future caller arrives another way. get_msg_desc() then indexes the table behind that pointer, and validate_message(), is_get_msg(), hsmp_ioctl_msg(), hsmp_test() and hsmp_cache_proto_ver() read the bound and the message IDs from the descriptor. get_msg_desc() returns NULL rather than indexing out of range if a caller skipped the bounds check, and the Spectre v1 clamp in hsmp_ioctl_msg() uses the running platform's table size. In init_platform_device() the mailbox setup becomes a copy of the descriptor's addresses. The F1Ah M0h message-id offset stays behind is_f1a_m0h() as an override, because it applies only to this non-ACPI path while the descriptor is shared with the ACPI one. It cannot disturb the client mailbox: it requires model <= 0x0F and the client models are all >= 0x80. The server mailbox addresses move from plat.c to hsmp.h so both address sets sit with the descriptors that consume them; the values are unchanged, written to the same width as the client set they now sit beside. Since this is the change that lets the platform driver match the client models, it also has to let the client probe survive the interface-version query. Some client SMU builds reject HSMP_CLIENT_GET_INTERFACE_VER with "invalid message" even though the mailbox works, as the test message issued just before it shows, so treat that failure as non-fatal on the client and register the socket anyway. Nothing on the client depends on the reported version: hsmp_pdev->proto_ver gates the metrics_bin attribute and the protocol_version sysfs file, both of which describe the server interface. The query stays fatal on server parts. The client descriptor table carries __attribute__((unused)), matching hsmp_msg_desc_table[]. Both are defined in a UAPI header that several translation units include without using the table, so without it -Wunused-const-variable fires on plat.c, acpi.c and hwmon.c. Document the client platforms in Documentation/arch/x86/amd_hsmp.rst, which until now described HSMP as available only on the EPYC server parts and MI300A. The character device and ioctl interface are unchanged there; what differs is the accepted message IDs, so the text points at the HSMP_CLIENT_* set in the UAPI header. Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> --- Documentation/arch/x86/amd_hsmp.rst | 18 +++- arch/x86/include/uapi/asm/amd_hsmp.h | 137 +++++++++++++++++++++++++++ drivers/platform/x86/amd/hsmp/hsmp.c | 125 ++++++++++++++++++++---- drivers/platform/x86/amd/hsmp/hsmp.h | 73 ++++++++++++++ drivers/platform/x86/amd/hsmp/plat.c | 39 ++++---- 5 files changed, 354 insertions(+), 38 deletions(-) diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst index fa1fc240e212..8a9d6fe7d4cf 100644 --- a/Documentation/arch/x86/amd_hsmp.rst +++ b/Documentation/arch/x86/amd_hsmp.rst @@ -8,6 +8,21 @@ Newer Fam19h(model 0x00-0x1f, 0x30-0x3f, 0x90-0x9f, 0xa0-0xaf), Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD support system management functionality via HSMP (Host System Management Port). +The Fam1Ah client platforms are supported as well: + + ============= ============= + Model Platform + ============= ============= + 0x80-0x87 Medusa1 + 0x88-0x8f Olympic Ridge + 0xe0-0xe3 Medusa2 + ============= ============= + +They share one mailbox and speak the Ryzen Master SMC message set instead +of the server HSMP message set, so the message IDs accepted on them are +the HSMP_CLIENT_* ones listed in arch/x86/include/uapi/asm/amd_hsmp.h. +The character device and ioctl interface described below are the same. + The Host System Management Port (HSMP) is an interface to provide OS-level software with access to system management functions via a set of mailbox registers. @@ -17,7 +32,8 @@ More details on the interface can be found in chapter Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50 -HSMP interface is supported on EPYC line of server CPUs and MI300A (APU). +HSMP interface is supported on EPYC line of server CPUs, MI300A (APU) and +the Fam1Ah client platforms listed above. HSMP device diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h index eda336bfd3e9..b4c3ddb9d1c1 100644 --- a/arch/x86/include/uapi/asm/amd_hsmp.h +++ b/arch/x86/include/uapi/asm/amd_hsmp.h @@ -664,4 +664,141 @@ struct hsmp_telemetry_data { #define HSMP_IOCTL_GET_TELEMETRY_DATA \ _IOW(HSMP_BASE_IOCTL_NR, 1, struct hsmp_telemetry_data) +/* + * Client HSMP messages supported on the Family 1Ah client platforms: + * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and + * Models E0h-E3h (Medusa2). + * These map to Ryzen Master SMC (RMSMC) message IDs. + */ +enum hsmp_client_message_ids { + HSMP_CLIENT_TEST = 1, /* 01h Test message */ + HSMP_CLIENT_GET_SMU_VER, /* 02h SMU FW version */ + HSMP_CLIENT_GET_INTERFACE_VER, /* 03h Interface version */ + HSMP_CLIENT_GET_METRICS_TABLE_VER, /* 04h Metrics table version */ + HSMP_CLIENT_GET_METRICS_TABLE, /* 05h Metrics table log sample */ + HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR,/* 06h Metrics table DRAM address */ + HSMP_CLIENT_SET_CORE_PSM_MARGIN, /* 07h Set core PSM margin */ + HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN, /* 08h Set all core PSM margin */ + HSMP_CLIENT_SET_FAST_PPT_LIMIT, /* 09h Set fast PPT limit */ + HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT, /* 0Ah Set VRM VDD current limit */ + HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT,/* 0Bh Set VRM VDD max current limit */ + HSMP_CLIENT_SET_TJ_MAX, /* 0Ch Set TjMax */ + HSMP_CLIENT_SET_FIT_LIMIT_SCALAR, /* 0Dh Set FIT limit scalar */ + HSMP_CLIENT_ENABLE_OVERCLOCKING, /* 0Eh Enable overclocking */ + HSMP_CLIENT_DISABLE_OVERCLOCKING, /* 0Fh Disable overclocking */ + HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES,/* 10h Set OC freq all cores */ + HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE,/* 11h Set OC freq per core */ + HSMP_CLIENT_SET_OVERCLOCK_VID, /* 12h Set OC VID */ + HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY,/* 13h Set FCLK OC on-the-fly */ + HSMP_CLIENT_GET_CORE_PERF_ORDER, /* 14h Get core performance order */ + HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT, /* 15h Set sustained power limit */ + HSMP_CLIENT_SET_SLOW_PPT_LIMIT, /* 16h Set slow PPT limit */ + HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT,/* 17h Set VRM GFX max current limit */ + HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT, /* 18h Set VRM SOC current limit */ + HSMP_CLIENT_SET_FAST_SPM_LIMIT, /* 19h Set fast SPM limit */ + HSMP_CLIENT_SET_SLOW_SPM_LIMIT, /* 1Ah Set slow SPM limit */ + HSMP_CLIENT_GET_CORE_PSM_MARGIN, /* 1Bh Get core PSM margin */ + HSMP_CLIENT_GET_GFX_PSM_MARGIN, /* 1Ch Get GFX PSM margin */ + HSMP_CLIENT_SPARE_0X1D, /* 1Dh Spare */ + HSMP_CLIENT_SPARE_0X1E, /* 1Eh Spare */ + HSMP_CLIENT_SPARE_0X1F, /* 1Fh Spare */ + HSMP_CLIENT_SPARE_0X20, /* 20h Spare */ + HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID,/* 21h Set GfxClk overdrive by freq/VID */ + HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE, /* 22h Disable GfxClk overdrive */ + HSMP_CLIENT_SET_GFX_PSM_MARGIN, /* 23h Set GFX PSM margin */ + HSMP_CLIENT_SET_CCLK_FMAX_OFFSET, /* 24h Set CCLK Fmax offset */ + HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET,/* 25h Set core power limit offset */ + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND, /* 26h Add extra PSM guardband */ + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX,/* 27h Add extra PSM guardband GFX */ + HSMP_CLIENT_SET_GFXCLK_FMAX, /* 28h Set GfxClk Fmax */ + HSMP_CLIENT_MSG_ID_MAX, +}; + +/* Client message descriptor table (Family 1Ah Models 80h-8Fh, E0h-E3h) */ +static const struct hsmp_msg_desc hsmp_client_msg_desc_table[] + __attribute__((unused)) = { + /* RESERVED */ + {0, 0, HSMP_RSVD}, + /* HSMP_CLIENT_TEST */ + {1, 1, HSMP_GET}, + /* HSMP_CLIENT_GET_SMU_VER */ + {0, 1, HSMP_GET}, + /* HSMP_CLIENT_GET_INTERFACE_VER */ + {0, 1, HSMP_GET}, + /* HSMP_CLIENT_GET_METRICS_TABLE_VER */ + {0, 1, HSMP_GET}, + /* HSMP_CLIENT_GET_METRICS_TABLE */ + {0, 0, HSMP_GET}, + /* HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR */ + {0, 3, HSMP_GET}, + /* HSMP_CLIENT_SET_CORE_PSM_MARGIN */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_FAST_PPT_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_TJ_MAX */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_FIT_LIMIT_SCALAR */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_ENABLE_OVERCLOCKING */ + {0, 0, HSMP_SET}, + /* HSMP_CLIENT_DISABLE_OVERCLOCKING */ + {0, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_OVERCLOCK_VID */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_GET_CORE_PERF_ORDER */ + {1, 1, HSMP_GET}, + /* HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_SLOW_PPT_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_FAST_SPM_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_SLOW_SPM_LIMIT */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_GET_CORE_PSM_MARGIN */ + {1, 1, HSMP_GET}, + /* HSMP_CLIENT_GET_GFX_PSM_MARGIN */ + {0, 1, HSMP_GET}, + /* HSMP_CLIENT_SPARE_0X1D */ + {0, 0, HSMP_RSVD}, + /* HSMP_CLIENT_SPARE_0X1E */ + {0, 0, HSMP_RSVD}, + /* HSMP_CLIENT_SPARE_0X1F */ + {0, 0, HSMP_RSVD}, + /* HSMP_CLIENT_SPARE_0X20 */ + {0, 0, HSMP_RSVD}, + /* HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE */ + {0, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_GFX_PSM_MARGIN */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_CCLK_FMAX_OFFSET */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX */ + {1, 0, HSMP_SET}, + /* HSMP_CLIENT_SET_GFXCLK_FMAX */ + {1, 0, HSMP_SET}, +}; + #endif /*_ASM_X86_AMD_HSMP_H_*/ diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 5e123a4ecea9..9b65dbd47965 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -45,7 +45,67 @@ */ #define CHECK_GET_BIT BIT(31) -static struct hsmp_plat_device hsmp_pdev; +static const struct hsmp_mbaddr_info hsmp_mbinfo_server = { + .base_addr = SMN_HSMP_BASE, + .msg_id_off = SMN_HSMP_MSG_ID, + .msg_resp_off = SMN_HSMP_MSG_RESP, + .msg_arg_off = SMN_HSMP_MSG_DATA, +}; + +/* RM: Ryzen Master, the SMC interface these client addresses belong to */ +static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { + .base_addr = SMN_HSMP_BASE_RM, + .msg_id_off = SMN_HSMP_MSG_ID_RM, + .msg_resp_off = SMN_HSMP_MSG_RESP_RM, + .msg_arg_off = SMN_HSMP_MSG_DATA_RM, +}; + +/* + * The descriptors carry only the message IDs the driver issues itself, which + * are also the only ones the two message sets have in common. Messages named + * by userspace in an ioctl need no entry here, as that path already resolves + * the ID against the running platform's table. + */ +static const struct hsmp_plat_desc hsmp_desc_server = { + .mbinfo = &hsmp_mbinfo_server, + .msg_desc = hsmp_msg_desc_table, + .num_msgs = HSMP_MSG_ID_MAX, + .test_msg = HSMP_TEST, + .proto_ver_msg = HSMP_GET_PROTO_VER, +}; + +/* The client drives a different mailbox with the Ryzen Master SMC message set */ +static const struct hsmp_plat_desc hsmp_desc_client = { + .mbinfo = &hsmp_mbinfo_client, + .msg_desc = hsmp_client_msg_desc_table, + .num_msgs = HSMP_CLIENT_MSG_ID_MAX, + .test_msg = HSMP_CLIENT_TEST, + .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, +}; + +static struct hsmp_plat_device hsmp_pdev = { + /* + * Overridden for the client in get_hsmp_pdev(), which every driver + * calls before it touches a socket. Defaulting it here rather than + * leaving it NULL keeps the message plumbing safe for any caller that + * arrives without going through there. + */ + .desc = &hsmp_desc_server, +}; + +/* + * Callers must bounds-check msg_id against desc->num_msgs first; this returns + * NULL rather than indexing out of range if they did not. + */ +static inline const struct hsmp_msg_desc *get_msg_desc(u32 msg_id) +{ + const struct hsmp_plat_desc *desc = hsmp_pdev.desc; + + if (msg_id >= desc->num_msgs) + return NULL; + + return &desc->msg_desc[msg_id]; +} /* * Gates the AMD HSMP data plane against socket bring-up and teardown. @@ -184,30 +244,37 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms static int validate_message(struct hsmp_message *msg) { + const struct hsmp_msg_desc *desc; + /* msg_id against valid range of message IDs */ - if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) + if (msg->msg_id < hsmp_pdev.desc->test_msg || + msg->msg_id >= hsmp_pdev.desc->num_msgs) + return -ENOMSG; + + desc = get_msg_desc(msg->msg_id); + if (!desc) return -ENOMSG; /* msg_id is a reserved message ID */ - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) + if (desc->type == HSMP_RSVD) return -ENOMSG; /* * num_args passed by user should match the num_args specified in * message description table. */ - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) + if (msg->num_args != desc->num_args) return -EINVAL; /* * As the HSMP protocol evolves, newer platforms may define more * response arguments for existing messages. Use an upper-bound * check so that older userspace callers requesting fewer response - * words than what the current hsmp_msg_desc_table[] defines are - * still accepted, while rejecting requests that exceed the - * hardware capability. + * words than what the current descriptor table defines are still + * accepted, while rejecting requests that exceed the hardware + * capability. */ - if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) + if (msg->response_sz > desc->response_sz) return -EINVAL; return 0; @@ -316,7 +383,7 @@ int hsmp_test(u16 sock_ind, u32 value) * Test the hsmp port by performing TEST command. The test message * takes one argument and returns the value of that argument + 1. */ - msg.msg_id = HSMP_TEST; + msg.msg_id = hsmp_pdev.desc->test_msg; msg.num_args = 1; msg.response_sz = 1; msg.args[0] = value; @@ -340,10 +407,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); static bool is_get_msg(struct hsmp_message *msg) { - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) + const struct hsmp_msg_desc *desc = get_msg_desc(msg->msg_id); + + if (!desc) + return false; + + if (desc->type == HSMP_GET) return true; - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && + if (desc->type == HSMP_SET_GET && (msg->args[0] & CHECK_GET_BIT)) return true; @@ -354,6 +426,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) { int __user *arguser = (int __user *)arg; struct hsmp_message msg = { 0 }; + const struct hsmp_msg_desc *desc; int ret; if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct hsmp_message))) @@ -361,23 +434,28 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) /* * Check msg_id is within the range of supported msg ids - * i.e within the array bounds of hsmp_msg_desc_table + * i.e within the array bounds of the platform's descriptor table */ - if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) + if (msg.msg_id < hsmp_pdev.desc->test_msg || + msg.msg_id >= hsmp_pdev.desc->num_msgs) return -ENOMSG; /* * Sanitize the user-controlled msg_id against speculative * execution. The bounds check above retires the out-of-range * case with -ENOMSG, but a mispredicted branch can still let the - * CPU speculatively use msg_id as an index into - * hsmp_msg_desc_table[] (here and in validate_message() / + * CPU speculatively use msg_id as an index into the message + * descriptor tables (here and in validate_message() / * is_get_msg() called downstream via hsmp_send_message()), and * pull arbitrary kernel memory into the cache (Spectre v1, * CVE-2017-5753). Clamp once into msg.msg_id so every downstream * dereference sees the sanitized value. */ - msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); + msg.msg_id = array_index_nospec(msg.msg_id, hsmp_pdev.desc->num_msgs); + + desc = get_msg_desc(msg.msg_id); + if (!desc || desc->type == HSMP_RSVD) + return -ENOMSG; switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { case FMODE_WRITE: @@ -410,7 +488,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) if (ret) return ret; - if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { + if (desc->response_sz > 0) { /* Copy results back to user for get/monitor commands */ if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message))) return -EFAULT; @@ -687,9 +765,9 @@ int hsmp_cache_proto_ver(u16 sock_ind) struct hsmp_message msg = { 0 }; int ret; - msg.msg_id = HSMP_GET_PROTO_VER; + msg.msg_id = hsmp_pdev.desc->proto_ver_msg; msg.sock_ind = sock_ind; - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; ret = hsmp_send_message_locked(&msg); if (!ret) @@ -735,6 +813,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, "AMD_HSMP"); struct hsmp_plat_device *get_hsmp_pdev(void) { + /* + * Both drivers call this before they bring any socket up, and the CPU + * this selects on is fixed long before either can load, so resolving + * the platform description here is enough to have it in place for + * every mailbox access and for the probe paths themselves. + */ + if (is_client_platform()) + hsmp_pdev.desc = &hsmp_desc_client; + return &hsmp_pdev; } EXPORT_SYMBOL_NS_GPL(get_hsmp_pdev, "AMD_HSMP"); diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h index 8dbff16a87b1..b4bd9800dee1 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.h +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -10,6 +10,8 @@ #ifndef HSMP_H #define HSMP_H +#include <asm/amd/hsmp.h> + #include <linux/compiler_types.h> #include <linux/device.h> #include <linux/hwmon.h> @@ -17,6 +19,7 @@ #include <linux/miscdevice.h> #include <linux/mutex.h> #include <linux/pci.h> +#include <linux/processor.h> #include <linux/rwsem.h> #include <linux/semaphore.h> #include <linux/sysfs.h> @@ -32,6 +35,51 @@ #define DRIVER_VERSION "2.6" +/* + * To access a specific HSMP mailbox register, s/w writes the SMN address of the + * HSMP mailbox register into the SMN_INDEX register, and reads/writes the + * SMN_DATA reg. Below are the SMN addresses of the HSMP mailbox registers in + * SMU address space, as consumed by the fixed mailbox descriptors in hsmp.c. + * + * The server set is used by the non-ACPI path only; the ACPI path discovers the + * equivalent values from _CRS/_DSD. The _RM set is the client one, named for + * the Ryzen Master SMC (RMSMC) interface whose mailbox it addresses, and is + * used by both paths because the client exposes no _CRS/_DSD to discover them + * from. The two sets share a base and differ only in the register offsets + * behind it, but are kept separate so each stays tied to the platform it was + * documented for. + */ +#define SMN_HSMP_BASE 0x03B00000 +#define SMN_HSMP_MSG_ID 0x00010534 +#define SMN_HSMP_MSG_ID_F1A_M0H 0x00010934 +#define SMN_HSMP_MSG_RESP 0x00010980 +#define SMN_HSMP_MSG_DATA 0x000109E0 + +/* _RM: Ryzen Master SMC, the client mailbox */ +#define SMN_HSMP_BASE_RM 0x03B00000 +#define SMN_HSMP_MSG_ID_RM 0x00010A8C +#define SMN_HSMP_MSG_RESP_RM 0x00010A90 +#define SMN_HSMP_MSG_DATA_RM 0x00010A94 + +/* + * Family 1Ah client platforms use the Ryzen Master SMC (client) mailbox and + * message set instead of the server HSMP interface: + * + * Models 80h-87h Medusa1 + * Models 88h-8Fh Olympic Ridge + * Models E0h-E3h Medusa2 + * + * They all share the SMN_HSMP_*_RM mailbox addresses above and the same + * message set, so one predicate covers them and the two adjacent Model 8xh + * ranges are tested as one. + */ +static inline bool is_client_platform(void) +{ + return boot_cpu_data.x86 == 0x1A && + ((boot_cpu_data.x86_model >= 0x80 && boot_cpu_data.x86_model <= 0x8F) || + (boot_cpu_data.x86_model >= 0xE0 && boot_cpu_data.x86_model <= 0xE3)); +} + struct hsmp_mbaddr_info { u32 base_addr; u32 msg_id_off; @@ -40,6 +88,30 @@ struct hsmp_mbaddr_info { u32 size; }; +/* + * Per-platform description of the mailbox and the message set behind it. + * Resolved once from the running CPU and reached through hsmp_pdev->desc, so + * neither the message plumbing nor the probe paths have to re-derive which + * platform they are on. + * + * @mbinfo: fixed SMN mailbox addresses. The ACPI path uses these + * only where firmware exposes no _CRS/_DSD to read them + * from, i.e. on the client. + * @msg_desc: message descriptor table, indexed by message ID. + * @num_msgs: number of entries in @msg_desc, and so the exclusive + * upper bound on a valid message ID. + * @test_msg: no-op message used to probe the mailbox. Also the + * lowest valid message ID, as ID 0 is reserved. + * @proto_ver_msg: returns the interface version. + */ +struct hsmp_plat_desc { + const struct hsmp_mbaddr_info *mbinfo; + const struct hsmp_msg_desc *msg_desc; + u32 num_msgs; + u32 test_msg; + u32 proto_ver_msg; +}; + struct hsmp_socket { struct bin_attribute hsmp_attr; struct hsmp_mbaddr_info mbinfo; @@ -59,6 +131,7 @@ struct hsmp_socket { struct hsmp_plat_device { struct miscdevice mdev; struct hsmp_socket *sock; + const struct hsmp_plat_desc *desc; u32 proto_ver; u16 num_sockets; }; diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index e9b2b809c0f5..7423c98a7b3e 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -29,17 +29,6 @@ #define DRIVER_NAME "amd_hsmp" -/* - * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox - * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg. - * Below are required SMN address for HSMP Mailbox register offsets in SMU address space - */ -#define SMN_HSMP_BASE 0x3B00000 -#define SMN_HSMP_MSG_ID 0x0010534 -#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 -#define SMN_HSMP_MSG_RESP 0x0010980 -#define SMN_HSMP_MSG_DATA 0x00109E0 - static struct hsmp_plat_device *hsmp_pdev; static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, @@ -157,20 +146,18 @@ static int init_platform_device(struct device *dev) sock = &hsmp_pdev->sock[i]; sock->sock_ind = i; sock->dev = dev; - sock->mbinfo.base_addr = SMN_HSMP_BASE; sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; + sock->mbinfo = *hsmp_pdev->desc->mbinfo; /* * This is a transitional change from non-ACPI to ACPI, only * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI. + * It is the one part whose message-id register sits at a + * different offset on this path than the descriptor gives. */ if (is_f1a_m0h()) sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; - else - sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; - sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; - sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; sema_init(&sock->hsmp_sem, 1); /* Test the hsmp interface on each socket */ @@ -184,8 +171,18 @@ static int init_platform_device(struct device *dev) ret = hsmp_cache_proto_ver(i); if (ret) { - dev_err(dev, "Failed to read HSMP protocol version\n"); - return ret; + /* + * Some client SMU builds reject the interface-version + * query with "invalid message" even though the mailbox + * is functional, as the test message above just + * showed. Keep going there instead of failing the + * probe: proto_ver only gates server-side features. + */ + if (!is_client_platform()) { + dev_err(dev, "Failed to read HSMP protocol version\n"); + return ret; + } + dev_warn(dev, "Interface version query unsupported on client SMU; continuing\n"); } if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { @@ -317,6 +314,12 @@ static bool legacy_hsmp_support(void) case 0x1A: switch (boot_cpu_data.x86_model) { case 0x00 ... 0x0F: + /* + * Client platforms: 80h-87h (Medusa1), 88h-8Fh (Olympic + * Ridge), E0h-E3h (Medusa2) + */ + case 0x80 ... 0x8F: + case 0xE0 ... 0xE3: return true; default: return false; -- 2.34.1 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah 2026-07-29 16:40 ` [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah Muralidhara M K @ 2026-07-29 21:27 ` Mario Limonciello 2026-07-30 9:37 ` M K, Muralidhara 2026-07-30 16:43 ` Armin Wolf 1 sibling, 1 reply; 22+ messages in thread From: Mario Limonciello @ 2026-07-29 21:27 UTC (permalink / raw) To: Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/29/26 11:40, Muralidhara M K wrote: > Add non-ACPI HSMP mailbox support, Ryzen Master SMC client message > descriptors, and platform routing for the Family 1Ah client platforms: > > Models 80h-87h Medusa1 > Models 88h-8Fh Olympic Ridge > Models E0h-E3h Medusa2 > > All three share the same client mailbox addresses and message set, so a > single is_client_platform() predicate covers them, and the two adjacent > Model 8xh ranges are tested as one. > > The client parts drive a different mailbox from the server HSMP > interface. They expose no _DSD/_CRS to discover the mailbox, so the SMN > addresses are fixed in hsmp.h and shared by the platform and ACPI > probes, and they speak the Ryzen Master SMC (RMSMC) message set rather > than the server message IDs. Add that message set and its descriptor > table to the UAPI header. > > Two message descriptor tables now exist, so every lookup has to pick the > one matching the running platform. Rather than test the platform at each > lookup, describe the platform once: struct hsmp_plat_desc holds the > mailbox addresses, the descriptor table and its size, and the IDs of the > messages the driver issues itself. get_hsmp_pdev() resolves it, which > both drivers call before they bring any socket up and which runs on a > CPU that is fixed long before either can load. The server descriptor is > also the static initialiser, so the message plumbing cannot reach a NULL > descriptor even if a future caller arrives another way. > > get_msg_desc() then indexes the table behind that pointer, and > validate_message(), is_get_msg(), hsmp_ioctl_msg(), hsmp_test() and > hsmp_cache_proto_ver() read the bound and the message IDs from the > descriptor. get_msg_desc() returns NULL rather than indexing out of > range if a caller skipped the bounds check, and the Spectre v1 clamp in > hsmp_ioctl_msg() uses the running platform's table size. > > In init_platform_device() the mailbox setup becomes a copy of the > descriptor's addresses. The F1Ah M0h message-id offset stays behind > is_f1a_m0h() as an override, because it applies only to this non-ACPI > path while the descriptor is shared with the ACPI one. It cannot disturb > the client mailbox: it requires model <= 0x0F and the client models are > all >= 0x80. The server mailbox addresses move from plat.c to hsmp.h so > both address sets sit with the descriptors that consume them; the values > are unchanged, written to the same width as the client set they now sit > beside. > > Since this is the change that lets the platform driver match the client > models, it also has to let the client probe survive the interface-version > query. Some client SMU builds reject HSMP_CLIENT_GET_INTERFACE_VER with > "invalid message" even though the mailbox works, as the test message > issued just before it shows, so treat that failure as non-fatal on the > client and register the socket anyway. Nothing on the client depends on > the reported version: hsmp_pdev->proto_ver gates the metrics_bin > attribute and the protocol_version sysfs file, both of which describe > the server interface. The query stays fatal on server parts. > > The client descriptor table carries __attribute__((unused)), matching > hsmp_msg_desc_table[]. Both are defined in a UAPI header that several > translation units include without using the table, so without it > -Wunused-const-variable fires on plat.c, acpi.c and hwmon.c. > > Document the client platforms in Documentation/arch/x86/amd_hsmp.rst, > which until now described HSMP as available only on the EPYC server > parts and MI300A. The character device and ioctl interface are unchanged > there; what differs is the accepted message IDs, so the text points at > the HSMP_CLIENT_* set in the UAPI header. > > Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> Generic comment for the series. With such rich function comments, I think a lot of this would be really valuable to have as kdoc (/** */) and then reference in hsmp documentation. > --- > Documentation/arch/x86/amd_hsmp.rst | 18 +++- > arch/x86/include/uapi/asm/amd_hsmp.h | 137 +++++++++++++++++++++++++++ > drivers/platform/x86/amd/hsmp/hsmp.c | 125 ++++++++++++++++++++---- > drivers/platform/x86/amd/hsmp/hsmp.h | 73 ++++++++++++++ > drivers/platform/x86/amd/hsmp/plat.c | 39 ++++---- > 5 files changed, 354 insertions(+), 38 deletions(-) > > diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst > index fa1fc240e212..8a9d6fe7d4cf 100644 > --- a/Documentation/arch/x86/amd_hsmp.rst > +++ b/Documentation/arch/x86/amd_hsmp.rst > @@ -8,6 +8,21 @@ Newer Fam19h(model 0x00-0x1f, 0x30-0x3f, 0x90-0x9f, 0xa0-0xaf), > Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD support > system management functionality via HSMP (Host System Management Port). > > +The Fam1Ah client platforms are supported as well: > + > + ============= ============= > + Model Platform > + ============= ============= > + 0x80-0x87 Medusa1 > + 0x88-0x8f Olympic Ridge > + 0xe0-0xe3 Medusa2 > + ============= ============= > + > +They share one mailbox and speak the Ryzen Master SMC message set instead > +of the server HSMP message set, so the message IDs accepted on them are > +the HSMP_CLIENT_* ones listed in arch/x86/include/uapi/asm/amd_hsmp.h. > +The character device and ioctl interface described below are the same. > + > The Host System Management Port (HSMP) is an interface to provide > OS-level software with access to system management functions via a > set of mailbox registers. > @@ -17,7 +32,8 @@ More details on the interface can be found in chapter > Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50 > > > -HSMP interface is supported on EPYC line of server CPUs and MI300A (APU). > +HSMP interface is supported on EPYC line of server CPUs, MI300A (APU) and > +the Fam1Ah client platforms listed above. > > > HSMP device > diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h > index eda336bfd3e9..b4c3ddb9d1c1 100644 > --- a/arch/x86/include/uapi/asm/amd_hsmp.h > +++ b/arch/x86/include/uapi/asm/amd_hsmp.h > @@ -664,4 +664,141 @@ struct hsmp_telemetry_data { > #define HSMP_IOCTL_GET_TELEMETRY_DATA \ > _IOW(HSMP_BASE_IOCTL_NR, 1, struct hsmp_telemetry_data) > > +/* > + * Client HSMP messages supported on the Family 1Ah client platforms: > + * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and > + * Models E0h-E3h (Medusa2). > + * These map to Ryzen Master SMC (RMSMC) message IDs. > + */ > +enum hsmp_client_message_ids { > + HSMP_CLIENT_TEST = 1, /* 01h Test message */ > + HSMP_CLIENT_GET_SMU_VER, /* 02h SMU FW version */ > + HSMP_CLIENT_GET_INTERFACE_VER, /* 03h Interface version */ > + HSMP_CLIENT_GET_METRICS_TABLE_VER, /* 04h Metrics table version */ > + HSMP_CLIENT_GET_METRICS_TABLE, /* 05h Metrics table log sample */ > + HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR,/* 06h Metrics table DRAM address */ > + HSMP_CLIENT_SET_CORE_PSM_MARGIN, /* 07h Set core PSM margin */ > + HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN, /* 08h Set all core PSM margin */ > + HSMP_CLIENT_SET_FAST_PPT_LIMIT, /* 09h Set fast PPT limit */ > + HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT, /* 0Ah Set VRM VDD current limit */ > + HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT,/* 0Bh Set VRM VDD max current limit */ > + HSMP_CLIENT_SET_TJ_MAX, /* 0Ch Set TjMax */ > + HSMP_CLIENT_SET_FIT_LIMIT_SCALAR, /* 0Dh Set FIT limit scalar */ > + HSMP_CLIENT_ENABLE_OVERCLOCKING, /* 0Eh Enable overclocking */ > + HSMP_CLIENT_DISABLE_OVERCLOCKING, /* 0Fh Disable overclocking */ > + HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES,/* 10h Set OC freq all cores */ > + HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE,/* 11h Set OC freq per core */ > + HSMP_CLIENT_SET_OVERCLOCK_VID, /* 12h Set OC VID */ > + HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY,/* 13h Set FCLK OC on-the-fly */ > + HSMP_CLIENT_GET_CORE_PERF_ORDER, /* 14h Get core performance order */ > + HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT, /* 15h Set sustained power limit */ > + HSMP_CLIENT_SET_SLOW_PPT_LIMIT, /* 16h Set slow PPT limit */ > + HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT,/* 17h Set VRM GFX max current limit */ > + HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT, /* 18h Set VRM SOC current limit */ > + HSMP_CLIENT_SET_FAST_SPM_LIMIT, /* 19h Set fast SPM limit */ > + HSMP_CLIENT_SET_SLOW_SPM_LIMIT, /* 1Ah Set slow SPM limit */ > + HSMP_CLIENT_GET_CORE_PSM_MARGIN, /* 1Bh Get core PSM margin */ > + HSMP_CLIENT_GET_GFX_PSM_MARGIN, /* 1Ch Get GFX PSM margin */ > + HSMP_CLIENT_SPARE_0X1D, /* 1Dh Spare */ > + HSMP_CLIENT_SPARE_0X1E, /* 1Eh Spare */ > + HSMP_CLIENT_SPARE_0X1F, /* 1Fh Spare */ > + HSMP_CLIENT_SPARE_0X20, /* 20h Spare */ > + HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID,/* 21h Set GfxClk overdrive by freq/VID */ > + HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE, /* 22h Disable GfxClk overdrive */ > + HSMP_CLIENT_SET_GFX_PSM_MARGIN, /* 23h Set GFX PSM margin */ > + HSMP_CLIENT_SET_CCLK_FMAX_OFFSET, /* 24h Set CCLK Fmax offset */ > + HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET,/* 25h Set core power limit offset */ > + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND, /* 26h Add extra PSM guardband */ > + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX,/* 27h Add extra PSM guardband GFX */ > + HSMP_CLIENT_SET_GFXCLK_FMAX, /* 28h Set GfxClk Fmax */ > + HSMP_CLIENT_MSG_ID_MAX, > +}; > + > +/* Client message descriptor table (Family 1Ah Models 80h-8Fh, E0h-E3h) */ > +static const struct hsmp_msg_desc hsmp_client_msg_desc_table[] > + __attribute__((unused)) = { > + /* RESERVED */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_TEST */ > + {1, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_SMU_VER */ > + {0, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_INTERFACE_VER */ > + {0, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_METRICS_TABLE_VER */ > + {0, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_METRICS_TABLE */ > + {0, 0, HSMP_GET}, > + /* HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR */ > + {0, 3, HSMP_GET}, > + /* HSMP_CLIENT_SET_CORE_PSM_MARGIN */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_FAST_PPT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_TJ_MAX */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_FIT_LIMIT_SCALAR */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_ENABLE_OVERCLOCKING */ > + {0, 0, HSMP_SET}, > + /* HSMP_CLIENT_DISABLE_OVERCLOCKING */ > + {0, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_OVERCLOCK_VID */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_GET_CORE_PERF_ORDER */ > + {1, 1, HSMP_GET}, > + /* HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_SLOW_PPT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_FAST_SPM_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_SLOW_SPM_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_GET_CORE_PSM_MARGIN */ > + {1, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_GFX_PSM_MARGIN */ > + {0, 1, HSMP_GET}, > + /* HSMP_CLIENT_SPARE_0X1D */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_SPARE_0X1E */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_SPARE_0X1F */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_SPARE_0X20 */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE */ > + {0, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_GFX_PSM_MARGIN */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_CCLK_FMAX_OFFSET */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_GFXCLK_FMAX */ > + {1, 0, HSMP_SET}, > +}; How would you feel about adding a static (compile time) assert that the number of entries here matched the number of enums? This can avoid making a mistake later. > + > #endif /*_ASM_X86_AMD_HSMP_H_*/ > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c > index 5e123a4ecea9..9b65dbd47965 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.c > +++ b/drivers/platform/x86/amd/hsmp/hsmp.c > @@ -45,7 +45,67 @@ > */ > #define CHECK_GET_BIT BIT(31) > > -static struct hsmp_plat_device hsmp_pdev; > +static const struct hsmp_mbaddr_info hsmp_mbinfo_server = { > + .base_addr = SMN_HSMP_BASE, > + .msg_id_off = SMN_HSMP_MSG_ID, > + .msg_resp_off = SMN_HSMP_MSG_RESP, > + .msg_arg_off = SMN_HSMP_MSG_DATA, > +}; > + > +/* RM: Ryzen Master, the SMC interface these client addresses belong to */ > +static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { > + .base_addr = SMN_HSMP_BASE_RM, > + .msg_id_off = SMN_HSMP_MSG_ID_RM, > + .msg_resp_off = SMN_HSMP_MSG_RESP_RM, > + .msg_arg_off = SMN_HSMP_MSG_DATA_RM, > +}; > + > +/* > + * The descriptors carry only the message IDs the driver issues itself, which > + * are also the only ones the two message sets have in common. Messages named > + * by userspace in an ioctl need no entry here, as that path already resolves > + * the ID against the running platform's table. > + */ > +static const struct hsmp_plat_desc hsmp_desc_server = { > + .mbinfo = &hsmp_mbinfo_server, > + .msg_desc = hsmp_msg_desc_table, > + .num_msgs = HSMP_MSG_ID_MAX, > + .test_msg = HSMP_TEST, > + .proto_ver_msg = HSMP_GET_PROTO_VER, > +}; > + > +/* The client drives a different mailbox with the Ryzen Master SMC message set */ > +static const struct hsmp_plat_desc hsmp_desc_client = { > + .mbinfo = &hsmp_mbinfo_client, > + .msg_desc = hsmp_client_msg_desc_table, > + .num_msgs = HSMP_CLIENT_MSG_ID_MAX, > + .test_msg = HSMP_CLIENT_TEST, > + .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, > +}; > + > +static struct hsmp_plat_device hsmp_pdev = { > + /* > + * Overridden for the client in get_hsmp_pdev(), which every driver > + * calls before it touches a socket. Defaulting it here rather than > + * leaving it NULL keeps the message plumbing safe for any caller that > + * arrives without going through there. > + */ > + .desc = &hsmp_desc_server, > +}; > + > +/* > + * Callers must bounds-check msg_id against desc->num_msgs first; this returns > + * NULL rather than indexing out of range if they did not. > + */ > +static inline const struct hsmp_msg_desc *get_msg_desc(u32 msg_id) > +{ > + const struct hsmp_plat_desc *desc = hsmp_pdev.desc; > + > + if (msg_id >= desc->num_msgs) > + return NULL; > + > + return &desc->msg_desc[msg_id]; > +} > > /* > * Gates the AMD HSMP data plane against socket bring-up and teardown. > @@ -184,30 +244,37 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms > > static int validate_message(struct hsmp_message *msg) > { > + const struct hsmp_msg_desc *desc; > + > /* msg_id against valid range of message IDs */ > - if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) > + if (msg->msg_id < hsmp_pdev.desc->test_msg || > + msg->msg_id >= hsmp_pdev.desc->num_msgs) > + return -ENOMSG; > + > + desc = get_msg_desc(msg->msg_id); > + if (!desc) > return -ENOMSG; > > /* msg_id is a reserved message ID */ > - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) > + if (desc->type == HSMP_RSVD) > return -ENOMSG; > > /* > * num_args passed by user should match the num_args specified in > * message description table. > */ > - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) > + if (msg->num_args != desc->num_args) > return -EINVAL; > > /* > * As the HSMP protocol evolves, newer platforms may define more > * response arguments for existing messages. Use an upper-bound > * check so that older userspace callers requesting fewer response > - * words than what the current hsmp_msg_desc_table[] defines are > - * still accepted, while rejecting requests that exceed the > - * hardware capability. > + * words than what the current descriptor table defines are still > + * accepted, while rejecting requests that exceed the hardware > + * capability. > */ > - if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) > + if (msg->response_sz > desc->response_sz) > return -EINVAL; > > return 0; > @@ -316,7 +383,7 @@ int hsmp_test(u16 sock_ind, u32 value) > * Test the hsmp port by performing TEST command. The test message > * takes one argument and returns the value of that argument + 1. > */ > - msg.msg_id = HSMP_TEST; > + msg.msg_id = hsmp_pdev.desc->test_msg; > msg.num_args = 1; > msg.response_sz = 1; > msg.args[0] = value; > @@ -340,10 +407,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); > > static bool is_get_msg(struct hsmp_message *msg) > { > - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) > + const struct hsmp_msg_desc *desc = get_msg_desc(msg->msg_id); > + > + if (!desc) > + return false; > + > + if (desc->type == HSMP_GET) > return true; > > - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && > + if (desc->type == HSMP_SET_GET && > (msg->args[0] & CHECK_GET_BIT)) > return true; > > @@ -354,6 +426,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) > { > int __user *arguser = (int __user *)arg; > struct hsmp_message msg = { 0 }; > + const struct hsmp_msg_desc *desc; > int ret; > > if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct hsmp_message))) > @@ -361,23 +434,28 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) > > /* > * Check msg_id is within the range of supported msg ids > - * i.e within the array bounds of hsmp_msg_desc_table > + * i.e within the array bounds of the platform's descriptor table > */ > - if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) > + if (msg.msg_id < hsmp_pdev.desc->test_msg || > + msg.msg_id >= hsmp_pdev.desc->num_msgs) > return -ENOMSG; > > /* > * Sanitize the user-controlled msg_id against speculative > * execution. The bounds check above retires the out-of-range > * case with -ENOMSG, but a mispredicted branch can still let the > - * CPU speculatively use msg_id as an index into > - * hsmp_msg_desc_table[] (here and in validate_message() / > + * CPU speculatively use msg_id as an index into the message > + * descriptor tables (here and in validate_message() / > * is_get_msg() called downstream via hsmp_send_message()), and > * pull arbitrary kernel memory into the cache (Spectre v1, > * CVE-2017-5753). Clamp once into msg.msg_id so every downstream > * dereference sees the sanitized value. > */ > - msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); > + msg.msg_id = array_index_nospec(msg.msg_id, hsmp_pdev.desc->num_msgs); > + > + desc = get_msg_desc(msg.msg_id); > + if (!desc || desc->type == HSMP_RSVD) > + return -ENOMSG; > > switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { > case FMODE_WRITE: > @@ -410,7 +488,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) > if (ret) > return ret; > > - if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { > + if (desc->response_sz > 0) { > /* Copy results back to user for get/monitor commands */ > if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message))) > return -EFAULT; > @@ -687,9 +765,9 @@ int hsmp_cache_proto_ver(u16 sock_ind) > struct hsmp_message msg = { 0 }; > int ret; > > - msg.msg_id = HSMP_GET_PROTO_VER; > + msg.msg_id = hsmp_pdev.desc->proto_ver_msg; > msg.sock_ind = sock_ind; > - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; > + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; > > ret = hsmp_send_message_locked(&msg); > if (!ret) > @@ -735,6 +813,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, "AMD_HSMP"); > > struct hsmp_plat_device *get_hsmp_pdev(void) > { > + /* > + * Both drivers call this before they bring any socket up, and the CPU > + * this selects on is fixed long before either can load, so resolving > + * the platform description here is enough to have it in place for > + * every mailbox access and for the probe paths themselves. > + */ > + if (is_client_platform()) > + hsmp_pdev.desc = &hsmp_desc_client; > + > return &hsmp_pdev; > } > EXPORT_SYMBOL_NS_GPL(get_hsmp_pdev, "AMD_HSMP"); > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h > index 8dbff16a87b1..b4bd9800dee1 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.h > +++ b/drivers/platform/x86/amd/hsmp/hsmp.h > @@ -10,6 +10,8 @@ > #ifndef HSMP_H > #define HSMP_H > > +#include <asm/amd/hsmp.h> > + > #include <linux/compiler_types.h> > #include <linux/device.h> > #include <linux/hwmon.h> > @@ -17,6 +19,7 @@ > #include <linux/miscdevice.h> > #include <linux/mutex.h> > #include <linux/pci.h> > +#include <linux/processor.h> > #include <linux/rwsem.h> > #include <linux/semaphore.h> > #include <linux/sysfs.h> > @@ -32,6 +35,51 @@ > > #define DRIVER_VERSION "2.6" You're adding new IOCTL's in patches 5 and 6, does this version need to bump for userspace to discover? > > +/* > + * To access a specific HSMP mailbox register, s/w writes the SMN address of the > + * HSMP mailbox register into the SMN_INDEX register, and reads/writes the > + * SMN_DATA reg. Below are the SMN addresses of the HSMP mailbox registers in > + * SMU address space, as consumed by the fixed mailbox descriptors in hsmp.c. > + * > + * The server set is used by the non-ACPI path only; the ACPI path discovers the > + * equivalent values from _CRS/_DSD. The _RM set is the client one, named for > + * the Ryzen Master SMC (RMSMC) interface whose mailbox it addresses, and is > + * used by both paths because the client exposes no _CRS/_DSD to discover them > + * from. The two sets share a base and differ only in the register offsets > + * behind it, but are kept separate so each stays tied to the platform it was > + * documented for. > + */ > +#define SMN_HSMP_BASE 0x03B00000 > +#define SMN_HSMP_MSG_ID 0x00010534 > +#define SMN_HSMP_MSG_ID_F1A_M0H 0x00010934 > +#define SMN_HSMP_MSG_RESP 0x00010980 > +#define SMN_HSMP_MSG_DATA 0x000109E0 > + > +/* _RM: Ryzen Master SMC, the client mailbox */ > +#define SMN_HSMP_BASE_RM 0x03B00000 > +#define SMN_HSMP_MSG_ID_RM 0x00010A8C > +#define SMN_HSMP_MSG_RESP_RM 0x00010A90 > +#define SMN_HSMP_MSG_DATA_RM 0x00010A94 Although these are the same for these 3 platforms, they might not be the same for the follow on ones. Have you seen the work that's going into the AMD_PMF driver to build structs of register lists? Take a look at this. https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/commit/?h=review-ilpo-next&id=e860e561926bf75e323d43fe59a9bf25d2ef33a3 > + > +/* > + * Family 1Ah client platforms use the Ryzen Master SMC (client) mailbox and > + * message set instead of the server HSMP interface: > + * > + * Models 80h-87h Medusa1 > + * Models 88h-8Fh Olympic Ridge > + * Models E0h-E3h Medusa2 > + * > + * They all share the SMN_HSMP_*_RM mailbox addresses above and the same > + * message set, so one predicate covers them and the two adjacent Model 8xh > + * ranges are tested as one. > + */ > +static inline bool is_client_platform(void) > +{ > + return boot_cpu_data.x86 == 0x1A && > + ((boot_cpu_data.x86_model >= 0x80 && boot_cpu_data.x86_model <= 0x8F) || > + (boot_cpu_data.x86_model >= 0xE0 && boot_cpu_data.x86_model <= 0xE3)); > +} Rather than enumerating lists of CPU IDs, could you instead go off the ACPI ID? Or are these identical from server to client? > + > struct hsmp_mbaddr_info { > u32 base_addr; > u32 msg_id_off; > @@ -40,6 +88,30 @@ struct hsmp_mbaddr_info { > u32 size; > }; > > +/* > + * Per-platform description of the mailbox and the message set behind it. > + * Resolved once from the running CPU and reached through hsmp_pdev->desc, so > + * neither the message plumbing nor the probe paths have to re-derive which > + * platform they are on. > + * > + * @mbinfo: fixed SMN mailbox addresses. The ACPI path uses these > + * only where firmware exposes no _CRS/_DSD to read them > + * from, i.e. on the client. > + * @msg_desc: message descriptor table, indexed by message ID. > + * @num_msgs: number of entries in @msg_desc, and so the exclusive > + * upper bound on a valid message ID. > + * @test_msg: no-op message used to probe the mailbox. Also the > + * lowest valid message ID, as ID 0 is reserved. > + * @proto_ver_msg: returns the interface version. > + */ > +struct hsmp_plat_desc { > + const struct hsmp_mbaddr_info *mbinfo; > + const struct hsmp_msg_desc *msg_desc; > + u32 num_msgs; > + u32 test_msg; > + u32 proto_ver_msg; > +}; > + > struct hsmp_socket { > struct bin_attribute hsmp_attr; > struct hsmp_mbaddr_info mbinfo; > @@ -59,6 +131,7 @@ struct hsmp_socket { > struct hsmp_plat_device { > struct miscdevice mdev; > struct hsmp_socket *sock; > + const struct hsmp_plat_desc *desc; > u32 proto_ver; > u16 num_sockets; > }; > diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c > index e9b2b809c0f5..7423c98a7b3e 100644 > --- a/drivers/platform/x86/amd/hsmp/plat.c > +++ b/drivers/platform/x86/amd/hsmp/plat.c > @@ -29,17 +29,6 @@ > > #define DRIVER_NAME "amd_hsmp" > > -/* > - * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox > - * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg. > - * Below are required SMN address for HSMP Mailbox register offsets in SMU address space > - */ > -#define SMN_HSMP_BASE 0x3B00000 > -#define SMN_HSMP_MSG_ID 0x0010534 > -#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 > -#define SMN_HSMP_MSG_RESP 0x0010980 > -#define SMN_HSMP_MSG_DATA 0x00109E0 > - > static struct hsmp_plat_device *hsmp_pdev; > > static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, > @@ -157,20 +146,18 @@ static int init_platform_device(struct device *dev) > sock = &hsmp_pdev->sock[i]; > sock->sock_ind = i; > sock->dev = dev; > - sock->mbinfo.base_addr = SMN_HSMP_BASE; > sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; > + sock->mbinfo = *hsmp_pdev->desc->mbinfo; > > /* > * This is a transitional change from non-ACPI to ACPI, only > * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI. > + * It is the one part whose message-id register sits at a > + * different offset on this path than the descriptor gives. > */ > if (is_f1a_m0h()) > sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; > - else > - sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; > > - sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; > - sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; > sema_init(&sock->hsmp_sem, 1); > > /* Test the hsmp interface on each socket */ > @@ -184,8 +171,18 @@ static int init_platform_device(struct device *dev) > > ret = hsmp_cache_proto_ver(i); > if (ret) { > - dev_err(dev, "Failed to read HSMP protocol version\n"); > - return ret; > + /* > + * Some client SMU builds reject the interface-version > + * query with "invalid message" even though the mailbox > + * is functional, as the test message above just > + * showed. Keep going there instead of failing the > + * probe: proto_ver only gates server-side features. > + */ > + if (!is_client_platform()) { > + dev_err(dev, "Failed to read HSMP protocol version\n"); > + return ret; > + } > + dev_warn(dev, "Interface version query unsupported on client SMU; continuing\n"); Shouldn't this be fatal? Can we fix the F/W to follow the expected HSMP driver behavior? > } > > if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { > @@ -317,6 +314,12 @@ static bool legacy_hsmp_support(void) > case 0x1A: > switch (boot_cpu_data.x86_model) { > case 0x00 ... 0x0F: > + /* > + * Client platforms: 80h-87h (Medusa1), 88h-8Fh (Olympic > + * Ridge), E0h-E3h (Medusa2) > + */ > + case 0x80 ... 0x8F: > + case 0xE0 ... 0xE3: > return true; > default: > return false; ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah 2026-07-29 21:27 ` Mario Limonciello @ 2026-07-30 9:37 ` M K, Muralidhara 0 siblings, 0 replies; 22+ messages in thread From: M K, Muralidhara @ 2026-07-30 9:37 UTC (permalink / raw) To: Mario Limonciello, Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/30/2026 2:57 AM, Mario Limonciello wrote: > > > On 7/29/26 11:40, Muralidhara M K wrote: >> Add non-ACPI HSMP mailbox support, Ryzen Master SMC client message >> descriptors, and platform routing for the Family 1Ah client platforms: >> >> Models 80h-87h Medusa1 >> Models 88h-8Fh Olympic Ridge >> Models E0h-E3h Medusa2 >> >> All three share the same client mailbox addresses and message set, so a >> single is_client_platform() predicate covers them, and the two adjacent >> Model 8xh ranges are tested as one. >> >> The client parts drive a different mailbox from the server HSMP >> interface. They expose no _DSD/_CRS to discover the mailbox, so the SMN >> addresses are fixed in hsmp.h and shared by the platform and ACPI >> probes, and they speak the Ryzen Master SMC (RMSMC) message set rather >> than the server message IDs. Add that message set and its descriptor >> table to the UAPI header. >> >> Two message descriptor tables now exist, so every lookup has to pick the >> one matching the running platform. Rather than test the platform at each >> lookup, describe the platform once: struct hsmp_plat_desc holds the >> mailbox addresses, the descriptor table and its size, and the IDs of the >> messages the driver issues itself. get_hsmp_pdev() resolves it, which >> both drivers call before they bring any socket up and which runs on a >> CPU that is fixed long before either can load. The server descriptor is >> also the static initialiser, so the message plumbing cannot reach a NULL >> descriptor even if a future caller arrives another way. >> >> get_msg_desc() then indexes the table behind that pointer, and >> validate_message(), is_get_msg(), hsmp_ioctl_msg(), hsmp_test() and >> hsmp_cache_proto_ver() read the bound and the message IDs from the >> descriptor. get_msg_desc() returns NULL rather than indexing out of >> range if a caller skipped the bounds check, and the Spectre v1 clamp in >> hsmp_ioctl_msg() uses the running platform's table size. >> >> In init_platform_device() the mailbox setup becomes a copy of the >> descriptor's addresses. The F1Ah M0h message-id offset stays behind >> is_f1a_m0h() as an override, because it applies only to this non-ACPI >> path while the descriptor is shared with the ACPI one. It cannot disturb >> the client mailbox: it requires model <= 0x0F and the client models are >> all >= 0x80. The server mailbox addresses move from plat.c to hsmp.h so >> both address sets sit with the descriptors that consume them; the values >> are unchanged, written to the same width as the client set they now sit >> beside. >> >> Since this is the change that lets the platform driver match the client >> models, it also has to let the client probe survive the interface-version >> query. Some client SMU builds reject HSMP_CLIENT_GET_INTERFACE_VER with >> "invalid message" even though the mailbox works, as the test message >> issued just before it shows, so treat that failure as non-fatal on the >> client and register the socket anyway. Nothing on the client depends on >> the reported version: hsmp_pdev->proto_ver gates the metrics_bin >> attribute and the protocol_version sysfs file, both of which describe >> the server interface. The query stays fatal on server parts. >> >> The client descriptor table carries __attribute__((unused)), matching >> hsmp_msg_desc_table[]. Both are defined in a UAPI header that several >> translation units include without using the table, so without it >> -Wunused-const-variable fires on plat.c, acpi.c and hwmon.c. >> >> Document the client platforms in Documentation/arch/x86/amd_hsmp.rst, >> which until now described HSMP as available only on the EPYC server >> parts and MI300A. The character device and ioctl interface are unchanged >> there; what differs is the accepted message IDs, so the text points at >> the HSMP_CLIENT_* set in the UAPI header. >> >> Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> > > Generic comment for the series. > > With such rich function comments, I think a lot of this would be really > valuable to have as kdoc (/** */) and then reference in hsmp documentation. > Agreed. also I will make commit message precise. >> --- >> Documentation/arch/x86/amd_hsmp.rst | 18 +++- >> arch/x86/include/uapi/asm/amd_hsmp.h | 137 +++++++++++++++++++++++++++ >> drivers/platform/x86/amd/hsmp/hsmp.c | 125 ++++++++++++++++++++---- >> drivers/platform/x86/amd/hsmp/hsmp.h | 73 ++++++++++++++ >> drivers/platform/x86/amd/hsmp/plat.c | 39 ++++---- >> 5 files changed, 354 insertions(+), 38 deletions(-) >> >> diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/ >> x86/amd_hsmp.rst >> index fa1fc240e212..8a9d6fe7d4cf 100644 >> --- a/Documentation/arch/x86/amd_hsmp.rst >> +++ b/Documentation/arch/x86/amd_hsmp.rst >> @@ -8,6 +8,21 @@ Newer Fam19h(model 0x00-0x1f, 0x30-0x3f, 0x90-0x9f, >> 0xa0-0xaf), >> Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD support >> system management functionality via HSMP (Host System Management Port). >> +The Fam1Ah client platforms are supported as well: >> + >> + ============= ============= >> + Model Platform >> + ============= ============= >> + 0x80-0x87 Medusa1 >> + 0x88-0x8f Olympic Ridge >> + 0xe0-0xe3 Medusa2 >> + ============= ============= >> + >> +They share one mailbox and speak the Ryzen Master SMC message set >> instead >> +of the server HSMP message set, so the message IDs accepted on them are >> +the HSMP_CLIENT_* ones listed in arch/x86/include/uapi/asm/amd_hsmp.h. >> +The character device and ioctl interface described below are the same. >> + >> The Host System Management Port (HSMP) is an interface to provide >> OS-level software with access to system management functions via a >> set of mailbox registers. >> @@ -17,7 +32,8 @@ More details on the interface can be found in chapter >> Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50 >> -HSMP interface is supported on EPYC line of server CPUs and MI300A >> (APU). >> +HSMP interface is supported on EPYC line of server CPUs, MI300A (APU) >> and >> +the Fam1Ah client platforms listed above. >> HSMP device >> diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/ >> uapi/asm/amd_hsmp.h >> index eda336bfd3e9..b4c3ddb9d1c1 100644 >> --- a/arch/x86/include/uapi/asm/amd_hsmp.h >> +++ b/arch/x86/include/uapi/asm/amd_hsmp.h >> @@ -664,4 +664,141 @@ struct hsmp_telemetry_data { >> #define HSMP_IOCTL_GET_TELEMETRY_DATA \ >> _IOW(HSMP_BASE_IOCTL_NR, 1, struct hsmp_telemetry_data) >> +/* >> + * Client HSMP messages supported on the Family 1Ah client platforms: >> + * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and >> + * Models E0h-E3h (Medusa2). >> + * These map to Ryzen Master SMC (RMSMC) message IDs. >> + */ >> +enum hsmp_client_message_ids { >> + HSMP_CLIENT_TEST = 1, /* 01h Test message */ >> + HSMP_CLIENT_GET_SMU_VER, /* 02h SMU FW version */ >> + HSMP_CLIENT_GET_INTERFACE_VER, /* 03h Interface version */ >> + HSMP_CLIENT_GET_METRICS_TABLE_VER, /* 04h Metrics table >> version */ >> + HSMP_CLIENT_GET_METRICS_TABLE, /* 05h Metrics table log >> sample */ >> + HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR,/* 06h Metrics table DRAM >> address */ >> + HSMP_CLIENT_SET_CORE_PSM_MARGIN, /* 07h Set core PSM margin */ >> + HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN, /* 08h Set all core PSM >> margin */ >> + HSMP_CLIENT_SET_FAST_PPT_LIMIT, /* 09h Set fast PPT limit */ >> + HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT, /* 0Ah Set VRM VDD >> current limit */ >> + HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT,/* 0Bh Set VRM VDD max >> current limit */ >> + HSMP_CLIENT_SET_TJ_MAX, /* 0Ch Set TjMax */ >> + HSMP_CLIENT_SET_FIT_LIMIT_SCALAR, /* 0Dh Set FIT limit scalar */ >> + HSMP_CLIENT_ENABLE_OVERCLOCKING, /* 0Eh Enable overclocking */ >> + HSMP_CLIENT_DISABLE_OVERCLOCKING, /* 0Fh Disable overclocking */ >> + HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES,/* 10h Set OC freq all >> cores */ >> + HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE,/* 11h Set OC freq per >> core */ >> + HSMP_CLIENT_SET_OVERCLOCK_VID, /* 12h Set OC VID */ >> + HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY,/* 13h Set FCLK OC on- >> the-fly */ >> + HSMP_CLIENT_GET_CORE_PERF_ORDER, /* 14h Get core performance >> order */ >> + HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT, /* 15h Set sustained >> power limit */ >> + HSMP_CLIENT_SET_SLOW_PPT_LIMIT, /* 16h Set slow PPT limit */ >> + HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT,/* 17h Set VRM GFX max >> current limit */ >> + HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT, /* 18h Set VRM SOC >> current limit */ >> + HSMP_CLIENT_SET_FAST_SPM_LIMIT, /* 19h Set fast SPM limit */ >> + HSMP_CLIENT_SET_SLOW_SPM_LIMIT, /* 1Ah Set slow SPM limit */ >> + HSMP_CLIENT_GET_CORE_PSM_MARGIN, /* 1Bh Get core PSM margin */ >> + HSMP_CLIENT_GET_GFX_PSM_MARGIN, /* 1Ch Get GFX PSM margin */ >> + HSMP_CLIENT_SPARE_0X1D, /* 1Dh Spare */ >> + HSMP_CLIENT_SPARE_0X1E, /* 1Eh Spare */ >> + HSMP_CLIENT_SPARE_0X1F, /* 1Fh Spare */ >> + HSMP_CLIENT_SPARE_0X20, /* 20h Spare */ >> + HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID,/* 21h Set GfxClk >> overdrive by freq/VID */ >> + HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE, /* 22h Disable GfxClk >> overdrive */ >> + HSMP_CLIENT_SET_GFX_PSM_MARGIN, /* 23h Set GFX PSM margin */ >> + HSMP_CLIENT_SET_CCLK_FMAX_OFFSET, /* 24h Set CCLK Fmax offset */ >> + HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET,/* 25h Set core power >> limit offset */ >> + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND, /* 26h Add extra PSM >> guardband */ >> + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX,/* 27h Add extra PSM >> guardband GFX */ >> + HSMP_CLIENT_SET_GFXCLK_FMAX, /* 28h Set GfxClk Fmax */ >> + HSMP_CLIENT_MSG_ID_MAX, >> +}; >> + >> +/* Client message descriptor table (Family 1Ah Models 80h-8Fh, E0h- >> E3h) */ >> +static const struct hsmp_msg_desc hsmp_client_msg_desc_table[] >> + __attribute__((unused)) = { >> + /* RESERVED */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_TEST */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_SMU_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_INTERFACE_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE */ >> + {0, 0, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR */ >> + {0, 3, HSMP_GET}, >> + /* HSMP_CLIENT_SET_CORE_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FAST_PPT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_TJ_MAX */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FIT_LIMIT_SCALAR */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ENABLE_OVERCLOCKING */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_DISABLE_OVERCLOCKING */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_VID */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_GET_CORE_PERF_ORDER */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_SLOW_PPT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FAST_SPM_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_SLOW_SPM_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_GET_CORE_PSM_MARGIN */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_GFX_PSM_MARGIN */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_SPARE_0X1D */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X1E */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X1F */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X20 */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_GFX_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_CCLK_FMAX_OFFSET */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_GFXCLK_FMAX */ >> + {1, 0, HSMP_SET}, >> +}; > > How would you feel about adding a static (compile time) assert that the > number of entries here matched the number of enums? > > This can avoid making a mistake later. > Good idea, done: static_assert(ARRAY_SIZE(hsmp_msg_desc_table) == HSMP_MSG_ID_MAX); static_assert(ARRAY_SIZE(hsmp_client_msg_desc_table) == HSMP_CLIENT_MSG_ID_MAX); >> + >> #endif /*_ASM_X86_AMD_HSMP_H_*/ >> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/ >> x86/amd/hsmp/hsmp.c >> index 5e123a4ecea9..9b65dbd47965 100644 >> --- a/drivers/platform/x86/amd/hsmp/hsmp.c >> +++ b/drivers/platform/x86/amd/hsmp/hsmp.c >> @@ -45,7 +45,67 @@ >> */ >> #define CHECK_GET_BIT BIT(31) >> -static struct hsmp_plat_device hsmp_pdev; >> +static const struct hsmp_mbaddr_info hsmp_mbinfo_server = { >> + .base_addr = SMN_HSMP_BASE, >> + .msg_id_off = SMN_HSMP_MSG_ID, >> + .msg_resp_off = SMN_HSMP_MSG_RESP, >> + .msg_arg_off = SMN_HSMP_MSG_DATA, >> +}; >> + >> +/* RM: Ryzen Master, the SMC interface these client addresses belong >> to */ >> +static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { >> + .base_addr = SMN_HSMP_BASE_RM, >> + .msg_id_off = SMN_HSMP_MSG_ID_RM, >> + .msg_resp_off = SMN_HSMP_MSG_RESP_RM, >> + .msg_arg_off = SMN_HSMP_MSG_DATA_RM, >> +}; >> + >> +/* >> + * The descriptors carry only the message IDs the driver issues >> itself, which >> + * are also the only ones the two message sets have in common. >> Messages named >> + * by userspace in an ioctl need no entry here, as that path already >> resolves >> + * the ID against the running platform's table. >> + */ >> +static const struct hsmp_plat_desc hsmp_desc_server = { >> + .mbinfo = &hsmp_mbinfo_server, >> + .msg_desc = hsmp_msg_desc_table, >> + .num_msgs = HSMP_MSG_ID_MAX, >> + .test_msg = HSMP_TEST, >> + .proto_ver_msg = HSMP_GET_PROTO_VER, >> +}; >> + >> +/* The client drives a different mailbox with the Ryzen Master SMC >> message set */ >> +static const struct hsmp_plat_desc hsmp_desc_client = { >> + .mbinfo = &hsmp_mbinfo_client, >> + .msg_desc = hsmp_client_msg_desc_table, >> + .num_msgs = HSMP_CLIENT_MSG_ID_MAX, >> + .test_msg = HSMP_CLIENT_TEST, >> + .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, >> +}; >> + >> +static struct hsmp_plat_device hsmp_pdev = { >> + /* >> + * Overridden for the client in get_hsmp_pdev(), which every driver >> + * calls before it touches a socket. Defaulting it here rather than >> + * leaving it NULL keeps the message plumbing safe for any caller >> that >> + * arrives without going through there. >> + */ >> + .desc = &hsmp_desc_server, >> +}; >> + >> +/* >> + * Callers must bounds-check msg_id against desc->num_msgs first; >> this returns >> + * NULL rather than indexing out of range if they did not. >> + */ >> +static inline const struct hsmp_msg_desc *get_msg_desc(u32 msg_id) >> +{ >> + const struct hsmp_plat_desc *desc = hsmp_pdev.desc; >> + >> + if (msg_id >= desc->num_msgs) >> + return NULL; >> + >> + return &desc->msg_desc[msg_id]; >> +} >> /* >> * Gates the AMD HSMP data plane against socket bring-up and teardown. >> @@ -184,30 +244,37 @@ static int __hsmp_send_message(struct >> hsmp_socket *sock, struct hsmp_message *ms >> static int validate_message(struct hsmp_message *msg) >> { >> + const struct hsmp_msg_desc *desc; >> + >> /* msg_id against valid range of message IDs */ >> - if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) >> + if (msg->msg_id < hsmp_pdev.desc->test_msg || >> + msg->msg_id >= hsmp_pdev.desc->num_msgs) >> + return -ENOMSG; >> + >> + desc = get_msg_desc(msg->msg_id); >> + if (!desc) >> return -ENOMSG; >> /* msg_id is a reserved message ID */ >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) >> + if (desc->type == HSMP_RSVD) >> return -ENOMSG; >> /* >> * num_args passed by user should match the num_args specified in >> * message description table. >> */ >> - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) >> + if (msg->num_args != desc->num_args) >> return -EINVAL; >> /* >> * As the HSMP protocol evolves, newer platforms may define more >> * response arguments for existing messages. Use an upper-bound >> * check so that older userspace callers requesting fewer response >> - * words than what the current hsmp_msg_desc_table[] defines are >> - * still accepted, while rejecting requests that exceed the >> - * hardware capability. >> + * words than what the current descriptor table defines are still >> + * accepted, while rejecting requests that exceed the hardware >> + * capability. >> */ >> - if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) >> + if (msg->response_sz > desc->response_sz) >> return -EINVAL; >> return 0; >> @@ -316,7 +383,7 @@ int hsmp_test(u16 sock_ind, u32 value) >> * Test the hsmp port by performing TEST command. The test message >> * takes one argument and returns the value of that argument + 1. >> */ >> - msg.msg_id = HSMP_TEST; >> + msg.msg_id = hsmp_pdev.desc->test_msg; >> msg.num_args = 1; >> msg.response_sz = 1; >> msg.args[0] = value; >> @@ -340,10 +407,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); >> static bool is_get_msg(struct hsmp_message *msg) >> { >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) >> + const struct hsmp_msg_desc *desc = get_msg_desc(msg->msg_id); >> + >> + if (!desc) >> + return false; >> + >> + if (desc->type == HSMP_GET) >> return true; >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && >> + if (desc->type == HSMP_SET_GET && >> (msg->args[0] & CHECK_GET_BIT)) >> return true; >> @@ -354,6 +426,7 @@ static long hsmp_ioctl_msg(struct file *fp, >> unsigned long arg) >> { >> int __user *arguser = (int __user *)arg; >> struct hsmp_message msg = { 0 }; >> + const struct hsmp_msg_desc *desc; >> int ret; >> if (copy_struct_from_user(&msg, sizeof(msg), arguser, >> sizeof(struct hsmp_message))) >> @@ -361,23 +434,28 @@ static long hsmp_ioctl_msg(struct file *fp, >> unsigned long arg) >> /* >> * Check msg_id is within the range of supported msg ids >> - * i.e within the array bounds of hsmp_msg_desc_table >> + * i.e within the array bounds of the platform's descriptor table >> */ >> - if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) >> + if (msg.msg_id < hsmp_pdev.desc->test_msg || >> + msg.msg_id >= hsmp_pdev.desc->num_msgs) >> return -ENOMSG; >> /* >> * Sanitize the user-controlled msg_id against speculative >> * execution. The bounds check above retires the out-of-range >> * case with -ENOMSG, but a mispredicted branch can still let the >> - * CPU speculatively use msg_id as an index into >> - * hsmp_msg_desc_table[] (here and in validate_message() / >> + * CPU speculatively use msg_id as an index into the message >> + * descriptor tables (here and in validate_message() / >> * is_get_msg() called downstream via hsmp_send_message()), and >> * pull arbitrary kernel memory into the cache (Spectre v1, >> * CVE-2017-5753). Clamp once into msg.msg_id so every downstream >> * dereference sees the sanitized value. >> */ >> - msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); >> + msg.msg_id = array_index_nospec(msg.msg_id, hsmp_pdev.desc- >> >num_msgs); >> + >> + desc = get_msg_desc(msg.msg_id); >> + if (!desc || desc->type == HSMP_RSVD) >> + return -ENOMSG; >> switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { >> case FMODE_WRITE: >> @@ -410,7 +488,7 @@ static long hsmp_ioctl_msg(struct file *fp, >> unsigned long arg) >> if (ret) >> return ret; >> - if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { >> + if (desc->response_sz > 0) { >> /* Copy results back to user for get/monitor commands */ >> if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message))) >> return -EFAULT; >> @@ -687,9 +765,9 @@ int hsmp_cache_proto_ver(u16 sock_ind) >> struct hsmp_message msg = { 0 }; >> int ret; >> - msg.msg_id = HSMP_GET_PROTO_VER; >> + msg.msg_id = hsmp_pdev.desc->proto_ver_msg; >> msg.sock_ind = sock_ind; >> - msg.response_sz = >> hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; >> + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; >> ret = hsmp_send_message_locked(&msg); >> if (!ret) >> @@ -735,6 +813,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, >> "AMD_HSMP"); >> struct hsmp_plat_device *get_hsmp_pdev(void) >> { >> + /* >> + * Both drivers call this before they bring any socket up, and >> the CPU >> + * this selects on is fixed long before either can load, so >> resolving >> + * the platform description here is enough to have it in place for >> + * every mailbox access and for the probe paths themselves. >> + */ >> + if (is_client_platform()) >> + hsmp_pdev.desc = &hsmp_desc_client; >> + >> return &hsmp_pdev; >> } >> EXPORT_SYMBOL_NS_GPL(get_hsmp_pdev, "AMD_HSMP"); >> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/ >> x86/amd/hsmp/hsmp.h >> index 8dbff16a87b1..b4bd9800dee1 100644 >> --- a/drivers/platform/x86/amd/hsmp/hsmp.h >> +++ b/drivers/platform/x86/amd/hsmp/hsmp.h >> @@ -10,6 +10,8 @@ >> #ifndef HSMP_H >> #define HSMP_H >> +#include <asm/amd/hsmp.h> >> + >> #include <linux/compiler_types.h> >> #include <linux/device.h> >> #include <linux/hwmon.h> >> @@ -17,6 +19,7 @@ >> #include <linux/miscdevice.h> >> #include <linux/mutex.h> >> #include <linux/pci.h> >> +#include <linux/processor.h> >> #include <linux/rwsem.h> >> #include <linux/semaphore.h> >> #include <linux/sysfs.h> >> @@ -32,6 +35,51 @@ >> #define DRIVER_VERSION "2.6" > > You're adding new IOCTL's in patches 5 and 6, does this version need to > bump for userspace to discover? > Yes - bumped to 2.7 in patch 5, where the first new ioctl appears, with a comment recording that this is what the version is for. Without it a tool has to issue the ioctl and treat -ENOTTY as "old driver", which is awkward when the same errno can come from a wrong-sized struct. >> +/* >> + * To access a specific HSMP mailbox register, s/w writes the SMN >> address of the >> + * HSMP mailbox register into the SMN_INDEX register, and reads/ >> writes the >> + * SMN_DATA reg. Below are the SMN addresses of the HSMP mailbox >> registers in >> + * SMU address space, as consumed by the fixed mailbox descriptors in >> hsmp.c. >> + * >> + * The server set is used by the non-ACPI path only; the ACPI path >> discovers the >> + * equivalent values from _CRS/_DSD. The _RM set is the client one, >> named for >> + * the Ryzen Master SMC (RMSMC) interface whose mailbox it addresses, >> and is >> + * used by both paths because the client exposes no _CRS/_DSD to >> discover them >> + * from. The two sets share a base and differ only in the register >> offsets >> + * behind it, but are kept separate so each stays tied to the >> platform it was >> + * documented for. >> + */ >> +#define SMN_HSMP_BASE 0x03B00000 >> +#define SMN_HSMP_MSG_ID 0x00010534 >> +#define SMN_HSMP_MSG_ID_F1A_M0H 0x00010934 >> +#define SMN_HSMP_MSG_RESP 0x00010980 >> +#define SMN_HSMP_MSG_DATA 0x000109E0 >> + >> +/* _RM: Ryzen Master SMC, the client mailbox */ >> +#define SMN_HSMP_BASE_RM 0x03B00000 >> +#define SMN_HSMP_MSG_ID_RM 0x00010A8C >> +#define SMN_HSMP_MSG_RESP_RM 0x00010A90 >> +#define SMN_HSMP_MSG_DATA_RM 0x00010A94 > > Although these are the same for these 3 platforms, they might not be the > same for the follow on ones. Have you seen the work that's going into > the AMD_PMF driver to build structs of register lists? Take a look at > this. > > https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers- > x86.git/commit/?h=review-ilpo- > next&id=e860e561926bf75e323d43fe59a9bf25d2ef33a3 > I think the series is already built the way it asks for. struct hsmp_mbaddr_info is the equivalent of amd_pmf_smu_regs: the offsets live in per-platform instances (hsmp_mbinfo_server, hsmp_mbinfo_client), reached through hsmp_pdev->desc->mbinfo, and the send path in __hsmp_send_message() reads them from there rather than from constants. Adding a part whose mailbox sits elsewhere means adding one hsmp_mbaddr_info and one descriptor; no conditional lands in the send path. The one difference from PMF is how the instance is selected. PMF matches a PCI ID and hangs the struct off driver_data via PCI_DEVICE_DATA(); HSMP has no PCI device to match on, so get_hsmp_pdev() picks the descriptor from the CPU model instead. On the concern behind the question: the client and server address sets are already separate structs even though two of the four values happen to coincide today, precisely so a follow-on part can differ without disturbing either. If a later part moves the mailbox, it gets its own hsmp_mbaddr_info and its own descriptor, and is_client_platform() splits into a per-family selector. >> + >> +/* >> + * Family 1Ah client platforms use the Ryzen Master SMC (client) >> mailbox and >> + * message set instead of the server HSMP interface: >> + * >> + * Models 80h-87h Medusa1 >> + * Models 88h-8Fh Olympic Ridge >> + * Models E0h-E3h Medusa2 >> + * >> + * They all share the SMN_HSMP_*_RM mailbox addresses above and the same >> + * message set, so one predicate covers them and the two adjacent >> Model 8xh >> + * ranges are tested as one. >> + */ >> +static inline bool is_client_platform(void) >> +{ >> + return boot_cpu_data.x86 == 0x1A && >> + ((boot_cpu_data.x86_model >= 0x80 && >> boot_cpu_data.x86_model <= 0x8F) || >> + (boot_cpu_data.x86_model >= 0xE0 && boot_cpu_data.x86_model >> <= 0xE3)); >> +} > > Rather than enumerating lists of CPU IDs, could you instead go off the > ACPI ID? Or are these identical from server to client? > They are identical - AMDI0097 on both, enumerating as AMDI0097:00 on the client system here and on the server parts. So the ACPI ID cannot select between the two message sets, which is why the model list is there. What does differ is downstream of the ID and not usable as an dentifier: the client device is at \_SB_.TELD with a bare _UID of "0" and no _CRS/_DSD, where the server has the "IDXX" _UID form and both methods. I would rather not infer the platform from a missing method, since absent firmware objects are exactly the thing that varies between BIOS builds. >> + >> struct hsmp_mbaddr_info { >> u32 base_addr; >> u32 msg_id_off; >> @@ -40,6 +88,30 @@ struct hsmp_mbaddr_info { >> u32 size; >> }; >> +/* >> + * Per-platform description of the mailbox and the message set behind >> it. >> + * Resolved once from the running CPU and reached through hsmp_pdev- >> >desc, so >> + * neither the message plumbing nor the probe paths have to re-derive >> which >> + * platform they are on. >> + * >> + * @mbinfo: fixed SMN mailbox addresses. The ACPI path uses >> these >> + * only where firmware exposes no _CRS/_DSD to read them >> + * from, i.e. on the client. >> + * @msg_desc: message descriptor table, indexed by message ID. >> + * @num_msgs: number of entries in @msg_desc, and so the >> exclusive >> + * upper bound on a valid message ID. >> + * @test_msg: no-op message used to probe the mailbox. Also the >> + * lowest valid message ID, as ID 0 is reserved. >> + * @proto_ver_msg: returns the interface version. >> + */ >> +struct hsmp_plat_desc { >> + const struct hsmp_mbaddr_info *mbinfo; >> + const struct hsmp_msg_desc *msg_desc; >> + u32 num_msgs; >> + u32 test_msg; >> + u32 proto_ver_msg; >> +}; >> + >> struct hsmp_socket { >> struct bin_attribute hsmp_attr; >> struct hsmp_mbaddr_info mbinfo; >> @@ -59,6 +131,7 @@ struct hsmp_socket { >> struct hsmp_plat_device { >> struct miscdevice mdev; >> struct hsmp_socket *sock; >> + const struct hsmp_plat_desc *desc; >> u32 proto_ver; >> u16 num_sockets; >> }; >> diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/ >> x86/amd/hsmp/plat.c >> index e9b2b809c0f5..7423c98a7b3e 100644 >> --- a/drivers/platform/x86/amd/hsmp/plat.c >> +++ b/drivers/platform/x86/amd/hsmp/plat.c >> @@ -29,17 +29,6 @@ >> #define DRIVER_NAME "amd_hsmp" >> -/* >> - * To access specific HSMP mailbox register, s/w writes the SMN >> address of HSMP mailbox >> - * register into the SMN_INDEX register, and reads/writes the >> SMN_DATA reg. >> - * Below are required SMN address for HSMP Mailbox register offsets >> in SMU address space >> - */ >> -#define SMN_HSMP_BASE 0x3B00000 >> -#define SMN_HSMP_MSG_ID 0x0010534 >> -#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 >> -#define SMN_HSMP_MSG_RESP 0x0010980 >> -#define SMN_HSMP_MSG_DATA 0x00109E0 >> - >> static struct hsmp_plat_device *hsmp_pdev; >> static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, >> @@ -157,20 +146,18 @@ static int init_platform_device(struct device *dev) >> sock = &hsmp_pdev->sock[i]; >> sock->sock_ind = i; >> sock->dev = dev; >> - sock->mbinfo.base_addr = SMN_HSMP_BASE; >> sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; >> + sock->mbinfo = *hsmp_pdev->desc->mbinfo; >> /* >> * This is a transitional change from non-ACPI to ACPI, only >> * family 0x1A, model 0x00 platform is supported for both >> ACPI and non-ACPI. >> + * It is the one part whose message-id register sits at a >> + * different offset on this path than the descriptor gives. >> */ >> if (is_f1a_m0h()) >> sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; >> - else >> - sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; >> - sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; >> - sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; >> sema_init(&sock->hsmp_sem, 1); >> /* Test the hsmp interface on each socket */ >> @@ -184,8 +171,18 @@ static int init_platform_device(struct device *dev) >> ret = hsmp_cache_proto_ver(i); >> if (ret) { >> - dev_err(dev, "Failed to read HSMP protocol version\n"); >> - return ret; >> + /* >> + * Some client SMU builds reject the interface-version >> + * query with "invalid message" even though the mailbox >> + * is functional, as the test message above just >> + * showed. Keep going there instead of failing the >> + * probe: proto_ver only gates server-side features. >> + */ >> + if (!is_client_platform()) { >> + dev_err(dev, "Failed to read HSMP protocol version\n"); >> + return ret; >> + } >> + dev_warn(dev, "Interface version query unsupported on >> client SMU; continuing\n"); > > Shouldn't this be fatal? Can we fix the F/W to follow the expected HSMP > driver behavior? > Fixing the firmware is the right end state and I have raised it with the SMU team; I will follow up on this thread when I have a commitment. For what it is worth the failure is not a wrong answer, it is a missing message: the client SMU rejects HSMP_CLIENT_GET_INTERFACE_VER (03h) with 0xFE, "invalid message". The mailbox itself is fine, which the test message issued immediately before it demonstrates, and on the Model 80h system here everything else answers with the query failing - SMU version 117.81.0, metric table version 5, table DRAM address 0x5DD8114C. >> } >> if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { >> @@ -317,6 +314,12 @@ static bool legacy_hsmp_support(void) >> case 0x1A: >> switch (boot_cpu_data.x86_model) { >> case 0x00 ... 0x0F: >> + /* >> + * Client platforms: 80h-87h (Medusa1), 88h-8Fh (Olympic >> + * Ridge), E0h-E3h (Medusa2) >> + */ >> + case 0x80 ... 0x8F: >> + case 0xE0 ... 0xE3: >> return true; >> default: >> return false; > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah 2026-07-29 16:40 ` [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah Muralidhara M K 2026-07-29 21:27 ` Mario Limonciello @ 2026-07-30 16:43 ` Armin Wolf 2026-07-31 4:27 ` M K, Muralidhara 2026-07-31 7:00 ` Shyam Sundar S K 1 sibling, 2 replies; 22+ messages in thread From: Armin Wolf @ 2026-07-30 16:43 UTC (permalink / raw) To: Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam Am 29.07.26 um 18:40 schrieb Muralidhara M K: > Add non-ACPI HSMP mailbox support, Ryzen Master SMC client message > descriptors, and platform routing for the Family 1Ah client platforms: > > Models 80h-87h Medusa1 > Models 88h-8Fh Olympic Ridge > Models E0h-E3h Medusa2 > > All three share the same client mailbox addresses and message set, so a > single is_client_platform() predicate covers them, and the two adjacent > Model 8xh ranges are tested as one. > > The client parts drive a different mailbox from the server HSMP > interface. They expose no _DSD/_CRS to discover the mailbox, so the SMN > addresses are fixed in hsmp.h and shared by the platform and ACPI > probes, and they speak the Ryzen Master SMC (RMSMC) message set rather > than the server message IDs. Add that message set and its descriptor > table to the UAPI header. > > Two message descriptor tables now exist, so every lookup has to pick the > one matching the running platform. Rather than test the platform at each > lookup, describe the platform once: struct hsmp_plat_desc holds the > mailbox addresses, the descriptor table and its size, and the IDs of the > messages the driver issues itself. get_hsmp_pdev() resolves it, which > both drivers call before they bring any socket up and which runs on a > CPU that is fixed long before either can load. The server descriptor is > also the static initialiser, so the message plumbing cannot reach a NULL > descriptor even if a future caller arrives another way. > > get_msg_desc() then indexes the table behind that pointer, and > validate_message(), is_get_msg(), hsmp_ioctl_msg(), hsmp_test() and > hsmp_cache_proto_ver() read the bound and the message IDs from the > descriptor. get_msg_desc() returns NULL rather than indexing out of > range if a caller skipped the bounds check, and the Spectre v1 clamp in > hsmp_ioctl_msg() uses the running platform's table size. > > In init_platform_device() the mailbox setup becomes a copy of the > descriptor's addresses. The F1Ah M0h message-id offset stays behind > is_f1a_m0h() as an override, because it applies only to this non-ACPI > path while the descriptor is shared with the ACPI one. It cannot disturb > the client mailbox: it requires model <= 0x0F and the client models are > all >= 0x80. The server mailbox addresses move from plat.c to hsmp.h so > both address sets sit with the descriptors that consume them; the values > are unchanged, written to the same width as the client set they now sit > beside. > > Since this is the change that lets the platform driver match the client > models, it also has to let the client probe survive the interface-version > query. Some client SMU builds reject HSMP_CLIENT_GET_INTERFACE_VER with > "invalid message" even though the mailbox works, as the test message > issued just before it shows, so treat that failure as non-fatal on the > client and register the socket anyway. Nothing on the client depends on > the reported version: hsmp_pdev->proto_ver gates the metrics_bin > attribute and the protocol_version sysfs file, both of which describe > the server interface. The query stays fatal on server parts. > > The client descriptor table carries __attribute__((unused)), matching > hsmp_msg_desc_table[]. Both are defined in a UAPI header that several > translation units include without using the table, so without it > -Wunused-const-variable fires on plat.c, acpi.c and hwmon.c. > > Document the client platforms in Documentation/arch/x86/amd_hsmp.rst, > which until now described HSMP as available only on the EPYC server > parts and MI300A. The character device and ioctl interface are unchanged > there; what differs is the accepted message IDs, so the text points at > the HSMP_CLIENT_* set in the UAPI header. Hi, i am asking myself if there exist a connection between the Ryzen Master SMC message ids and the AMD_ACPI WMI device: [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Class to Query/Set a CLKValue"), guid("{95F20B61-3D44-41F8-87E1-03CBF94C4E8F}")] class AMD_ACPI { [key, read] string InstanceName; [read] boolean Active; [WmiMethodId(1), Implemented, read, write, Description("Get version")] void QueryVersion([out, Description("version")] uint32 version); [WmiMethodId(2), Implemented, read, write, Description("WMI function init.")] void Init([out, Description("status")] uint8 status); [WmiMethodId(3), Implemented, read, write, Description("Get BIOS ACPI support function table.")] void GetObjectID([out, Description("Get BIOS support Object ID table")] OIDPackage pack); [WmiMethodId(4), Implemented, read, write, Description("Bios request data")] void Getdvalues([in, Description("ID used to identify")] uint32 ID, [out, Description("status")] GetdValuesPackage pack); [WmiMethodId(5), Implemented, read, write, Description("Command Run")] void RunCommand([in, Description("In buffer")] uint8 Inbuf[8], [out, Description("Out buffer")] OutBuffer Outbuf); [WmiMethodId(6), Implemented, read, write, Description("Get Min/Max/Stepping values")] void GetMinMaxSteppingValues([in, Description("ID used to identify")] uint32 ID, [out, Description("status")] GetMinMaxSteppingPackage pack); [WmiMethodId(7), Implemented, read, write, Description("Get DIMM SPD data")] void GetDimmSpdData([in, Description("DIMM Index")] uint32 Index, [out, Description("SPD Data Buffer")] DimmSpdBuffer DimmSpdData); [WmiMethodId(8), Implemented, read, write, Description("Get BIOS ACPI support function table2.")] void GetObjectID2([out, Description("Get BIOS support Object ID table2")] OIDPackage pack); [WmiMethodId(9), Implemented, read, write, Description("Get RMP table")] void GetRmpData([out, Description("Get RMP table")] RMPPackage pack); [WmiMethodId(10), Implemented, read, write, Description("Get OPP Heaer")] void GetOppTable([out, Description("Get Opp Heaer")] OPPHeader pack); [WmiMethodId(11), Implemented, read, write, Description("Get OPP Data")] void GetOppData([in, Description("Profile Index")] uint32 Index, [out, Description("Opp Profile")] OPPProfole pack); }; I own multiple devices with such an interface, and i would _love_ to add support for them. Thanks, Armin Wolf > Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> > --- > Documentation/arch/x86/amd_hsmp.rst | 18 +++- > arch/x86/include/uapi/asm/amd_hsmp.h | 137 +++++++++++++++++++++++++++ > drivers/platform/x86/amd/hsmp/hsmp.c | 125 ++++++++++++++++++++---- > drivers/platform/x86/amd/hsmp/hsmp.h | 73 ++++++++++++++ > drivers/platform/x86/amd/hsmp/plat.c | 39 ++++---- > 5 files changed, 354 insertions(+), 38 deletions(-) > > diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst > index fa1fc240e212..8a9d6fe7d4cf 100644 > --- a/Documentation/arch/x86/amd_hsmp.rst > +++ b/Documentation/arch/x86/amd_hsmp.rst > @@ -8,6 +8,21 @@ Newer Fam19h(model 0x00-0x1f, 0x30-0x3f, 0x90-0x9f, 0xa0-0xaf), > Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD support > system management functionality via HSMP (Host System Management Port). > > +The Fam1Ah client platforms are supported as well: > + > + ============= ============= > + Model Platform > + ============= ============= > + 0x80-0x87 Medusa1 > + 0x88-0x8f Olympic Ridge > + 0xe0-0xe3 Medusa2 > + ============= ============= > + > +They share one mailbox and speak the Ryzen Master SMC message set instead > +of the server HSMP message set, so the message IDs accepted on them are > +the HSMP_CLIENT_* ones listed in arch/x86/include/uapi/asm/amd_hsmp.h. > +The character device and ioctl interface described below are the same. > + > The Host System Management Port (HSMP) is an interface to provide > OS-level software with access to system management functions via a > set of mailbox registers. > @@ -17,7 +32,8 @@ More details on the interface can be found in chapter > Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50 > > > -HSMP interface is supported on EPYC line of server CPUs and MI300A (APU). > +HSMP interface is supported on EPYC line of server CPUs, MI300A (APU) and > +the Fam1Ah client platforms listed above. > > > HSMP device > diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h > index eda336bfd3e9..b4c3ddb9d1c1 100644 > --- a/arch/x86/include/uapi/asm/amd_hsmp.h > +++ b/arch/x86/include/uapi/asm/amd_hsmp.h > @@ -664,4 +664,141 @@ struct hsmp_telemetry_data { > #define HSMP_IOCTL_GET_TELEMETRY_DATA \ > _IOW(HSMP_BASE_IOCTL_NR, 1, struct hsmp_telemetry_data) > > +/* > + * Client HSMP messages supported on the Family 1Ah client platforms: > + * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and > + * Models E0h-E3h (Medusa2). > + * These map to Ryzen Master SMC (RMSMC) message IDs. > + */ > +enum hsmp_client_message_ids { > + HSMP_CLIENT_TEST = 1, /* 01h Test message */ > + HSMP_CLIENT_GET_SMU_VER, /* 02h SMU FW version */ > + HSMP_CLIENT_GET_INTERFACE_VER, /* 03h Interface version */ > + HSMP_CLIENT_GET_METRICS_TABLE_VER, /* 04h Metrics table version */ > + HSMP_CLIENT_GET_METRICS_TABLE, /* 05h Metrics table log sample */ > + HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR,/* 06h Metrics table DRAM address */ > + HSMP_CLIENT_SET_CORE_PSM_MARGIN, /* 07h Set core PSM margin */ > + HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN, /* 08h Set all core PSM margin */ > + HSMP_CLIENT_SET_FAST_PPT_LIMIT, /* 09h Set fast PPT limit */ > + HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT, /* 0Ah Set VRM VDD current limit */ > + HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT,/* 0Bh Set VRM VDD max current limit */ > + HSMP_CLIENT_SET_TJ_MAX, /* 0Ch Set TjMax */ > + HSMP_CLIENT_SET_FIT_LIMIT_SCALAR, /* 0Dh Set FIT limit scalar */ > + HSMP_CLIENT_ENABLE_OVERCLOCKING, /* 0Eh Enable overclocking */ > + HSMP_CLIENT_DISABLE_OVERCLOCKING, /* 0Fh Disable overclocking */ > + HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES,/* 10h Set OC freq all cores */ > + HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE,/* 11h Set OC freq per core */ > + HSMP_CLIENT_SET_OVERCLOCK_VID, /* 12h Set OC VID */ > + HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY,/* 13h Set FCLK OC on-the-fly */ > + HSMP_CLIENT_GET_CORE_PERF_ORDER, /* 14h Get core performance order */ > + HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT, /* 15h Set sustained power limit */ > + HSMP_CLIENT_SET_SLOW_PPT_LIMIT, /* 16h Set slow PPT limit */ > + HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT,/* 17h Set VRM GFX max current limit */ > + HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT, /* 18h Set VRM SOC current limit */ > + HSMP_CLIENT_SET_FAST_SPM_LIMIT, /* 19h Set fast SPM limit */ > + HSMP_CLIENT_SET_SLOW_SPM_LIMIT, /* 1Ah Set slow SPM limit */ > + HSMP_CLIENT_GET_CORE_PSM_MARGIN, /* 1Bh Get core PSM margin */ > + HSMP_CLIENT_GET_GFX_PSM_MARGIN, /* 1Ch Get GFX PSM margin */ > + HSMP_CLIENT_SPARE_0X1D, /* 1Dh Spare */ > + HSMP_CLIENT_SPARE_0X1E, /* 1Eh Spare */ > + HSMP_CLIENT_SPARE_0X1F, /* 1Fh Spare */ > + HSMP_CLIENT_SPARE_0X20, /* 20h Spare */ > + HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID,/* 21h Set GfxClk overdrive by freq/VID */ > + HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE, /* 22h Disable GfxClk overdrive */ > + HSMP_CLIENT_SET_GFX_PSM_MARGIN, /* 23h Set GFX PSM margin */ > + HSMP_CLIENT_SET_CCLK_FMAX_OFFSET, /* 24h Set CCLK Fmax offset */ > + HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET,/* 25h Set core power limit offset */ > + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND, /* 26h Add extra PSM guardband */ > + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX,/* 27h Add extra PSM guardband GFX */ > + HSMP_CLIENT_SET_GFXCLK_FMAX, /* 28h Set GfxClk Fmax */ > + HSMP_CLIENT_MSG_ID_MAX, > +}; > + > +/* Client message descriptor table (Family 1Ah Models 80h-8Fh, E0h-E3h) */ > +static const struct hsmp_msg_desc hsmp_client_msg_desc_table[] > + __attribute__((unused)) = { > + /* RESERVED */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_TEST */ > + {1, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_SMU_VER */ > + {0, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_INTERFACE_VER */ > + {0, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_METRICS_TABLE_VER */ > + {0, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_METRICS_TABLE */ > + {0, 0, HSMP_GET}, > + /* HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR */ > + {0, 3, HSMP_GET}, > + /* HSMP_CLIENT_SET_CORE_PSM_MARGIN */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_FAST_PPT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_TJ_MAX */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_FIT_LIMIT_SCALAR */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_ENABLE_OVERCLOCKING */ > + {0, 0, HSMP_SET}, > + /* HSMP_CLIENT_DISABLE_OVERCLOCKING */ > + {0, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_OVERCLOCK_VID */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_GET_CORE_PERF_ORDER */ > + {1, 1, HSMP_GET}, > + /* HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_SLOW_PPT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_FAST_SPM_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_SLOW_SPM_LIMIT */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_GET_CORE_PSM_MARGIN */ > + {1, 1, HSMP_GET}, > + /* HSMP_CLIENT_GET_GFX_PSM_MARGIN */ > + {0, 1, HSMP_GET}, > + /* HSMP_CLIENT_SPARE_0X1D */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_SPARE_0X1E */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_SPARE_0X1F */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_SPARE_0X20 */ > + {0, 0, HSMP_RSVD}, > + /* HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE */ > + {0, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_GFX_PSM_MARGIN */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_CCLK_FMAX_OFFSET */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX */ > + {1, 0, HSMP_SET}, > + /* HSMP_CLIENT_SET_GFXCLK_FMAX */ > + {1, 0, HSMP_SET}, > +}; > + > #endif /*_ASM_X86_AMD_HSMP_H_*/ > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c > index 5e123a4ecea9..9b65dbd47965 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.c > +++ b/drivers/platform/x86/amd/hsmp/hsmp.c > @@ -45,7 +45,67 @@ > */ > #define CHECK_GET_BIT BIT(31) > > -static struct hsmp_plat_device hsmp_pdev; > +static const struct hsmp_mbaddr_info hsmp_mbinfo_server = { > + .base_addr = SMN_HSMP_BASE, > + .msg_id_off = SMN_HSMP_MSG_ID, > + .msg_resp_off = SMN_HSMP_MSG_RESP, > + .msg_arg_off = SMN_HSMP_MSG_DATA, > +}; > + > +/* RM: Ryzen Master, the SMC interface these client addresses belong to */ > +static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { > + .base_addr = SMN_HSMP_BASE_RM, > + .msg_id_off = SMN_HSMP_MSG_ID_RM, > + .msg_resp_off = SMN_HSMP_MSG_RESP_RM, > + .msg_arg_off = SMN_HSMP_MSG_DATA_RM, > +}; > + > +/* > + * The descriptors carry only the message IDs the driver issues itself, which > + * are also the only ones the two message sets have in common. Messages named > + * by userspace in an ioctl need no entry here, as that path already resolves > + * the ID against the running platform's table. > + */ > +static const struct hsmp_plat_desc hsmp_desc_server = { > + .mbinfo = &hsmp_mbinfo_server, > + .msg_desc = hsmp_msg_desc_table, > + .num_msgs = HSMP_MSG_ID_MAX, > + .test_msg = HSMP_TEST, > + .proto_ver_msg = HSMP_GET_PROTO_VER, > +}; > + > +/* The client drives a different mailbox with the Ryzen Master SMC message set */ > +static const struct hsmp_plat_desc hsmp_desc_client = { > + .mbinfo = &hsmp_mbinfo_client, > + .msg_desc = hsmp_client_msg_desc_table, > + .num_msgs = HSMP_CLIENT_MSG_ID_MAX, > + .test_msg = HSMP_CLIENT_TEST, > + .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, > +}; > + > +static struct hsmp_plat_device hsmp_pdev = { > + /* > + * Overridden for the client in get_hsmp_pdev(), which every driver > + * calls before it touches a socket. Defaulting it here rather than > + * leaving it NULL keeps the message plumbing safe for any caller that > + * arrives without going through there. > + */ > + .desc = &hsmp_desc_server, > +}; > + > +/* > + * Callers must bounds-check msg_id against desc->num_msgs first; this returns > + * NULL rather than indexing out of range if they did not. > + */ > +static inline const struct hsmp_msg_desc *get_msg_desc(u32 msg_id) > +{ > + const struct hsmp_plat_desc *desc = hsmp_pdev.desc; > + > + if (msg_id >= desc->num_msgs) > + return NULL; > + > + return &desc->msg_desc[msg_id]; > +} > > /* > * Gates the AMD HSMP data plane against socket bring-up and teardown. > @@ -184,30 +244,37 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms > > static int validate_message(struct hsmp_message *msg) > { > + const struct hsmp_msg_desc *desc; > + > /* msg_id against valid range of message IDs */ > - if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) > + if (msg->msg_id < hsmp_pdev.desc->test_msg || > + msg->msg_id >= hsmp_pdev.desc->num_msgs) > + return -ENOMSG; > + > + desc = get_msg_desc(msg->msg_id); > + if (!desc) > return -ENOMSG; > > /* msg_id is a reserved message ID */ > - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) > + if (desc->type == HSMP_RSVD) > return -ENOMSG; > > /* > * num_args passed by user should match the num_args specified in > * message description table. > */ > - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) > + if (msg->num_args != desc->num_args) > return -EINVAL; > > /* > * As the HSMP protocol evolves, newer platforms may define more > * response arguments for existing messages. Use an upper-bound > * check so that older userspace callers requesting fewer response > - * words than what the current hsmp_msg_desc_table[] defines are > - * still accepted, while rejecting requests that exceed the > - * hardware capability. > + * words than what the current descriptor table defines are still > + * accepted, while rejecting requests that exceed the hardware > + * capability. > */ > - if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) > + if (msg->response_sz > desc->response_sz) > return -EINVAL; > > return 0; > @@ -316,7 +383,7 @@ int hsmp_test(u16 sock_ind, u32 value) > * Test the hsmp port by performing TEST command. The test message > * takes one argument and returns the value of that argument + 1. > */ > - msg.msg_id = HSMP_TEST; > + msg.msg_id = hsmp_pdev.desc->test_msg; > msg.num_args = 1; > msg.response_sz = 1; > msg.args[0] = value; > @@ -340,10 +407,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); > > static bool is_get_msg(struct hsmp_message *msg) > { > - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) > + const struct hsmp_msg_desc *desc = get_msg_desc(msg->msg_id); > + > + if (!desc) > + return false; > + > + if (desc->type == HSMP_GET) > return true; > > - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && > + if (desc->type == HSMP_SET_GET && > (msg->args[0] & CHECK_GET_BIT)) > return true; > > @@ -354,6 +426,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) > { > int __user *arguser = (int __user *)arg; > struct hsmp_message msg = { 0 }; > + const struct hsmp_msg_desc *desc; > int ret; > > if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct hsmp_message))) > @@ -361,23 +434,28 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) > > /* > * Check msg_id is within the range of supported msg ids > - * i.e within the array bounds of hsmp_msg_desc_table > + * i.e within the array bounds of the platform's descriptor table > */ > - if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) > + if (msg.msg_id < hsmp_pdev.desc->test_msg || > + msg.msg_id >= hsmp_pdev.desc->num_msgs) > return -ENOMSG; > > /* > * Sanitize the user-controlled msg_id against speculative > * execution. The bounds check above retires the out-of-range > * case with -ENOMSG, but a mispredicted branch can still let the > - * CPU speculatively use msg_id as an index into > - * hsmp_msg_desc_table[] (here and in validate_message() / > + * CPU speculatively use msg_id as an index into the message > + * descriptor tables (here and in validate_message() / > * is_get_msg() called downstream via hsmp_send_message()), and > * pull arbitrary kernel memory into the cache (Spectre v1, > * CVE-2017-5753). Clamp once into msg.msg_id so every downstream > * dereference sees the sanitized value. > */ > - msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); > + msg.msg_id = array_index_nospec(msg.msg_id, hsmp_pdev.desc->num_msgs); > + > + desc = get_msg_desc(msg.msg_id); > + if (!desc || desc->type == HSMP_RSVD) > + return -ENOMSG; > > switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { > case FMODE_WRITE: > @@ -410,7 +488,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) > if (ret) > return ret; > > - if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { > + if (desc->response_sz > 0) { > /* Copy results back to user for get/monitor commands */ > if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message))) > return -EFAULT; > @@ -687,9 +765,9 @@ int hsmp_cache_proto_ver(u16 sock_ind) > struct hsmp_message msg = { 0 }; > int ret; > > - msg.msg_id = HSMP_GET_PROTO_VER; > + msg.msg_id = hsmp_pdev.desc->proto_ver_msg; > msg.sock_ind = sock_ind; > - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; > + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; > > ret = hsmp_send_message_locked(&msg); > if (!ret) > @@ -735,6 +813,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, "AMD_HSMP"); > > struct hsmp_plat_device *get_hsmp_pdev(void) > { > + /* > + * Both drivers call this before they bring any socket up, and the CPU > + * this selects on is fixed long before either can load, so resolving > + * the platform description here is enough to have it in place for > + * every mailbox access and for the probe paths themselves. > + */ > + if (is_client_platform()) > + hsmp_pdev.desc = &hsmp_desc_client; > + > return &hsmp_pdev; > } > EXPORT_SYMBOL_NS_GPL(get_hsmp_pdev, "AMD_HSMP"); > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h > index 8dbff16a87b1..b4bd9800dee1 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.h > +++ b/drivers/platform/x86/amd/hsmp/hsmp.h > @@ -10,6 +10,8 @@ > #ifndef HSMP_H > #define HSMP_H > > +#include <asm/amd/hsmp.h> > + > #include <linux/compiler_types.h> > #include <linux/device.h> > #include <linux/hwmon.h> > @@ -17,6 +19,7 @@ > #include <linux/miscdevice.h> > #include <linux/mutex.h> > #include <linux/pci.h> > +#include <linux/processor.h> > #include <linux/rwsem.h> > #include <linux/semaphore.h> > #include <linux/sysfs.h> > @@ -32,6 +35,51 @@ > > #define DRIVER_VERSION "2.6" > > +/* > + * To access a specific HSMP mailbox register, s/w writes the SMN address of the > + * HSMP mailbox register into the SMN_INDEX register, and reads/writes the > + * SMN_DATA reg. Below are the SMN addresses of the HSMP mailbox registers in > + * SMU address space, as consumed by the fixed mailbox descriptors in hsmp.c. > + * > + * The server set is used by the non-ACPI path only; the ACPI path discovers the > + * equivalent values from _CRS/_DSD. The _RM set is the client one, named for > + * the Ryzen Master SMC (RMSMC) interface whose mailbox it addresses, and is > + * used by both paths because the client exposes no _CRS/_DSD to discover them > + * from. The two sets share a base and differ only in the register offsets > + * behind it, but are kept separate so each stays tied to the platform it was > + * documented for. > + */ > +#define SMN_HSMP_BASE 0x03B00000 > +#define SMN_HSMP_MSG_ID 0x00010534 > +#define SMN_HSMP_MSG_ID_F1A_M0H 0x00010934 > +#define SMN_HSMP_MSG_RESP 0x00010980 > +#define SMN_HSMP_MSG_DATA 0x000109E0 > + > +/* _RM: Ryzen Master SMC, the client mailbox */ > +#define SMN_HSMP_BASE_RM 0x03B00000 > +#define SMN_HSMP_MSG_ID_RM 0x00010A8C > +#define SMN_HSMP_MSG_RESP_RM 0x00010A90 > +#define SMN_HSMP_MSG_DATA_RM 0x00010A94 > + > +/* > + * Family 1Ah client platforms use the Ryzen Master SMC (client) mailbox and > + * message set instead of the server HSMP interface: > + * > + * Models 80h-87h Medusa1 > + * Models 88h-8Fh Olympic Ridge > + * Models E0h-E3h Medusa2 > + * > + * They all share the SMN_HSMP_*_RM mailbox addresses above and the same > + * message set, so one predicate covers them and the two adjacent Model 8xh > + * ranges are tested as one. > + */ > +static inline bool is_client_platform(void) > +{ > + return boot_cpu_data.x86 == 0x1A && > + ((boot_cpu_data.x86_model >= 0x80 && boot_cpu_data.x86_model <= 0x8F) || > + (boot_cpu_data.x86_model >= 0xE0 && boot_cpu_data.x86_model <= 0xE3)); > +} > + > struct hsmp_mbaddr_info { > u32 base_addr; > u32 msg_id_off; > @@ -40,6 +88,30 @@ struct hsmp_mbaddr_info { > u32 size; > }; > > +/* > + * Per-platform description of the mailbox and the message set behind it. > + * Resolved once from the running CPU and reached through hsmp_pdev->desc, so > + * neither the message plumbing nor the probe paths have to re-derive which > + * platform they are on. > + * > + * @mbinfo: fixed SMN mailbox addresses. The ACPI path uses these > + * only where firmware exposes no _CRS/_DSD to read them > + * from, i.e. on the client. > + * @msg_desc: message descriptor table, indexed by message ID. > + * @num_msgs: number of entries in @msg_desc, and so the exclusive > + * upper bound on a valid message ID. > + * @test_msg: no-op message used to probe the mailbox. Also the > + * lowest valid message ID, as ID 0 is reserved. > + * @proto_ver_msg: returns the interface version. > + */ > +struct hsmp_plat_desc { > + const struct hsmp_mbaddr_info *mbinfo; > + const struct hsmp_msg_desc *msg_desc; > + u32 num_msgs; > + u32 test_msg; > + u32 proto_ver_msg; > +}; > + > struct hsmp_socket { > struct bin_attribute hsmp_attr; > struct hsmp_mbaddr_info mbinfo; > @@ -59,6 +131,7 @@ struct hsmp_socket { > struct hsmp_plat_device { > struct miscdevice mdev; > struct hsmp_socket *sock; > + const struct hsmp_plat_desc *desc; > u32 proto_ver; > u16 num_sockets; > }; > diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c > index e9b2b809c0f5..7423c98a7b3e 100644 > --- a/drivers/platform/x86/amd/hsmp/plat.c > +++ b/drivers/platform/x86/amd/hsmp/plat.c > @@ -29,17 +29,6 @@ > > #define DRIVER_NAME "amd_hsmp" > > -/* > - * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox > - * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg. > - * Below are required SMN address for HSMP Mailbox register offsets in SMU address space > - */ > -#define SMN_HSMP_BASE 0x3B00000 > -#define SMN_HSMP_MSG_ID 0x0010534 > -#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 > -#define SMN_HSMP_MSG_RESP 0x0010980 > -#define SMN_HSMP_MSG_DATA 0x00109E0 > - > static struct hsmp_plat_device *hsmp_pdev; > > static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, > @@ -157,20 +146,18 @@ static int init_platform_device(struct device *dev) > sock = &hsmp_pdev->sock[i]; > sock->sock_ind = i; > sock->dev = dev; > - sock->mbinfo.base_addr = SMN_HSMP_BASE; > sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; > + sock->mbinfo = *hsmp_pdev->desc->mbinfo; > > /* > * This is a transitional change from non-ACPI to ACPI, only > * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI. > + * It is the one part whose message-id register sits at a > + * different offset on this path than the descriptor gives. > */ > if (is_f1a_m0h()) > sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; > - else > - sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; > > - sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; > - sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; > sema_init(&sock->hsmp_sem, 1); > > /* Test the hsmp interface on each socket */ > @@ -184,8 +171,18 @@ static int init_platform_device(struct device *dev) > > ret = hsmp_cache_proto_ver(i); > if (ret) { > - dev_err(dev, "Failed to read HSMP protocol version\n"); > - return ret; > + /* > + * Some client SMU builds reject the interface-version > + * query with "invalid message" even though the mailbox > + * is functional, as the test message above just > + * showed. Keep going there instead of failing the > + * probe: proto_ver only gates server-side features. > + */ > + if (!is_client_platform()) { > + dev_err(dev, "Failed to read HSMP protocol version\n"); > + return ret; > + } > + dev_warn(dev, "Interface version query unsupported on client SMU; continuing\n"); > } > > if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { > @@ -317,6 +314,12 @@ static bool legacy_hsmp_support(void) > case 0x1A: > switch (boot_cpu_data.x86_model) { > case 0x00 ... 0x0F: > + /* > + * Client platforms: 80h-87h (Medusa1), 88h-8Fh (Olympic > + * Ridge), E0h-E3h (Medusa2) > + */ > + case 0x80 ... 0x8F: > + case 0xE0 ... 0xE3: > return true; > default: > return false; ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah 2026-07-30 16:43 ` Armin Wolf @ 2026-07-31 4:27 ` M K, Muralidhara 2026-07-31 7:00 ` Shyam Sundar S K 1 sibling, 0 replies; 22+ messages in thread From: M K, Muralidhara @ 2026-07-31 4:27 UTC (permalink / raw) To: Armin Wolf, Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/30/2026 10:13 PM, Armin Wolf wrote: > Caution: This message originated from an External Source. Use proper > caution when opening attachments, clicking links, or responding. > > > Am 29.07.26 um 18:40 schrieb Muralidhara M K: > >> Add non-ACPI HSMP mailbox support, Ryzen Master SMC client message >> descriptors, and platform routing for the Family 1Ah client platforms: >> >> Models 80h-87h Medusa1 >> Models 88h-8Fh Olympic Ridge >> Models E0h-E3h Medusa2 >> >> All three share the same client mailbox addresses and message set, so a >> single is_client_platform() predicate covers them, and the two adjacent >> Model 8xh ranges are tested as one. >> >> The client parts drive a different mailbox from the server HSMP >> interface. They expose no _DSD/_CRS to discover the mailbox, so the SMN >> addresses are fixed in hsmp.h and shared by the platform and ACPI >> probes, and they speak the Ryzen Master SMC (RMSMC) message set rather >> than the server message IDs. Add that message set and its descriptor >> table to the UAPI header. >> >> Two message descriptor tables now exist, so every lookup has to pick the >> one matching the running platform. Rather than test the platform at each >> lookup, describe the platform once: struct hsmp_plat_desc holds the >> mailbox addresses, the descriptor table and its size, and the IDs of the >> messages the driver issues itself. get_hsmp_pdev() resolves it, which >> both drivers call before they bring any socket up and which runs on a >> CPU that is fixed long before either can load. The server descriptor is >> also the static initialiser, so the message plumbing cannot reach a NULL >> descriptor even if a future caller arrives another way. >> >> get_msg_desc() then indexes the table behind that pointer, and >> validate_message(), is_get_msg(), hsmp_ioctl_msg(), hsmp_test() and >> hsmp_cache_proto_ver() read the bound and the message IDs from the >> descriptor. get_msg_desc() returns NULL rather than indexing out of >> range if a caller skipped the bounds check, and the Spectre v1 clamp in >> hsmp_ioctl_msg() uses the running platform's table size. >> >> In init_platform_device() the mailbox setup becomes a copy of the >> descriptor's addresses. The F1Ah M0h message-id offset stays behind >> is_f1a_m0h() as an override, because it applies only to this non-ACPI >> path while the descriptor is shared with the ACPI one. It cannot disturb >> the client mailbox: it requires model <= 0x0F and the client models are >> all >= 0x80. The server mailbox addresses move from plat.c to hsmp.h so >> both address sets sit with the descriptors that consume them; the values >> are unchanged, written to the same width as the client set they now sit >> beside. >> >> Since this is the change that lets the platform driver match the client >> models, it also has to let the client probe survive the interface-version >> query. Some client SMU builds reject HSMP_CLIENT_GET_INTERFACE_VER with >> "invalid message" even though the mailbox works, as the test message >> issued just before it shows, so treat that failure as non-fatal on the >> client and register the socket anyway. Nothing on the client depends on >> the reported version: hsmp_pdev->proto_ver gates the metrics_bin >> attribute and the protocol_version sysfs file, both of which describe >> the server interface. The query stays fatal on server parts. >> >> The client descriptor table carries __attribute__((unused)), matching >> hsmp_msg_desc_table[]. Both are defined in a UAPI header that several >> translation units include without using the table, so without it >> -Wunused-const-variable fires on plat.c, acpi.c and hwmon.c. >> >> Document the client platforms in Documentation/arch/x86/amd_hsmp.rst, >> which until now described HSMP as available only on the EPYC server >> parts and MI300A. The character device and ioctl interface are unchanged >> there; what differs is the accepted message IDs, so the text points at >> the HSMP_CLIENT_* set in the UAPI header. > > Hi, > > i am asking myself if there exist a connection between the Ryzen Master SMC > message ids and the AMD_ACPI WMI device: > Hi Amin, There is no connection between the RM HSMP message IDs and the WMI driver. We have already implemented the AMD WMI driver and plan to submit it for upstream review shortly. > [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), > Description("Class to Query/Set a CLKValue"), > guid("{95F20B61-3D44-41F8-87E1-03CBF94C4E8F}")] > class AMD_ACPI { > [key, read] string InstanceName; > [read] boolean Active; > > [WmiMethodId(1), Implemented, read, write, Description("Get > version")] void QueryVersion([out, Description("version")] uint32 version); > [WmiMethodId(2), Implemented, read, write, Description("WMI function > init.")] void Init([out, Description("status")] uint8 status); > [WmiMethodId(3), Implemented, read, write, Description("Get BIOS ACPI > support function table.")] void GetObjectID([out, Description("Get BIOS > support Object ID table")] OIDPackage pack); > [WmiMethodId(4), Implemented, read, write, Description("Bios request > data")] void Getdvalues([in, Description("ID used to identify")] uint32 > ID, [out, Description("status")] GetdValuesPackage pack); > [WmiMethodId(5), Implemented, read, write, Description("Command > Run")] void RunCommand([in, Description("In buffer")] uint8 Inbuf[8], > [out, Description("Out buffer")] OutBuffer Outbuf); > [WmiMethodId(6), Implemented, read, write, Description("Get Min/Max/ > Stepping values")] void GetMinMaxSteppingValues([in, Description("ID > used to identify")] uint32 ID, [out, Description("status")] > GetMinMaxSteppingPackage pack); > [WmiMethodId(7), Implemented, read, write, Description("Get DIMM SPD > data")] void GetDimmSpdData([in, Description("DIMM Index")] uint32 > Index, [out, Description("SPD Data Buffer")] DimmSpdBuffer DimmSpdData); > [WmiMethodId(8), Implemented, read, write, Description("Get BIOS ACPI > support function table2.")] void GetObjectID2([out, Description("Get > BIOS support Object ID table2")] OIDPackage pack); > [WmiMethodId(9), Implemented, read, write, Description("Get RMP > table")] void GetRmpData([out, Description("Get RMP table")] RMPPackage > pack); > [WmiMethodId(10), Implemented, read, write, Description("Get OPP > Heaer")] void GetOppTable([out, Description("Get Opp Heaer")] OPPHeader > pack); > [WmiMethodId(11), Implemented, read, write, Description("Get OPP > Data")] void GetOppData([in, Description("Profile Index")] uint32 Index, > [out, Description("Opp Profile")] OPPProfole pack); > }; > > I own multiple devices with such an interface, and i would _love_ to add > support > for them. > Thanks, We have already implemented in AMD WMI driver all the above methods. will submit soon. > Thanks, > Armin Wolf > >> Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> >> --- >> Documentation/arch/x86/amd_hsmp.rst | 18 +++- >> arch/x86/include/uapi/asm/amd_hsmp.h | 137 +++++++++++++++++++++++++++ >> drivers/platform/x86/amd/hsmp/hsmp.c | 125 ++++++++++++++++++++---- >> drivers/platform/x86/amd/hsmp/hsmp.h | 73 ++++++++++++++ >> drivers/platform/x86/amd/hsmp/plat.c | 39 ++++---- >> 5 files changed, 354 insertions(+), 38 deletions(-) >> >> diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/ >> x86/amd_hsmp.rst >> index fa1fc240e212..8a9d6fe7d4cf 100644 >> --- a/Documentation/arch/x86/amd_hsmp.rst >> +++ b/Documentation/arch/x86/amd_hsmp.rst >> @@ -8,6 +8,21 @@ Newer Fam19h(model 0x00-0x1f, 0x30-0x3f, 0x90-0x9f, >> 0xa0-0xaf), >> Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD support >> system management functionality via HSMP (Host System Management Port). >> >> +The Fam1Ah client platforms are supported as well: >> + >> + ============= ============= >> + Model Platform >> + ============= ============= >> + 0x80-0x87 Medusa1 >> + 0x88-0x8f Olympic Ridge >> + 0xe0-0xe3 Medusa2 >> + ============= ============= >> + >> +They share one mailbox and speak the Ryzen Master SMC message set >> instead >> +of the server HSMP message set, so the message IDs accepted on them are >> +the HSMP_CLIENT_* ones listed in arch/x86/include/uapi/asm/amd_hsmp.h. >> +The character device and ioctl interface described below are the same. >> + >> The Host System Management Port (HSMP) is an interface to provide >> OS-level software with access to system management functions via a >> set of mailbox registers. >> @@ -17,7 +32,8 @@ More details on the interface can be found in chapter >> Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50 >> >> >> -HSMP interface is supported on EPYC line of server CPUs and MI300A >> (APU). >> +HSMP interface is supported on EPYC line of server CPUs, MI300A (APU) >> and >> +the Fam1Ah client platforms listed above. >> >> >> HSMP device >> diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/ >> uapi/asm/amd_hsmp.h >> index eda336bfd3e9..b4c3ddb9d1c1 100644 >> --- a/arch/x86/include/uapi/asm/amd_hsmp.h >> +++ b/arch/x86/include/uapi/asm/amd_hsmp.h >> @@ -664,4 +664,141 @@ struct hsmp_telemetry_data { >> #define HSMP_IOCTL_GET_TELEMETRY_DATA \ >> _IOW(HSMP_BASE_IOCTL_NR, 1, struct hsmp_telemetry_data) >> >> +/* >> + * Client HSMP messages supported on the Family 1Ah client platforms: >> + * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and >> + * Models E0h-E3h (Medusa2). >> + * These map to Ryzen Master SMC (RMSMC) message IDs. >> + */ >> +enum hsmp_client_message_ids { >> + HSMP_CLIENT_TEST = 1, /* 01h Test message */ >> + HSMP_CLIENT_GET_SMU_VER, /* 02h SMU FW version */ >> + HSMP_CLIENT_GET_INTERFACE_VER, /* 03h Interface version */ >> + HSMP_CLIENT_GET_METRICS_TABLE_VER, /* 04h Metrics table >> version */ >> + HSMP_CLIENT_GET_METRICS_TABLE, /* 05h Metrics table log >> sample */ >> + HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR,/* 06h Metrics table >> DRAM address */ >> + HSMP_CLIENT_SET_CORE_PSM_MARGIN, /* 07h Set core PSM >> margin */ >> + HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN, /* 08h Set all core PSM >> margin */ >> + HSMP_CLIENT_SET_FAST_PPT_LIMIT, /* 09h Set fast PPT >> limit */ >> + HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT, /* 0Ah Set VRM VDD >> current limit */ >> + HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT,/* 0Bh Set VRM VDD max >> current limit */ >> + HSMP_CLIENT_SET_TJ_MAX, /* 0Ch Set TjMax */ >> + HSMP_CLIENT_SET_FIT_LIMIT_SCALAR, /* 0Dh Set FIT limit >> scalar */ >> + HSMP_CLIENT_ENABLE_OVERCLOCKING, /* 0Eh Enable >> overclocking */ >> + HSMP_CLIENT_DISABLE_OVERCLOCKING, /* 0Fh Disable >> overclocking */ >> + HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES,/* 10h Set OC freq all >> cores */ >> + HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE,/* 11h Set OC freq per >> core */ >> + HSMP_CLIENT_SET_OVERCLOCK_VID, /* 12h Set OC VID */ >> + HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY,/* 13h Set FCLK OC on- >> the-fly */ >> + HSMP_CLIENT_GET_CORE_PERF_ORDER, /* 14h Get core >> performance order */ >> + HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT, /* 15h Set sustained >> power limit */ >> + HSMP_CLIENT_SET_SLOW_PPT_LIMIT, /* 16h Set slow PPT >> limit */ >> + HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT,/* 17h Set VRM GFX max >> current limit */ >> + HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT, /* 18h Set VRM SOC >> current limit */ >> + HSMP_CLIENT_SET_FAST_SPM_LIMIT, /* 19h Set fast SPM >> limit */ >> + HSMP_CLIENT_SET_SLOW_SPM_LIMIT, /* 1Ah Set slow SPM >> limit */ >> + HSMP_CLIENT_GET_CORE_PSM_MARGIN, /* 1Bh Get core PSM >> margin */ >> + HSMP_CLIENT_GET_GFX_PSM_MARGIN, /* 1Ch Get GFX PSM >> margin */ >> + HSMP_CLIENT_SPARE_0X1D, /* 1Dh Spare */ >> + HSMP_CLIENT_SPARE_0X1E, /* 1Eh Spare */ >> + HSMP_CLIENT_SPARE_0X1F, /* 1Fh Spare */ >> + HSMP_CLIENT_SPARE_0X20, /* 20h Spare */ >> + HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID,/* 21h Set GfxClk >> overdrive by freq/VID */ >> + HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE, /* 22h Disable GfxClk >> overdrive */ >> + HSMP_CLIENT_SET_GFX_PSM_MARGIN, /* 23h Set GFX PSM >> margin */ >> + HSMP_CLIENT_SET_CCLK_FMAX_OFFSET, /* 24h Set CCLK Fmax >> offset */ >> + HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET,/* 25h Set core power >> limit offset */ >> + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND, /* 26h Add extra PSM >> guardband */ >> + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX,/* 27h Add extra PSM >> guardband GFX */ >> + HSMP_CLIENT_SET_GFXCLK_FMAX, /* 28h Set GfxClk Fmax */ >> + HSMP_CLIENT_MSG_ID_MAX, >> +}; >> + >> +/* Client message descriptor table (Family 1Ah Models 80h-8Fh, E0h- >> E3h) */ >> +static const struct hsmp_msg_desc hsmp_client_msg_desc_table[] >> + __attribute__((unused)) = { >> + /* RESERVED */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_TEST */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_SMU_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_INTERFACE_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE */ >> + {0, 0, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR */ >> + {0, 3, HSMP_GET}, >> + /* HSMP_CLIENT_SET_CORE_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FAST_PPT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_TJ_MAX */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FIT_LIMIT_SCALAR */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ENABLE_OVERCLOCKING */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_DISABLE_OVERCLOCKING */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_VID */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_GET_CORE_PERF_ORDER */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_SLOW_PPT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FAST_SPM_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_SLOW_SPM_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_GET_CORE_PSM_MARGIN */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_GFX_PSM_MARGIN */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_SPARE_0X1D */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X1E */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X1F */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X20 */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_GFX_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_CCLK_FMAX_OFFSET */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_GFXCLK_FMAX */ >> + {1, 0, HSMP_SET}, >> +}; >> + >> #endif /*_ASM_X86_AMD_HSMP_H_*/ >> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/ >> x86/amd/hsmp/hsmp.c >> index 5e123a4ecea9..9b65dbd47965 100644 >> --- a/drivers/platform/x86/amd/hsmp/hsmp.c >> +++ b/drivers/platform/x86/amd/hsmp/hsmp.c >> @@ -45,7 +45,67 @@ >> */ >> #define CHECK_GET_BIT BIT(31) >> >> -static struct hsmp_plat_device hsmp_pdev; >> +static const struct hsmp_mbaddr_info hsmp_mbinfo_server = { >> + .base_addr = SMN_HSMP_BASE, >> + .msg_id_off = SMN_HSMP_MSG_ID, >> + .msg_resp_off = SMN_HSMP_MSG_RESP, >> + .msg_arg_off = SMN_HSMP_MSG_DATA, >> +}; >> + >> +/* RM: Ryzen Master, the SMC interface these client addresses belong >> to */ >> +static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { >> + .base_addr = SMN_HSMP_BASE_RM, >> + .msg_id_off = SMN_HSMP_MSG_ID_RM, >> + .msg_resp_off = SMN_HSMP_MSG_RESP_RM, >> + .msg_arg_off = SMN_HSMP_MSG_DATA_RM, >> +}; >> + >> +/* >> + * The descriptors carry only the message IDs the driver issues >> itself, which >> + * are also the only ones the two message sets have in common. >> Messages named >> + * by userspace in an ioctl need no entry here, as that path already >> resolves >> + * the ID against the running platform's table. >> + */ >> +static const struct hsmp_plat_desc hsmp_desc_server = { >> + .mbinfo = &hsmp_mbinfo_server, >> + .msg_desc = hsmp_msg_desc_table, >> + .num_msgs = HSMP_MSG_ID_MAX, >> + .test_msg = HSMP_TEST, >> + .proto_ver_msg = HSMP_GET_PROTO_VER, >> +}; >> + >> +/* The client drives a different mailbox with the Ryzen Master SMC >> message set */ >> +static const struct hsmp_plat_desc hsmp_desc_client = { >> + .mbinfo = &hsmp_mbinfo_client, >> + .msg_desc = hsmp_client_msg_desc_table, >> + .num_msgs = HSMP_CLIENT_MSG_ID_MAX, >> + .test_msg = HSMP_CLIENT_TEST, >> + .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, >> +}; >> + >> +static struct hsmp_plat_device hsmp_pdev = { >> + /* >> + * Overridden for the client in get_hsmp_pdev(), which every driver >> + * calls before it touches a socket. Defaulting it here rather >> than >> + * leaving it NULL keeps the message plumbing safe for any >> caller that >> + * arrives without going through there. >> + */ >> + .desc = &hsmp_desc_server, >> +}; >> + >> +/* >> + * Callers must bounds-check msg_id against desc->num_msgs first; >> this returns >> + * NULL rather than indexing out of range if they did not. >> + */ >> +static inline const struct hsmp_msg_desc *get_msg_desc(u32 msg_id) >> +{ >> + const struct hsmp_plat_desc *desc = hsmp_pdev.desc; >> + >> + if (msg_id >= desc->num_msgs) >> + return NULL; >> + >> + return &desc->msg_desc[msg_id]; >> +} >> >> /* >> * Gates the AMD HSMP data plane against socket bring-up and teardown. >> @@ -184,30 +244,37 @@ static int __hsmp_send_message(struct >> hsmp_socket *sock, struct hsmp_message *ms >> >> static int validate_message(struct hsmp_message *msg) >> { >> + const struct hsmp_msg_desc *desc; >> + >> /* msg_id against valid range of message IDs */ >> - if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) >> + if (msg->msg_id < hsmp_pdev.desc->test_msg || >> + msg->msg_id >= hsmp_pdev.desc->num_msgs) >> + return -ENOMSG; >> + >> + desc = get_msg_desc(msg->msg_id); >> + if (!desc) >> return -ENOMSG; >> >> /* msg_id is a reserved message ID */ >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) >> + if (desc->type == HSMP_RSVD) >> return -ENOMSG; >> >> /* >> * num_args passed by user should match the num_args specified in >> * message description table. >> */ >> - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) >> + if (msg->num_args != desc->num_args) >> return -EINVAL; >> >> /* >> * As the HSMP protocol evolves, newer platforms may define more >> * response arguments for existing messages. Use an upper-bound >> * check so that older userspace callers requesting fewer response >> - * words than what the current hsmp_msg_desc_table[] defines are >> - * still accepted, while rejecting requests that exceed the >> - * hardware capability. >> + * words than what the current descriptor table defines are still >> + * accepted, while rejecting requests that exceed the hardware >> + * capability. >> */ >> - if (msg->response_sz > hsmp_msg_desc_table[msg- >> >msg_id].response_sz) >> + if (msg->response_sz > desc->response_sz) >> return -EINVAL; >> >> return 0; >> @@ -316,7 +383,7 @@ int hsmp_test(u16 sock_ind, u32 value) >> * Test the hsmp port by performing TEST command. The test message >> * takes one argument and returns the value of that argument + 1. >> */ >> - msg.msg_id = HSMP_TEST; >> + msg.msg_id = hsmp_pdev.desc->test_msg; >> msg.num_args = 1; >> msg.response_sz = 1; >> msg.args[0] = value; >> @@ -340,10 +407,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); >> >> static bool is_get_msg(struct hsmp_message *msg) >> { >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) >> + const struct hsmp_msg_desc *desc = get_msg_desc(msg->msg_id); >> + >> + if (!desc) >> + return false; >> + >> + if (desc->type == HSMP_GET) >> return true; >> >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && >> + if (desc->type == HSMP_SET_GET && >> (msg->args[0] & CHECK_GET_BIT)) >> return true; >> >> @@ -354,6 +426,7 @@ static long hsmp_ioctl_msg(struct file *fp, >> unsigned long arg) >> { >> int __user *arguser = (int __user *)arg; >> struct hsmp_message msg = { 0 }; >> + const struct hsmp_msg_desc *desc; >> int ret; >> >> if (copy_struct_from_user(&msg, sizeof(msg), arguser, >> sizeof(struct hsmp_message))) >> @@ -361,23 +434,28 @@ static long hsmp_ioctl_msg(struct file *fp, >> unsigned long arg) >> >> /* >> * Check msg_id is within the range of supported msg ids >> - * i.e within the array bounds of hsmp_msg_desc_table >> + * i.e within the array bounds of the platform's descriptor table >> */ >> - if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) >> + if (msg.msg_id < hsmp_pdev.desc->test_msg || >> + msg.msg_id >= hsmp_pdev.desc->num_msgs) >> return -ENOMSG; >> >> /* >> * Sanitize the user-controlled msg_id against speculative >> * execution. The bounds check above retires the out-of-range >> * case with -ENOMSG, but a mispredicted branch can still let the >> - * CPU speculatively use msg_id as an index into >> - * hsmp_msg_desc_table[] (here and in validate_message() / >> + * CPU speculatively use msg_id as an index into the message >> + * descriptor tables (here and in validate_message() / >> * is_get_msg() called downstream via hsmp_send_message()), and >> * pull arbitrary kernel memory into the cache (Spectre v1, >> * CVE-2017-5753). Clamp once into msg.msg_id so every downstream >> * dereference sees the sanitized value. >> */ >> - msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); >> + msg.msg_id = array_index_nospec(msg.msg_id, hsmp_pdev.desc- >> >num_msgs); >> + >> + desc = get_msg_desc(msg.msg_id); >> + if (!desc || desc->type == HSMP_RSVD) >> + return -ENOMSG; >> >> switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { >> case FMODE_WRITE: >> @@ -410,7 +488,7 @@ static long hsmp_ioctl_msg(struct file *fp, >> unsigned long arg) >> if (ret) >> return ret; >> >> - if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { >> + if (desc->response_sz > 0) { >> /* Copy results back to user for get/monitor commands */ >> if (copy_to_user(arguser, &msg, sizeof(struct >> hsmp_message))) >> return -EFAULT; >> @@ -687,9 +765,9 @@ int hsmp_cache_proto_ver(u16 sock_ind) >> struct hsmp_message msg = { 0 }; >> int ret; >> >> - msg.msg_id = HSMP_GET_PROTO_VER; >> + msg.msg_id = hsmp_pdev.desc->proto_ver_msg; >> msg.sock_ind = sock_ind; >> - msg.response_sz = >> hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; >> + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; >> >> ret = hsmp_send_message_locked(&msg); >> if (!ret) >> @@ -735,6 +813,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, >> "AMD_HSMP"); >> >> struct hsmp_plat_device *get_hsmp_pdev(void) >> { >> + /* >> + * Both drivers call this before they bring any socket up, and >> the CPU >> + * this selects on is fixed long before either can load, so >> resolving >> + * the platform description here is enough to have it in place for >> + * every mailbox access and for the probe paths themselves. >> + */ >> + if (is_client_platform()) >> + hsmp_pdev.desc = &hsmp_desc_client; >> + >> return &hsmp_pdev; >> } >> EXPORT_SYMBOL_NS_GPL(get_hsmp_pdev, "AMD_HSMP"); >> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/ >> x86/amd/hsmp/hsmp.h >> index 8dbff16a87b1..b4bd9800dee1 100644 >> --- a/drivers/platform/x86/amd/hsmp/hsmp.h >> +++ b/drivers/platform/x86/amd/hsmp/hsmp.h >> @@ -10,6 +10,8 @@ >> #ifndef HSMP_H >> #define HSMP_H >> >> +#include <asm/amd/hsmp.h> >> + >> #include <linux/compiler_types.h> >> #include <linux/device.h> >> #include <linux/hwmon.h> >> @@ -17,6 +19,7 @@ >> #include <linux/miscdevice.h> >> #include <linux/mutex.h> >> #include <linux/pci.h> >> +#include <linux/processor.h> >> #include <linux/rwsem.h> >> #include <linux/semaphore.h> >> #include <linux/sysfs.h> >> @@ -32,6 +35,51 @@ >> >> #define DRIVER_VERSION "2.6" >> >> +/* >> + * To access a specific HSMP mailbox register, s/w writes the SMN >> address of the >> + * HSMP mailbox register into the SMN_INDEX register, and reads/ >> writes the >> + * SMN_DATA reg. Below are the SMN addresses of the HSMP mailbox >> registers in >> + * SMU address space, as consumed by the fixed mailbox descriptors in >> hsmp.c. >> + * >> + * The server set is used by the non-ACPI path only; the ACPI path >> discovers the >> + * equivalent values from _CRS/_DSD. The _RM set is the client one, >> named for >> + * the Ryzen Master SMC (RMSMC) interface whose mailbox it addresses, >> and is >> + * used by both paths because the client exposes no _CRS/_DSD to >> discover them >> + * from. The two sets share a base and differ only in the register >> offsets >> + * behind it, but are kept separate so each stays tied to the >> platform it was >> + * documented for. >> + */ >> +#define SMN_HSMP_BASE 0x03B00000 >> +#define SMN_HSMP_MSG_ID 0x00010534 >> +#define SMN_HSMP_MSG_ID_F1A_M0H 0x00010934 >> +#define SMN_HSMP_MSG_RESP 0x00010980 >> +#define SMN_HSMP_MSG_DATA 0x000109E0 >> + >> +/* _RM: Ryzen Master SMC, the client mailbox */ >> +#define SMN_HSMP_BASE_RM 0x03B00000 >> +#define SMN_HSMP_MSG_ID_RM 0x00010A8C >> +#define SMN_HSMP_MSG_RESP_RM 0x00010A90 >> +#define SMN_HSMP_MSG_DATA_RM 0x00010A94 >> + >> +/* >> + * Family 1Ah client platforms use the Ryzen Master SMC (client) >> mailbox and >> + * message set instead of the server HSMP interface: >> + * >> + * Models 80h-87h Medusa1 >> + * Models 88h-8Fh Olympic Ridge >> + * Models E0h-E3h Medusa2 >> + * >> + * They all share the SMN_HSMP_*_RM mailbox addresses above and the same >> + * message set, so one predicate covers them and the two adjacent >> Model 8xh >> + * ranges are tested as one. >> + */ >> +static inline bool is_client_platform(void) >> +{ >> + return boot_cpu_data.x86 == 0x1A && >> + ((boot_cpu_data.x86_model >= 0x80 && >> boot_cpu_data.x86_model <= 0x8F) || >> + (boot_cpu_data.x86_model >= 0xE0 && >> boot_cpu_data.x86_model <= 0xE3)); >> +} >> + >> struct hsmp_mbaddr_info { >> u32 base_addr; >> u32 msg_id_off; >> @@ -40,6 +88,30 @@ struct hsmp_mbaddr_info { >> u32 size; >> }; >> >> +/* >> + * Per-platform description of the mailbox and the message set behind >> it. >> + * Resolved once from the running CPU and reached through hsmp_pdev- >> >desc, so >> + * neither the message plumbing nor the probe paths have to re-derive >> which >> + * platform they are on. >> + * >> + * @mbinfo: fixed SMN mailbox addresses. The ACPI path uses >> these >> + * only where firmware exposes no _CRS/_DSD to read >> them >> + * from, i.e. on the client. >> + * @msg_desc: message descriptor table, indexed by >> message ID. >> + * @num_msgs: number of entries in @msg_desc, and so >> the exclusive >> + * upper bound on a valid message ID. >> + * @test_msg: no-op message used to probe the >> mailbox. Also the >> + * lowest valid message ID, as ID 0 is reserved. >> + * @proto_ver_msg: returns the interface version. >> + */ >> +struct hsmp_plat_desc { >> + const struct hsmp_mbaddr_info *mbinfo; >> + const struct hsmp_msg_desc *msg_desc; >> + u32 num_msgs; >> + u32 test_msg; >> + u32 proto_ver_msg; >> +}; >> + >> struct hsmp_socket { >> struct bin_attribute hsmp_attr; >> struct hsmp_mbaddr_info mbinfo; >> @@ -59,6 +131,7 @@ struct hsmp_socket { >> struct hsmp_plat_device { >> struct miscdevice mdev; >> struct hsmp_socket *sock; >> + const struct hsmp_plat_desc *desc; >> u32 proto_ver; >> u16 num_sockets; >> }; >> diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/ >> x86/amd/hsmp/plat.c >> index e9b2b809c0f5..7423c98a7b3e 100644 >> --- a/drivers/platform/x86/amd/hsmp/plat.c >> +++ b/drivers/platform/x86/amd/hsmp/plat.c >> @@ -29,17 +29,6 @@ >> >> #define DRIVER_NAME "amd_hsmp" >> >> -/* >> - * To access specific HSMP mailbox register, s/w writes the SMN >> address of HSMP mailbox >> - * register into the SMN_INDEX register, and reads/writes the >> SMN_DATA reg. >> - * Below are required SMN address for HSMP Mailbox register offsets >> in SMU address space >> - */ >> -#define SMN_HSMP_BASE 0x3B00000 >> -#define SMN_HSMP_MSG_ID 0x0010534 >> -#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 >> -#define SMN_HSMP_MSG_RESP 0x0010980 >> -#define SMN_HSMP_MSG_DATA 0x00109E0 >> - >> static struct hsmp_plat_device *hsmp_pdev; >> >> static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, >> @@ -157,20 +146,18 @@ static int init_platform_device(struct device *dev) >> sock = &hsmp_pdev->sock[i]; >> sock->sock_ind = i; >> sock->dev = dev; >> - sock->mbinfo.base_addr = SMN_HSMP_BASE; >> sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; >> + sock->mbinfo = *hsmp_pdev->desc->mbinfo; >> >> /* >> * This is a transitional change from non-ACPI to ACPI, >> only >> * family 0x1A, model 0x00 platform is supported for >> both ACPI and non-ACPI. >> + * It is the one part whose message-id register sits at a >> + * different offset on this path than the descriptor gives. >> */ >> if (is_f1a_m0h()) >> sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; >> - else >> - sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; >> >> - sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; >> - sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; >> sema_init(&sock->hsmp_sem, 1); >> >> /* Test the hsmp interface on each socket */ >> @@ -184,8 +171,18 @@ static int init_platform_device(struct device *dev) >> >> ret = hsmp_cache_proto_ver(i); >> if (ret) { >> - dev_err(dev, "Failed to read HSMP protocol >> version\n"); >> - return ret; >> + /* >> + * Some client SMU builds reject the interface- >> version >> + * query with "invalid message" even though the >> mailbox >> + * is functional, as the test message above just >> + * showed. Keep going there instead of failing the >> + * probe: proto_ver only gates server-side >> features. >> + */ >> + if (!is_client_platform()) { >> + dev_err(dev, "Failed to read HSMP >> protocol version\n"); >> + return ret; >> + } >> + dev_warn(dev, "Interface version query >> unsupported on client SMU; continuing\n"); >> } >> >> if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { >> @@ -317,6 +314,12 @@ static bool legacy_hsmp_support(void) >> case 0x1A: >> switch (boot_cpu_data.x86_model) { >> case 0x00 ... 0x0F: >> + /* >> + * Client platforms: 80h-87h (Medusa1), 88h-8Fh (Olympic >> + * Ridge), E0h-E3h (Medusa2) >> + */ >> + case 0x80 ... 0x8F: >> + case 0xE0 ... 0xE3: >> return true; >> default: >> return false; ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah 2026-07-30 16:43 ` Armin Wolf 2026-07-31 4:27 ` M K, Muralidhara @ 2026-07-31 7:00 ` Shyam Sundar S K 2026-07-31 10:29 ` Armin Wolf 1 sibling, 1 reply; 22+ messages in thread From: Shyam Sundar S K @ 2026-07-31 7:00 UTC (permalink / raw) To: Armin Wolf, Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 30-07-2026 22:13, Armin Wolf wrote: > Am 29.07.26 um 18:40 schrieb Muralidhara M K: > >> Add non-ACPI HSMP mailbox support, Ryzen Master SMC client message >> descriptors, and platform routing for the Family 1Ah client platforms: >> >> Models 80h-87h Medusa1 >> Models 88h-8Fh Olympic Ridge >> Models E0h-E3h Medusa2 >> >> All three share the same client mailbox addresses and message set, so a >> single is_client_platform() predicate covers them, and the two adjacent >> Model 8xh ranges are tested as one. >> >> The client parts drive a different mailbox from the server HSMP >> interface. They expose no _DSD/_CRS to discover the mailbox, so the SMN >> addresses are fixed in hsmp.h and shared by the platform and ACPI >> probes, and they speak the Ryzen Master SMC (RMSMC) message set rather >> than the server message IDs. Add that message set and its descriptor >> table to the UAPI header. >> >> Two message descriptor tables now exist, so every lookup has to pick the >> one matching the running platform. Rather than test the platform at each >> lookup, describe the platform once: struct hsmp_plat_desc holds the >> mailbox addresses, the descriptor table and its size, and the IDs of the >> messages the driver issues itself. get_hsmp_pdev() resolves it, which >> both drivers call before they bring any socket up and which runs on a >> CPU that is fixed long before either can load. The server descriptor is >> also the static initialiser, so the message plumbing cannot reach a NULL >> descriptor even if a future caller arrives another way. >> >> get_msg_desc() then indexes the table behind that pointer, and >> validate_message(), is_get_msg(), hsmp_ioctl_msg(), hsmp_test() and >> hsmp_cache_proto_ver() read the bound and the message IDs from the >> descriptor. get_msg_desc() returns NULL rather than indexing out of >> range if a caller skipped the bounds check, and the Spectre v1 clamp in >> hsmp_ioctl_msg() uses the running platform's table size. >> >> In init_platform_device() the mailbox setup becomes a copy of the >> descriptor's addresses. The F1Ah M0h message-id offset stays behind >> is_f1a_m0h() as an override, because it applies only to this non-ACPI >> path while the descriptor is shared with the ACPI one. It cannot disturb >> the client mailbox: it requires model <= 0x0F and the client models are >> all >= 0x80. The server mailbox addresses move from plat.c to hsmp.h so >> both address sets sit with the descriptors that consume them; the values >> are unchanged, written to the same width as the client set they now sit >> beside. >> >> Since this is the change that lets the platform driver match the client >> models, it also has to let the client probe survive the interface-version >> query. Some client SMU builds reject HSMP_CLIENT_GET_INTERFACE_VER with >> "invalid message" even though the mailbox works, as the test message >> issued just before it shows, so treat that failure as non-fatal on the >> client and register the socket anyway. Nothing on the client depends on >> the reported version: hsmp_pdev->proto_ver gates the metrics_bin >> attribute and the protocol_version sysfs file, both of which describe >> the server interface. The query stays fatal on server parts. >> >> The client descriptor table carries __attribute__((unused)), matching >> hsmp_msg_desc_table[]. Both are defined in a UAPI header that several >> translation units include without using the table, so without it >> -Wunused-const-variable fires on plat.c, acpi.c and hwmon.c. >> >> Document the client platforms in Documentation/arch/x86/amd_hsmp.rst, >> which until now described HSMP as available only on the EPYC server >> parts and MI300A. The character device and ioctl interface are unchanged >> there; what differs is the accepted message IDs, so the text points at >> the HSMP_CLIENT_* set in the UAPI header. > > Hi, > > i am asking myself if there exist a connection between the Ryzen Master SMC > message ids and the AMD_ACPI WMI device: > > [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Class to Query/Set a CLKValue"), guid("{95F20B61-3D44-41F8-87E1-03CBF94C4E8F}")] > class AMD_ACPI { > [key, read] string InstanceName; > [read] boolean Active; > > [WmiMethodId(1), Implemented, read, write, Description("Get version")] void QueryVersion([out, Description("version")] uint32 version); > [WmiMethodId(2), Implemented, read, write, Description("WMI function init.")] void Init([out, Description("status")] uint8 status); > [WmiMethodId(3), Implemented, read, write, Description("Get BIOS ACPI support function table.")] void GetObjectID([out, Description("Get BIOS support Object ID table")] OIDPackage pack); > [WmiMethodId(4), Implemented, read, write, Description("Bios request data")] void Getdvalues([in, Description("ID used to identify")] uint32 ID, [out, Description("status")] GetdValuesPackage pack); > [WmiMethodId(5), Implemented, read, write, Description("Command Run")] void RunCommand([in, Description("In buffer")] uint8 Inbuf[8], [out, Description("Out buffer")] OutBuffer Outbuf); > [WmiMethodId(6), Implemented, read, write, Description("Get Min/Max/Stepping values")] void GetMinMaxSteppingValues([in, Description("ID used to identify")] uint32 ID, [out, Description("status")] GetMinMaxSteppingPackage pack); > [WmiMethodId(7), Implemented, read, write, Description("Get DIMM SPD data")] void GetDimmSpdData([in, Description("DIMM Index")] uint32 Index, [out, Description("SPD Data Buffer")] DimmSpdBuffer DimmSpdData); > [WmiMethodId(8), Implemented, read, write, Description("Get BIOS ACPI support function table2.")] void GetObjectID2([out, Description("Get BIOS support Object ID table2")] OIDPackage pack); > [WmiMethodId(9), Implemented, read, write, Description("Get RMP table")] void GetRmpData([out, Description("Get RMP table")] RMPPackage pack); > [WmiMethodId(10), Implemented, read, write, Description("Get OPP Heaer")] void GetOppTable([out, Description("Get Opp Heaer")] OPPHeader pack); > [WmiMethodId(11), Implemented, read, write, Description("Get OPP Data")] void GetOppData([in, Description("Profile Index")] uint32 Index, [out, Description("Opp Profile")] OPPProfole pack); > }; > > I own multiple devices with such an interface, and i would _love_ to add support > for them. Thanks Armin. WMI stuff was on my TODO for a long time and recently I have completed the work, please expect it to get posted next cycle - Shall include you to the review once I send out the series. Thanks, Shyam > > Thanks, > Armin Wolf > >> Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> >> --- >> Documentation/arch/x86/amd_hsmp.rst | 18 +++- >> arch/x86/include/uapi/asm/amd_hsmp.h | 137 +++++++++++++++++++++++++++ >> drivers/platform/x86/amd/hsmp/hsmp.c | 125 ++++++++++++++++++++---- >> drivers/platform/x86/amd/hsmp/hsmp.h | 73 ++++++++++++++ >> drivers/platform/x86/amd/hsmp/plat.c | 39 ++++---- >> 5 files changed, 354 insertions(+), 38 deletions(-) >> >> diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst >> index fa1fc240e212..8a9d6fe7d4cf 100644 >> --- a/Documentation/arch/x86/amd_hsmp.rst >> +++ b/Documentation/arch/x86/amd_hsmp.rst >> @@ -8,6 +8,21 @@ Newer Fam19h(model 0x00-0x1f, 0x30-0x3f, 0x90-0x9f, 0xa0-0xaf), >> Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD support >> system management functionality via HSMP (Host System Management Port). >> +The Fam1Ah client platforms are supported as well: >> + >> + ============= ============= >> + Model Platform >> + ============= ============= >> + 0x80-0x87 Medusa1 >> + 0x88-0x8f Olympic Ridge >> + 0xe0-0xe3 Medusa2 >> + ============= ============= >> + >> +They share one mailbox and speak the Ryzen Master SMC message set instead >> +of the server HSMP message set, so the message IDs accepted on them are >> +the HSMP_CLIENT_* ones listed in arch/x86/include/uapi/asm/amd_hsmp.h. >> +The character device and ioctl interface described below are the same. >> + >> The Host System Management Port (HSMP) is an interface to provide >> OS-level software with access to system management functions via a >> set of mailbox registers. >> @@ -17,7 +32,8 @@ More details on the interface can be found in chapter >> Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50 >> -HSMP interface is supported on EPYC line of server CPUs and MI300A (APU). >> +HSMP interface is supported on EPYC line of server CPUs, MI300A (APU) and >> +the Fam1Ah client platforms listed above. >> HSMP device >> diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h >> index eda336bfd3e9..b4c3ddb9d1c1 100644 >> --- a/arch/x86/include/uapi/asm/amd_hsmp.h >> +++ b/arch/x86/include/uapi/asm/amd_hsmp.h >> @@ -664,4 +664,141 @@ struct hsmp_telemetry_data { >> #define HSMP_IOCTL_GET_TELEMETRY_DATA \ >> _IOW(HSMP_BASE_IOCTL_NR, 1, struct hsmp_telemetry_data) >> +/* >> + * Client HSMP messages supported on the Family 1Ah client platforms: >> + * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and >> + * Models E0h-E3h (Medusa2). >> + * These map to Ryzen Master SMC (RMSMC) message IDs. >> + */ >> +enum hsmp_client_message_ids { >> + HSMP_CLIENT_TEST = 1, /* 01h Test message */ >> + HSMP_CLIENT_GET_SMU_VER, /* 02h SMU FW version */ >> + HSMP_CLIENT_GET_INTERFACE_VER, /* 03h Interface version */ >> + HSMP_CLIENT_GET_METRICS_TABLE_VER, /* 04h Metrics table version */ >> + HSMP_CLIENT_GET_METRICS_TABLE, /* 05h Metrics table log sample */ >> + HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR,/* 06h Metrics table DRAM address */ >> + HSMP_CLIENT_SET_CORE_PSM_MARGIN, /* 07h Set core PSM margin */ >> + HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN, /* 08h Set all core PSM margin */ >> + HSMP_CLIENT_SET_FAST_PPT_LIMIT, /* 09h Set fast PPT limit */ >> + HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT, /* 0Ah Set VRM VDD current limit */ >> + HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT,/* 0Bh Set VRM VDD max current limit */ >> + HSMP_CLIENT_SET_TJ_MAX, /* 0Ch Set TjMax */ >> + HSMP_CLIENT_SET_FIT_LIMIT_SCALAR, /* 0Dh Set FIT limit scalar */ >> + HSMP_CLIENT_ENABLE_OVERCLOCKING, /* 0Eh Enable overclocking */ >> + HSMP_CLIENT_DISABLE_OVERCLOCKING, /* 0Fh Disable overclocking */ >> + HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES,/* 10h Set OC freq all cores */ >> + HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE,/* 11h Set OC freq per core */ >> + HSMP_CLIENT_SET_OVERCLOCK_VID, /* 12h Set OC VID */ >> + HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY,/* 13h Set FCLK OC on-the-fly */ >> + HSMP_CLIENT_GET_CORE_PERF_ORDER, /* 14h Get core performance order */ >> + HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT, /* 15h Set sustained power limit */ >> + HSMP_CLIENT_SET_SLOW_PPT_LIMIT, /* 16h Set slow PPT limit */ >> + HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT,/* 17h Set VRM GFX max current limit */ >> + HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT, /* 18h Set VRM SOC current limit */ >> + HSMP_CLIENT_SET_FAST_SPM_LIMIT, /* 19h Set fast SPM limit */ >> + HSMP_CLIENT_SET_SLOW_SPM_LIMIT, /* 1Ah Set slow SPM limit */ >> + HSMP_CLIENT_GET_CORE_PSM_MARGIN, /* 1Bh Get core PSM margin */ >> + HSMP_CLIENT_GET_GFX_PSM_MARGIN, /* 1Ch Get GFX PSM margin */ >> + HSMP_CLIENT_SPARE_0X1D, /* 1Dh Spare */ >> + HSMP_CLIENT_SPARE_0X1E, /* 1Eh Spare */ >> + HSMP_CLIENT_SPARE_0X1F, /* 1Fh Spare */ >> + HSMP_CLIENT_SPARE_0X20, /* 20h Spare */ >> + HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID,/* 21h Set GfxClk overdrive by freq/VID */ >> + HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE, /* 22h Disable GfxClk overdrive */ >> + HSMP_CLIENT_SET_GFX_PSM_MARGIN, /* 23h Set GFX PSM margin */ >> + HSMP_CLIENT_SET_CCLK_FMAX_OFFSET, /* 24h Set CCLK Fmax offset */ >> + HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET,/* 25h Set core power limit offset */ >> + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND, /* 26h Add extra PSM guardband */ >> + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX,/* 27h Add extra PSM guardband GFX */ >> + HSMP_CLIENT_SET_GFXCLK_FMAX, /* 28h Set GfxClk Fmax */ >> + HSMP_CLIENT_MSG_ID_MAX, >> +}; >> + >> +/* Client message descriptor table (Family 1Ah Models 80h-8Fh, E0h-E3h) */ >> +static const struct hsmp_msg_desc hsmp_client_msg_desc_table[] >> + __attribute__((unused)) = { >> + /* RESERVED */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_TEST */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_SMU_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_INTERFACE_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE_VER */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE */ >> + {0, 0, HSMP_GET}, >> + /* HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR */ >> + {0, 3, HSMP_GET}, >> + /* HSMP_CLIENT_SET_CORE_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FAST_PPT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_TJ_MAX */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FIT_LIMIT_SCALAR */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ENABLE_OVERCLOCKING */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_DISABLE_OVERCLOCKING */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_OVERCLOCK_VID */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_GET_CORE_PERF_ORDER */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_SLOW_PPT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_FAST_SPM_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_SLOW_SPM_LIMIT */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_GET_CORE_PSM_MARGIN */ >> + {1, 1, HSMP_GET}, >> + /* HSMP_CLIENT_GET_GFX_PSM_MARGIN */ >> + {0, 1, HSMP_GET}, >> + /* HSMP_CLIENT_SPARE_0X1D */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X1E */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X1F */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SPARE_0X20 */ >> + {0, 0, HSMP_RSVD}, >> + /* HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE */ >> + {0, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_GFX_PSM_MARGIN */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_CCLK_FMAX_OFFSET */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX */ >> + {1, 0, HSMP_SET}, >> + /* HSMP_CLIENT_SET_GFXCLK_FMAX */ >> + {1, 0, HSMP_SET}, >> +}; >> + >> #endif /*_ASM_X86_AMD_HSMP_H_*/ >> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c >> index 5e123a4ecea9..9b65dbd47965 100644 >> --- a/drivers/platform/x86/amd/hsmp/hsmp.c >> +++ b/drivers/platform/x86/amd/hsmp/hsmp.c >> @@ -45,7 +45,67 @@ >> */ >> #define CHECK_GET_BIT BIT(31) >> -static struct hsmp_plat_device hsmp_pdev; >> +static const struct hsmp_mbaddr_info hsmp_mbinfo_server = { >> + .base_addr = SMN_HSMP_BASE, >> + .msg_id_off = SMN_HSMP_MSG_ID, >> + .msg_resp_off = SMN_HSMP_MSG_RESP, >> + .msg_arg_off = SMN_HSMP_MSG_DATA, >> +}; >> + >> +/* RM: Ryzen Master, the SMC interface these client addresses belong to */ >> +static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { >> + .base_addr = SMN_HSMP_BASE_RM, >> + .msg_id_off = SMN_HSMP_MSG_ID_RM, >> + .msg_resp_off = SMN_HSMP_MSG_RESP_RM, >> + .msg_arg_off = SMN_HSMP_MSG_DATA_RM, >> +}; >> + >> +/* >> + * The descriptors carry only the message IDs the driver issues itself, which >> + * are also the only ones the two message sets have in common. Messages named >> + * by userspace in an ioctl need no entry here, as that path already resolves >> + * the ID against the running platform's table. >> + */ >> +static const struct hsmp_plat_desc hsmp_desc_server = { >> + .mbinfo = &hsmp_mbinfo_server, >> + .msg_desc = hsmp_msg_desc_table, >> + .num_msgs = HSMP_MSG_ID_MAX, >> + .test_msg = HSMP_TEST, >> + .proto_ver_msg = HSMP_GET_PROTO_VER, >> +}; >> + >> +/* The client drives a different mailbox with the Ryzen Master SMC message set */ >> +static const struct hsmp_plat_desc hsmp_desc_client = { >> + .mbinfo = &hsmp_mbinfo_client, >> + .msg_desc = hsmp_client_msg_desc_table, >> + .num_msgs = HSMP_CLIENT_MSG_ID_MAX, >> + .test_msg = HSMP_CLIENT_TEST, >> + .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, >> +}; >> + >> +static struct hsmp_plat_device hsmp_pdev = { >> + /* >> + * Overridden for the client in get_hsmp_pdev(), which every driver >> + * calls before it touches a socket. Defaulting it here rather than >> + * leaving it NULL keeps the message plumbing safe for any caller that >> + * arrives without going through there. >> + */ >> + .desc = &hsmp_desc_server, >> +}; >> + >> +/* >> + * Callers must bounds-check msg_id against desc->num_msgs first; this returns >> + * NULL rather than indexing out of range if they did not. >> + */ >> +static inline const struct hsmp_msg_desc *get_msg_desc(u32 msg_id) >> +{ >> + const struct hsmp_plat_desc *desc = hsmp_pdev.desc; >> + >> + if (msg_id >= desc->num_msgs) >> + return NULL; >> + >> + return &desc->msg_desc[msg_id]; >> +} >> /* >> * Gates the AMD HSMP data plane against socket bring-up and teardown. >> @@ -184,30 +244,37 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms >> static int validate_message(struct hsmp_message *msg) >> { >> + const struct hsmp_msg_desc *desc; >> + >> /* msg_id against valid range of message IDs */ >> - if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) >> + if (msg->msg_id < hsmp_pdev.desc->test_msg || >> + msg->msg_id >= hsmp_pdev.desc->num_msgs) >> + return -ENOMSG; >> + >> + desc = get_msg_desc(msg->msg_id); >> + if (!desc) >> return -ENOMSG; >> /* msg_id is a reserved message ID */ >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) >> + if (desc->type == HSMP_RSVD) >> return -ENOMSG; >> /* >> * num_args passed by user should match the num_args specified in >> * message description table. >> */ >> - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) >> + if (msg->num_args != desc->num_args) >> return -EINVAL; >> /* >> * As the HSMP protocol evolves, newer platforms may define more >> * response arguments for existing messages. Use an upper-bound >> * check so that older userspace callers requesting fewer response >> - * words than what the current hsmp_msg_desc_table[] defines are >> - * still accepted, while rejecting requests that exceed the >> - * hardware capability. >> + * words than what the current descriptor table defines are still >> + * accepted, while rejecting requests that exceed the hardware >> + * capability. >> */ >> - if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) >> + if (msg->response_sz > desc->response_sz) >> return -EINVAL; >> return 0; >> @@ -316,7 +383,7 @@ int hsmp_test(u16 sock_ind, u32 value) >> * Test the hsmp port by performing TEST command. The test message >> * takes one argument and returns the value of that argument + 1. >> */ >> - msg.msg_id = HSMP_TEST; >> + msg.msg_id = hsmp_pdev.desc->test_msg; >> msg.num_args = 1; >> msg.response_sz = 1; >> msg.args[0] = value; >> @@ -340,10 +407,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); >> static bool is_get_msg(struct hsmp_message *msg) >> { >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) >> + const struct hsmp_msg_desc *desc = get_msg_desc(msg->msg_id); >> + >> + if (!desc) >> + return false; >> + >> + if (desc->type == HSMP_GET) >> return true; >> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && >> + if (desc->type == HSMP_SET_GET && >> (msg->args[0] & CHECK_GET_BIT)) >> return true; >> @@ -354,6 +426,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) >> { >> int __user *arguser = (int __user *)arg; >> struct hsmp_message msg = { 0 }; >> + const struct hsmp_msg_desc *desc; >> int ret; >> if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct hsmp_message))) >> @@ -361,23 +434,28 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) >> /* >> * Check msg_id is within the range of supported msg ids >> - * i.e within the array bounds of hsmp_msg_desc_table >> + * i.e within the array bounds of the platform's descriptor table >> */ >> - if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) >> + if (msg.msg_id < hsmp_pdev.desc->test_msg || >> + msg.msg_id >= hsmp_pdev.desc->num_msgs) >> return -ENOMSG; >> /* >> * Sanitize the user-controlled msg_id against speculative >> * execution. The bounds check above retires the out-of-range >> * case with -ENOMSG, but a mispredicted branch can still let the >> - * CPU speculatively use msg_id as an index into >> - * hsmp_msg_desc_table[] (here and in validate_message() / >> + * CPU speculatively use msg_id as an index into the message >> + * descriptor tables (here and in validate_message() / >> * is_get_msg() called downstream via hsmp_send_message()), and >> * pull arbitrary kernel memory into the cache (Spectre v1, >> * CVE-2017-5753). Clamp once into msg.msg_id so every downstream >> * dereference sees the sanitized value. >> */ >> - msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); >> + msg.msg_id = array_index_nospec(msg.msg_id, hsmp_pdev.desc->num_msgs); >> + >> + desc = get_msg_desc(msg.msg_id); >> + if (!desc || desc->type == HSMP_RSVD) >> + return -ENOMSG; >> switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { >> case FMODE_WRITE: >> @@ -410,7 +488,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) >> if (ret) >> return ret; >> - if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { >> + if (desc->response_sz > 0) { >> /* Copy results back to user for get/monitor commands */ >> if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message))) >> return -EFAULT; >> @@ -687,9 +765,9 @@ int hsmp_cache_proto_ver(u16 sock_ind) >> struct hsmp_message msg = { 0 }; >> int ret; >> - msg.msg_id = HSMP_GET_PROTO_VER; >> + msg.msg_id = hsmp_pdev.desc->proto_ver_msg; >> msg.sock_ind = sock_ind; >> - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; >> + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; >> ret = hsmp_send_message_locked(&msg); >> if (!ret) >> @@ -735,6 +813,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, "AMD_HSMP"); >> struct hsmp_plat_device *get_hsmp_pdev(void) >> { >> + /* >> + * Both drivers call this before they bring any socket up, and the CPU >> + * this selects on is fixed long before either can load, so resolving >> + * the platform description here is enough to have it in place for >> + * every mailbox access and for the probe paths themselves. >> + */ >> + if (is_client_platform()) >> + hsmp_pdev.desc = &hsmp_desc_client; >> + >> return &hsmp_pdev; >> } >> EXPORT_SYMBOL_NS_GPL(get_hsmp_pdev, "AMD_HSMP"); >> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h >> index 8dbff16a87b1..b4bd9800dee1 100644 >> --- a/drivers/platform/x86/amd/hsmp/hsmp.h >> +++ b/drivers/platform/x86/amd/hsmp/hsmp.h >> @@ -10,6 +10,8 @@ >> #ifndef HSMP_H >> #define HSMP_H >> +#include <asm/amd/hsmp.h> >> + >> #include <linux/compiler_types.h> >> #include <linux/device.h> >> #include <linux/hwmon.h> >> @@ -17,6 +19,7 @@ >> #include <linux/miscdevice.h> >> #include <linux/mutex.h> >> #include <linux/pci.h> >> +#include <linux/processor.h> >> #include <linux/rwsem.h> >> #include <linux/semaphore.h> >> #include <linux/sysfs.h> >> @@ -32,6 +35,51 @@ >> #define DRIVER_VERSION "2.6" >> +/* >> + * To access a specific HSMP mailbox register, s/w writes the SMN address of the >> + * HSMP mailbox register into the SMN_INDEX register, and reads/writes the >> + * SMN_DATA reg. Below are the SMN addresses of the HSMP mailbox registers in >> + * SMU address space, as consumed by the fixed mailbox descriptors in hsmp.c. >> + * >> + * The server set is used by the non-ACPI path only; the ACPI path discovers the >> + * equivalent values from _CRS/_DSD. The _RM set is the client one, named for >> + * the Ryzen Master SMC (RMSMC) interface whose mailbox it addresses, and is >> + * used by both paths because the client exposes no _CRS/_DSD to discover them >> + * from. The two sets share a base and differ only in the register offsets >> + * behind it, but are kept separate so each stays tied to the platform it was >> + * documented for. >> + */ >> +#define SMN_HSMP_BASE 0x03B00000 >> +#define SMN_HSMP_MSG_ID 0x00010534 >> +#define SMN_HSMP_MSG_ID_F1A_M0H 0x00010934 >> +#define SMN_HSMP_MSG_RESP 0x00010980 >> +#define SMN_HSMP_MSG_DATA 0x000109E0 >> + >> +/* _RM: Ryzen Master SMC, the client mailbox */ >> +#define SMN_HSMP_BASE_RM 0x03B00000 >> +#define SMN_HSMP_MSG_ID_RM 0x00010A8C >> +#define SMN_HSMP_MSG_RESP_RM 0x00010A90 >> +#define SMN_HSMP_MSG_DATA_RM 0x00010A94 >> + >> +/* >> + * Family 1Ah client platforms use the Ryzen Master SMC (client) mailbox and >> + * message set instead of the server HSMP interface: >> + * >> + * Models 80h-87h Medusa1 >> + * Models 88h-8Fh Olympic Ridge >> + * Models E0h-E3h Medusa2 >> + * >> + * They all share the SMN_HSMP_*_RM mailbox addresses above and the same >> + * message set, so one predicate covers them and the two adjacent Model 8xh >> + * ranges are tested as one. >> + */ >> +static inline bool is_client_platform(void) >> +{ >> + return boot_cpu_data.x86 == 0x1A && >> + ((boot_cpu_data.x86_model >= 0x80 && boot_cpu_data.x86_model <= 0x8F) || >> + (boot_cpu_data.x86_model >= 0xE0 && boot_cpu_data.x86_model <= 0xE3)); >> +} >> + >> struct hsmp_mbaddr_info { >> u32 base_addr; >> u32 msg_id_off; >> @@ -40,6 +88,30 @@ struct hsmp_mbaddr_info { >> u32 size; >> }; >> +/* >> + * Per-platform description of the mailbox and the message set behind it. >> + * Resolved once from the running CPU and reached through hsmp_pdev->desc, so >> + * neither the message plumbing nor the probe paths have to re-derive which >> + * platform they are on. >> + * >> + * @mbinfo: fixed SMN mailbox addresses. The ACPI path uses these >> + * only where firmware exposes no _CRS/_DSD to read them >> + * from, i.e. on the client. >> + * @msg_desc: message descriptor table, indexed by message ID. >> + * @num_msgs: number of entries in @msg_desc, and so the exclusive >> + * upper bound on a valid message ID. >> + * @test_msg: no-op message used to probe the mailbox. Also the >> + * lowest valid message ID, as ID 0 is reserved. >> + * @proto_ver_msg: returns the interface version. >> + */ >> +struct hsmp_plat_desc { >> + const struct hsmp_mbaddr_info *mbinfo; >> + const struct hsmp_msg_desc *msg_desc; >> + u32 num_msgs; >> + u32 test_msg; >> + u32 proto_ver_msg; >> +}; >> + >> struct hsmp_socket { >> struct bin_attribute hsmp_attr; >> struct hsmp_mbaddr_info mbinfo; >> @@ -59,6 +131,7 @@ struct hsmp_socket { >> struct hsmp_plat_device { >> struct miscdevice mdev; >> struct hsmp_socket *sock; >> + const struct hsmp_plat_desc *desc; >> u32 proto_ver; >> u16 num_sockets; >> }; >> diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c >> index e9b2b809c0f5..7423c98a7b3e 100644 >> --- a/drivers/platform/x86/amd/hsmp/plat.c >> +++ b/drivers/platform/x86/amd/hsmp/plat.c >> @@ -29,17 +29,6 @@ >> #define DRIVER_NAME "amd_hsmp" >> -/* >> - * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox >> - * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg. >> - * Below are required SMN address for HSMP Mailbox register offsets in SMU address space >> - */ >> -#define SMN_HSMP_BASE 0x3B00000 >> -#define SMN_HSMP_MSG_ID 0x0010534 >> -#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 >> -#define SMN_HSMP_MSG_RESP 0x0010980 >> -#define SMN_HSMP_MSG_DATA 0x00109E0 >> - >> static struct hsmp_plat_device *hsmp_pdev; >> static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, >> @@ -157,20 +146,18 @@ static int init_platform_device(struct device *dev) >> sock = &hsmp_pdev->sock[i]; >> sock->sock_ind = i; >> sock->dev = dev; >> - sock->mbinfo.base_addr = SMN_HSMP_BASE; >> sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; >> + sock->mbinfo = *hsmp_pdev->desc->mbinfo; >> /* >> * This is a transitional change from non-ACPI to ACPI, only >> * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI. >> + * It is the one part whose message-id register sits at a >> + * different offset on this path than the descriptor gives. >> */ >> if (is_f1a_m0h()) >> sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; >> - else >> - sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; >> - sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; >> - sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; >> sema_init(&sock->hsmp_sem, 1); >> /* Test the hsmp interface on each socket */ >> @@ -184,8 +171,18 @@ static int init_platform_device(struct device *dev) >> ret = hsmp_cache_proto_ver(i); >> if (ret) { >> - dev_err(dev, "Failed to read HSMP protocol version\n"); >> - return ret; >> + /* >> + * Some client SMU builds reject the interface-version >> + * query with "invalid message" even though the mailbox >> + * is functional, as the test message above just >> + * showed. Keep going there instead of failing the >> + * probe: proto_ver only gates server-side features. >> + */ >> + if (!is_client_platform()) { >> + dev_err(dev, "Failed to read HSMP protocol version\n"); >> + return ret; >> + } >> + dev_warn(dev, "Interface version query unsupported on client SMU; continuing\n"); >> } >> if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { >> @@ -317,6 +314,12 @@ static bool legacy_hsmp_support(void) >> case 0x1A: >> switch (boot_cpu_data.x86_model) { >> case 0x00 ... 0x0F: >> + /* >> + * Client platforms: 80h-87h (Medusa1), 88h-8Fh (Olympic >> + * Ridge), E0h-E3h (Medusa2) >> + */ >> + case 0x80 ... 0x8F: >> + case 0xE0 ... 0xE3: >> return true; >> default: >> return false; > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah 2026-07-31 7:00 ` Shyam Sundar S K @ 2026-07-31 10:29 ` Armin Wolf 0 siblings, 0 replies; 22+ messages in thread From: Armin Wolf @ 2026-07-31 10:29 UTC (permalink / raw) To: Shyam Sundar S K, Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam Am 31.07.26 um 09:00 schrieb Shyam Sundar S K: > > On 30-07-2026 22:13, Armin Wolf wrote: >> Am 29.07.26 um 18:40 schrieb Muralidhara M K: >> >>> Add non-ACPI HSMP mailbox support, Ryzen Master SMC client message >>> descriptors, and platform routing for the Family 1Ah client platforms: >>> >>> Models 80h-87h Medusa1 >>> Models 88h-8Fh Olympic Ridge >>> Models E0h-E3h Medusa2 >>> >>> All three share the same client mailbox addresses and message set, so a >>> single is_client_platform() predicate covers them, and the two adjacent >>> Model 8xh ranges are tested as one. >>> >>> The client parts drive a different mailbox from the server HSMP >>> interface. They expose no _DSD/_CRS to discover the mailbox, so the SMN >>> addresses are fixed in hsmp.h and shared by the platform and ACPI >>> probes, and they speak the Ryzen Master SMC (RMSMC) message set rather >>> than the server message IDs. Add that message set and its descriptor >>> table to the UAPI header. >>> >>> Two message descriptor tables now exist, so every lookup has to pick the >>> one matching the running platform. Rather than test the platform at each >>> lookup, describe the platform once: struct hsmp_plat_desc holds the >>> mailbox addresses, the descriptor table and its size, and the IDs of the >>> messages the driver issues itself. get_hsmp_pdev() resolves it, which >>> both drivers call before they bring any socket up and which runs on a >>> CPU that is fixed long before either can load. The server descriptor is >>> also the static initialiser, so the message plumbing cannot reach a NULL >>> descriptor even if a future caller arrives another way. >>> >>> get_msg_desc() then indexes the table behind that pointer, and >>> validate_message(), is_get_msg(), hsmp_ioctl_msg(), hsmp_test() and >>> hsmp_cache_proto_ver() read the bound and the message IDs from the >>> descriptor. get_msg_desc() returns NULL rather than indexing out of >>> range if a caller skipped the bounds check, and the Spectre v1 clamp in >>> hsmp_ioctl_msg() uses the running platform's table size. >>> >>> In init_platform_device() the mailbox setup becomes a copy of the >>> descriptor's addresses. The F1Ah M0h message-id offset stays behind >>> is_f1a_m0h() as an override, because it applies only to this non-ACPI >>> path while the descriptor is shared with the ACPI one. It cannot disturb >>> the client mailbox: it requires model <= 0x0F and the client models are >>> all >= 0x80. The server mailbox addresses move from plat.c to hsmp.h so >>> both address sets sit with the descriptors that consume them; the values >>> are unchanged, written to the same width as the client set they now sit >>> beside. >>> >>> Since this is the change that lets the platform driver match the client >>> models, it also has to let the client probe survive the interface-version >>> query. Some client SMU builds reject HSMP_CLIENT_GET_INTERFACE_VER with >>> "invalid message" even though the mailbox works, as the test message >>> issued just before it shows, so treat that failure as non-fatal on the >>> client and register the socket anyway. Nothing on the client depends on >>> the reported version: hsmp_pdev->proto_ver gates the metrics_bin >>> attribute and the protocol_version sysfs file, both of which describe >>> the server interface. The query stays fatal on server parts. >>> >>> The client descriptor table carries __attribute__((unused)), matching >>> hsmp_msg_desc_table[]. Both are defined in a UAPI header that several >>> translation units include without using the table, so without it >>> -Wunused-const-variable fires on plat.c, acpi.c and hwmon.c. >>> >>> Document the client platforms in Documentation/arch/x86/amd_hsmp.rst, >>> which until now described HSMP as available only on the EPYC server >>> parts and MI300A. The character device and ioctl interface are unchanged >>> there; what differs is the accepted message IDs, so the text points at >>> the HSMP_CLIENT_* set in the UAPI header. >> Hi, >> >> i am asking myself if there exist a connection between the Ryzen Master SMC >> message ids and the AMD_ACPI WMI device: >> >> [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Class to Query/Set a CLKValue"), guid("{95F20B61-3D44-41F8-87E1-03CBF94C4E8F}")] >> class AMD_ACPI { >> [key, read] string InstanceName; >> [read] boolean Active; >> >> [WmiMethodId(1), Implemented, read, write, Description("Get version")] void QueryVersion([out, Description("version")] uint32 version); >> [WmiMethodId(2), Implemented, read, write, Description("WMI function init.")] void Init([out, Description("status")] uint8 status); >> [WmiMethodId(3), Implemented, read, write, Description("Get BIOS ACPI support function table.")] void GetObjectID([out, Description("Get BIOS support Object ID table")] OIDPackage pack); >> [WmiMethodId(4), Implemented, read, write, Description("Bios request data")] void Getdvalues([in, Description("ID used to identify")] uint32 ID, [out, Description("status")] GetdValuesPackage pack); >> [WmiMethodId(5), Implemented, read, write, Description("Command Run")] void RunCommand([in, Description("In buffer")] uint8 Inbuf[8], [out, Description("Out buffer")] OutBuffer Outbuf); >> [WmiMethodId(6), Implemented, read, write, Description("Get Min/Max/Stepping values")] void GetMinMaxSteppingValues([in, Description("ID used to identify")] uint32 ID, [out, Description("status")] GetMinMaxSteppingPackage pack); >> [WmiMethodId(7), Implemented, read, write, Description("Get DIMM SPD data")] void GetDimmSpdData([in, Description("DIMM Index")] uint32 Index, [out, Description("SPD Data Buffer")] DimmSpdBuffer DimmSpdData); >> [WmiMethodId(8), Implemented, read, write, Description("Get BIOS ACPI support function table2.")] void GetObjectID2([out, Description("Get BIOS support Object ID table2")] OIDPackage pack); >> [WmiMethodId(9), Implemented, read, write, Description("Get RMP table")] void GetRmpData([out, Description("Get RMP table")] RMPPackage pack); >> [WmiMethodId(10), Implemented, read, write, Description("Get OPP Heaer")] void GetOppTable([out, Description("Get Opp Heaer")] OPPHeader pack); >> [WmiMethodId(11), Implemented, read, write, Description("Get OPP Data")] void GetOppData([in, Description("Profile Index")] uint32 Index, [out, Description("Opp Profile")] OPPProfole pack); >> }; >> >> I own multiple devices with such an interface, and i would _love_ to add support >> for them. > Thanks Armin. WMI stuff was on my TODO for a long time and recently I have completed the work, please expect it to get posted next cycle - Shall include you to the review once I send out the series. > > Thanks, > Shyam > I will gladly participate in the review of those patches. Thanks, Armin Wolf >> Thanks, >> Armin Wolf >> >>> Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> >>> --- >>> Documentation/arch/x86/amd_hsmp.rst | 18 +++- >>> arch/x86/include/uapi/asm/amd_hsmp.h | 137 +++++++++++++++++++++++++++ >>> drivers/platform/x86/amd/hsmp/hsmp.c | 125 ++++++++++++++++++++---- >>> drivers/platform/x86/amd/hsmp/hsmp.h | 73 ++++++++++++++ >>> drivers/platform/x86/amd/hsmp/plat.c | 39 ++++---- >>> 5 files changed, 354 insertions(+), 38 deletions(-) >>> >>> diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst >>> index fa1fc240e212..8a9d6fe7d4cf 100644 >>> --- a/Documentation/arch/x86/amd_hsmp.rst >>> +++ b/Documentation/arch/x86/amd_hsmp.rst >>> @@ -8,6 +8,21 @@ Newer Fam19h(model 0x00-0x1f, 0x30-0x3f, 0x90-0x9f, 0xa0-0xaf), >>> Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD support >>> system management functionality via HSMP (Host System Management Port). >>> +The Fam1Ah client platforms are supported as well: >>> + >>> + ============= ============= >>> + Model Platform >>> + ============= ============= >>> + 0x80-0x87 Medusa1 >>> + 0x88-0x8f Olympic Ridge >>> + 0xe0-0xe3 Medusa2 >>> + ============= ============= >>> + >>> +They share one mailbox and speak the Ryzen Master SMC message set instead >>> +of the server HSMP message set, so the message IDs accepted on them are >>> +the HSMP_CLIENT_* ones listed in arch/x86/include/uapi/asm/amd_hsmp.h. >>> +The character device and ioctl interface described below are the same. >>> + >>> The Host System Management Port (HSMP) is an interface to provide >>> OS-level software with access to system management functions via a >>> set of mailbox registers. >>> @@ -17,7 +32,8 @@ More details on the interface can be found in chapter >>> Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50 >>> -HSMP interface is supported on EPYC line of server CPUs and MI300A (APU). >>> +HSMP interface is supported on EPYC line of server CPUs, MI300A (APU) and >>> +the Fam1Ah client platforms listed above. >>> HSMP device >>> diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h >>> index eda336bfd3e9..b4c3ddb9d1c1 100644 >>> --- a/arch/x86/include/uapi/asm/amd_hsmp.h >>> +++ b/arch/x86/include/uapi/asm/amd_hsmp.h >>> @@ -664,4 +664,141 @@ struct hsmp_telemetry_data { >>> #define HSMP_IOCTL_GET_TELEMETRY_DATA \ >>> _IOW(HSMP_BASE_IOCTL_NR, 1, struct hsmp_telemetry_data) >>> +/* >>> + * Client HSMP messages supported on the Family 1Ah client platforms: >>> + * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and >>> + * Models E0h-E3h (Medusa2). >>> + * These map to Ryzen Master SMC (RMSMC) message IDs. >>> + */ >>> +enum hsmp_client_message_ids { >>> + HSMP_CLIENT_TEST = 1, /* 01h Test message */ >>> + HSMP_CLIENT_GET_SMU_VER, /* 02h SMU FW version */ >>> + HSMP_CLIENT_GET_INTERFACE_VER, /* 03h Interface version */ >>> + HSMP_CLIENT_GET_METRICS_TABLE_VER, /* 04h Metrics table version */ >>> + HSMP_CLIENT_GET_METRICS_TABLE, /* 05h Metrics table log sample */ >>> + HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR,/* 06h Metrics table DRAM address */ >>> + HSMP_CLIENT_SET_CORE_PSM_MARGIN, /* 07h Set core PSM margin */ >>> + HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN, /* 08h Set all core PSM margin */ >>> + HSMP_CLIENT_SET_FAST_PPT_LIMIT, /* 09h Set fast PPT limit */ >>> + HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT, /* 0Ah Set VRM VDD current limit */ >>> + HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT,/* 0Bh Set VRM VDD max current limit */ >>> + HSMP_CLIENT_SET_TJ_MAX, /* 0Ch Set TjMax */ >>> + HSMP_CLIENT_SET_FIT_LIMIT_SCALAR, /* 0Dh Set FIT limit scalar */ >>> + HSMP_CLIENT_ENABLE_OVERCLOCKING, /* 0Eh Enable overclocking */ >>> + HSMP_CLIENT_DISABLE_OVERCLOCKING, /* 0Fh Disable overclocking */ >>> + HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES,/* 10h Set OC freq all cores */ >>> + HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE,/* 11h Set OC freq per core */ >>> + HSMP_CLIENT_SET_OVERCLOCK_VID, /* 12h Set OC VID */ >>> + HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY,/* 13h Set FCLK OC on-the-fly */ >>> + HSMP_CLIENT_GET_CORE_PERF_ORDER, /* 14h Get core performance order */ >>> + HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT, /* 15h Set sustained power limit */ >>> + HSMP_CLIENT_SET_SLOW_PPT_LIMIT, /* 16h Set slow PPT limit */ >>> + HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT,/* 17h Set VRM GFX max current limit */ >>> + HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT, /* 18h Set VRM SOC current limit */ >>> + HSMP_CLIENT_SET_FAST_SPM_LIMIT, /* 19h Set fast SPM limit */ >>> + HSMP_CLIENT_SET_SLOW_SPM_LIMIT, /* 1Ah Set slow SPM limit */ >>> + HSMP_CLIENT_GET_CORE_PSM_MARGIN, /* 1Bh Get core PSM margin */ >>> + HSMP_CLIENT_GET_GFX_PSM_MARGIN, /* 1Ch Get GFX PSM margin */ >>> + HSMP_CLIENT_SPARE_0X1D, /* 1Dh Spare */ >>> + HSMP_CLIENT_SPARE_0X1E, /* 1Eh Spare */ >>> + HSMP_CLIENT_SPARE_0X1F, /* 1Fh Spare */ >>> + HSMP_CLIENT_SPARE_0X20, /* 20h Spare */ >>> + HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID,/* 21h Set GfxClk overdrive by freq/VID */ >>> + HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE, /* 22h Disable GfxClk overdrive */ >>> + HSMP_CLIENT_SET_GFX_PSM_MARGIN, /* 23h Set GFX PSM margin */ >>> + HSMP_CLIENT_SET_CCLK_FMAX_OFFSET, /* 24h Set CCLK Fmax offset */ >>> + HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET,/* 25h Set core power limit offset */ >>> + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND, /* 26h Add extra PSM guardband */ >>> + HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX,/* 27h Add extra PSM guardband GFX */ >>> + HSMP_CLIENT_SET_GFXCLK_FMAX, /* 28h Set GfxClk Fmax */ >>> + HSMP_CLIENT_MSG_ID_MAX, >>> +}; >>> + >>> +/* Client message descriptor table (Family 1Ah Models 80h-8Fh, E0h-E3h) */ >>> +static const struct hsmp_msg_desc hsmp_client_msg_desc_table[] >>> + __attribute__((unused)) = { >>> + /* RESERVED */ >>> + {0, 0, HSMP_RSVD}, >>> + /* HSMP_CLIENT_TEST */ >>> + {1, 1, HSMP_GET}, >>> + /* HSMP_CLIENT_GET_SMU_VER */ >>> + {0, 1, HSMP_GET}, >>> + /* HSMP_CLIENT_GET_INTERFACE_VER */ >>> + {0, 1, HSMP_GET}, >>> + /* HSMP_CLIENT_GET_METRICS_TABLE_VER */ >>> + {0, 1, HSMP_GET}, >>> + /* HSMP_CLIENT_GET_METRICS_TABLE */ >>> + {0, 0, HSMP_GET}, >>> + /* HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR */ >>> + {0, 3, HSMP_GET}, >>> + /* HSMP_CLIENT_SET_CORE_PSM_MARGIN */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_ALL_CORE_PSM_MARGIN */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_FAST_PPT_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_VRM_VDD_CURRENT_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_TJ_MAX */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_FIT_LIMIT_SCALAR */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_ENABLE_OVERCLOCKING */ >>> + {0, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_DISABLE_OVERCLOCKING */ >>> + {0, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_ALL_CORES */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_OVERCLOCK_FREQ_PER_CORE */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_OVERCLOCK_VID */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_FCLK_OVERCLOCK_ON_THE_FLY */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_GET_CORE_PERF_ORDER */ >>> + {1, 1, HSMP_GET}, >>> + /* HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_SLOW_PPT_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_FAST_SPM_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_SLOW_SPM_LIMIT */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_GET_CORE_PSM_MARGIN */ >>> + {1, 1, HSMP_GET}, >>> + /* HSMP_CLIENT_GET_GFX_PSM_MARGIN */ >>> + {0, 1, HSMP_GET}, >>> + /* HSMP_CLIENT_SPARE_0X1D */ >>> + {0, 0, HSMP_RSVD}, >>> + /* HSMP_CLIENT_SPARE_0X1E */ >>> + {0, 0, HSMP_RSVD}, >>> + /* HSMP_CLIENT_SPARE_0X1F */ >>> + {0, 0, HSMP_RSVD}, >>> + /* HSMP_CLIENT_SPARE_0X20 */ >>> + {0, 0, HSMP_RSVD}, >>> + /* HSMP_CLIENT_SET_GFXCLK_OVERDRIVE_BY_FREQ_VID */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_DISABLE_GFXCLK_OVERDRIVE */ >>> + {0, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_GFX_PSM_MARGIN */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_CCLK_FMAX_OFFSET */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_CORE_POWER_LIMIT_OFFSET */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_ADD_EXTRA_PSM_GUARDBAND_GFX */ >>> + {1, 0, HSMP_SET}, >>> + /* HSMP_CLIENT_SET_GFXCLK_FMAX */ >>> + {1, 0, HSMP_SET}, >>> +}; >>> + >>> #endif /*_ASM_X86_AMD_HSMP_H_*/ >>> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c >>> index 5e123a4ecea9..9b65dbd47965 100644 >>> --- a/drivers/platform/x86/amd/hsmp/hsmp.c >>> +++ b/drivers/platform/x86/amd/hsmp/hsmp.c >>> @@ -45,7 +45,67 @@ >>> */ >>> #define CHECK_GET_BIT BIT(31) >>> -static struct hsmp_plat_device hsmp_pdev; >>> +static const struct hsmp_mbaddr_info hsmp_mbinfo_server = { >>> + .base_addr = SMN_HSMP_BASE, >>> + .msg_id_off = SMN_HSMP_MSG_ID, >>> + .msg_resp_off = SMN_HSMP_MSG_RESP, >>> + .msg_arg_off = SMN_HSMP_MSG_DATA, >>> +}; >>> + >>> +/* RM: Ryzen Master, the SMC interface these client addresses belong to */ >>> +static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { >>> + .base_addr = SMN_HSMP_BASE_RM, >>> + .msg_id_off = SMN_HSMP_MSG_ID_RM, >>> + .msg_resp_off = SMN_HSMP_MSG_RESP_RM, >>> + .msg_arg_off = SMN_HSMP_MSG_DATA_RM, >>> +}; >>> + >>> +/* >>> + * The descriptors carry only the message IDs the driver issues itself, which >>> + * are also the only ones the two message sets have in common. Messages named >>> + * by userspace in an ioctl need no entry here, as that path already resolves >>> + * the ID against the running platform's table. >>> + */ >>> +static const struct hsmp_plat_desc hsmp_desc_server = { >>> + .mbinfo = &hsmp_mbinfo_server, >>> + .msg_desc = hsmp_msg_desc_table, >>> + .num_msgs = HSMP_MSG_ID_MAX, >>> + .test_msg = HSMP_TEST, >>> + .proto_ver_msg = HSMP_GET_PROTO_VER, >>> +}; >>> + >>> +/* The client drives a different mailbox with the Ryzen Master SMC message set */ >>> +static const struct hsmp_plat_desc hsmp_desc_client = { >>> + .mbinfo = &hsmp_mbinfo_client, >>> + .msg_desc = hsmp_client_msg_desc_table, >>> + .num_msgs = HSMP_CLIENT_MSG_ID_MAX, >>> + .test_msg = HSMP_CLIENT_TEST, >>> + .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, >>> +}; >>> + >>> +static struct hsmp_plat_device hsmp_pdev = { >>> + /* >>> + * Overridden for the client in get_hsmp_pdev(), which every driver >>> + * calls before it touches a socket. Defaulting it here rather than >>> + * leaving it NULL keeps the message plumbing safe for any caller that >>> + * arrives without going through there. >>> + */ >>> + .desc = &hsmp_desc_server, >>> +}; >>> + >>> +/* >>> + * Callers must bounds-check msg_id against desc->num_msgs first; this returns >>> + * NULL rather than indexing out of range if they did not. >>> + */ >>> +static inline const struct hsmp_msg_desc *get_msg_desc(u32 msg_id) >>> +{ >>> + const struct hsmp_plat_desc *desc = hsmp_pdev.desc; >>> + >>> + if (msg_id >= desc->num_msgs) >>> + return NULL; >>> + >>> + return &desc->msg_desc[msg_id]; >>> +} >>> /* >>> * Gates the AMD HSMP data plane against socket bring-up and teardown. >>> @@ -184,30 +244,37 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms >>> static int validate_message(struct hsmp_message *msg) >>> { >>> + const struct hsmp_msg_desc *desc; >>> + >>> /* msg_id against valid range of message IDs */ >>> - if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) >>> + if (msg->msg_id < hsmp_pdev.desc->test_msg || >>> + msg->msg_id >= hsmp_pdev.desc->num_msgs) >>> + return -ENOMSG; >>> + >>> + desc = get_msg_desc(msg->msg_id); >>> + if (!desc) >>> return -ENOMSG; >>> /* msg_id is a reserved message ID */ >>> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) >>> + if (desc->type == HSMP_RSVD) >>> return -ENOMSG; >>> /* >>> * num_args passed by user should match the num_args specified in >>> * message description table. >>> */ >>> - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) >>> + if (msg->num_args != desc->num_args) >>> return -EINVAL; >>> /* >>> * As the HSMP protocol evolves, newer platforms may define more >>> * response arguments for existing messages. Use an upper-bound >>> * check so that older userspace callers requesting fewer response >>> - * words than what the current hsmp_msg_desc_table[] defines are >>> - * still accepted, while rejecting requests that exceed the >>> - * hardware capability. >>> + * words than what the current descriptor table defines are still >>> + * accepted, while rejecting requests that exceed the hardware >>> + * capability. >>> */ >>> - if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) >>> + if (msg->response_sz > desc->response_sz) >>> return -EINVAL; >>> return 0; >>> @@ -316,7 +383,7 @@ int hsmp_test(u16 sock_ind, u32 value) >>> * Test the hsmp port by performing TEST command. The test message >>> * takes one argument and returns the value of that argument + 1. >>> */ >>> - msg.msg_id = HSMP_TEST; >>> + msg.msg_id = hsmp_pdev.desc->test_msg; >>> msg.num_args = 1; >>> msg.response_sz = 1; >>> msg.args[0] = value; >>> @@ -340,10 +407,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); >>> static bool is_get_msg(struct hsmp_message *msg) >>> { >>> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) >>> + const struct hsmp_msg_desc *desc = get_msg_desc(msg->msg_id); >>> + >>> + if (!desc) >>> + return false; >>> + >>> + if (desc->type == HSMP_GET) >>> return true; >>> - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && >>> + if (desc->type == HSMP_SET_GET && >>> (msg->args[0] & CHECK_GET_BIT)) >>> return true; >>> @@ -354,6 +426,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) >>> { >>> int __user *arguser = (int __user *)arg; >>> struct hsmp_message msg = { 0 }; >>> + const struct hsmp_msg_desc *desc; >>> int ret; >>> if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct hsmp_message))) >>> @@ -361,23 +434,28 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) >>> /* >>> * Check msg_id is within the range of supported msg ids >>> - * i.e within the array bounds of hsmp_msg_desc_table >>> + * i.e within the array bounds of the platform's descriptor table >>> */ >>> - if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) >>> + if (msg.msg_id < hsmp_pdev.desc->test_msg || >>> + msg.msg_id >= hsmp_pdev.desc->num_msgs) >>> return -ENOMSG; >>> /* >>> * Sanitize the user-controlled msg_id against speculative >>> * execution. The bounds check above retires the out-of-range >>> * case with -ENOMSG, but a mispredicted branch can still let the >>> - * CPU speculatively use msg_id as an index into >>> - * hsmp_msg_desc_table[] (here and in validate_message() / >>> + * CPU speculatively use msg_id as an index into the message >>> + * descriptor tables (here and in validate_message() / >>> * is_get_msg() called downstream via hsmp_send_message()), and >>> * pull arbitrary kernel memory into the cache (Spectre v1, >>> * CVE-2017-5753). Clamp once into msg.msg_id so every downstream >>> * dereference sees the sanitized value. >>> */ >>> - msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); >>> + msg.msg_id = array_index_nospec(msg.msg_id, hsmp_pdev.desc->num_msgs); >>> + >>> + desc = get_msg_desc(msg.msg_id); >>> + if (!desc || desc->type == HSMP_RSVD) >>> + return -ENOMSG; >>> switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { >>> case FMODE_WRITE: >>> @@ -410,7 +488,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) >>> if (ret) >>> return ret; >>> - if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { >>> + if (desc->response_sz > 0) { >>> /* Copy results back to user for get/monitor commands */ >>> if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message))) >>> return -EFAULT; >>> @@ -687,9 +765,9 @@ int hsmp_cache_proto_ver(u16 sock_ind) >>> struct hsmp_message msg = { 0 }; >>> int ret; >>> - msg.msg_id = HSMP_GET_PROTO_VER; >>> + msg.msg_id = hsmp_pdev.desc->proto_ver_msg; >>> msg.sock_ind = sock_ind; >>> - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; >>> + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; >>> ret = hsmp_send_message_locked(&msg); >>> if (!ret) >>> @@ -735,6 +813,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, "AMD_HSMP"); >>> struct hsmp_plat_device *get_hsmp_pdev(void) >>> { >>> + /* >>> + * Both drivers call this before they bring any socket up, and the CPU >>> + * this selects on is fixed long before either can load, so resolving >>> + * the platform description here is enough to have it in place for >>> + * every mailbox access and for the probe paths themselves. >>> + */ >>> + if (is_client_platform()) >>> + hsmp_pdev.desc = &hsmp_desc_client; >>> + >>> return &hsmp_pdev; >>> } >>> EXPORT_SYMBOL_NS_GPL(get_hsmp_pdev, "AMD_HSMP"); >>> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h >>> index 8dbff16a87b1..b4bd9800dee1 100644 >>> --- a/drivers/platform/x86/amd/hsmp/hsmp.h >>> +++ b/drivers/platform/x86/amd/hsmp/hsmp.h >>> @@ -10,6 +10,8 @@ >>> #ifndef HSMP_H >>> #define HSMP_H >>> +#include <asm/amd/hsmp.h> >>> + >>> #include <linux/compiler_types.h> >>> #include <linux/device.h> >>> #include <linux/hwmon.h> >>> @@ -17,6 +19,7 @@ >>> #include <linux/miscdevice.h> >>> #include <linux/mutex.h> >>> #include <linux/pci.h> >>> +#include <linux/processor.h> >>> #include <linux/rwsem.h> >>> #include <linux/semaphore.h> >>> #include <linux/sysfs.h> >>> @@ -32,6 +35,51 @@ >>> #define DRIVER_VERSION "2.6" >>> +/* >>> + * To access a specific HSMP mailbox register, s/w writes the SMN address of the >>> + * HSMP mailbox register into the SMN_INDEX register, and reads/writes the >>> + * SMN_DATA reg. Below are the SMN addresses of the HSMP mailbox registers in >>> + * SMU address space, as consumed by the fixed mailbox descriptors in hsmp.c. >>> + * >>> + * The server set is used by the non-ACPI path only; the ACPI path discovers the >>> + * equivalent values from _CRS/_DSD. The _RM set is the client one, named for >>> + * the Ryzen Master SMC (RMSMC) interface whose mailbox it addresses, and is >>> + * used by both paths because the client exposes no _CRS/_DSD to discover them >>> + * from. The two sets share a base and differ only in the register offsets >>> + * behind it, but are kept separate so each stays tied to the platform it was >>> + * documented for. >>> + */ >>> +#define SMN_HSMP_BASE 0x03B00000 >>> +#define SMN_HSMP_MSG_ID 0x00010534 >>> +#define SMN_HSMP_MSG_ID_F1A_M0H 0x00010934 >>> +#define SMN_HSMP_MSG_RESP 0x00010980 >>> +#define SMN_HSMP_MSG_DATA 0x000109E0 >>> + >>> +/* _RM: Ryzen Master SMC, the client mailbox */ >>> +#define SMN_HSMP_BASE_RM 0x03B00000 >>> +#define SMN_HSMP_MSG_ID_RM 0x00010A8C >>> +#define SMN_HSMP_MSG_RESP_RM 0x00010A90 >>> +#define SMN_HSMP_MSG_DATA_RM 0x00010A94 >>> + >>> +/* >>> + * Family 1Ah client platforms use the Ryzen Master SMC (client) mailbox and >>> + * message set instead of the server HSMP interface: >>> + * >>> + * Models 80h-87h Medusa1 >>> + * Models 88h-8Fh Olympic Ridge >>> + * Models E0h-E3h Medusa2 >>> + * >>> + * They all share the SMN_HSMP_*_RM mailbox addresses above and the same >>> + * message set, so one predicate covers them and the two adjacent Model 8xh >>> + * ranges are tested as one. >>> + */ >>> +static inline bool is_client_platform(void) >>> +{ >>> + return boot_cpu_data.x86 == 0x1A && >>> + ((boot_cpu_data.x86_model >= 0x80 && boot_cpu_data.x86_model <= 0x8F) || >>> + (boot_cpu_data.x86_model >= 0xE0 && boot_cpu_data.x86_model <= 0xE3)); >>> +} >>> + >>> struct hsmp_mbaddr_info { >>> u32 base_addr; >>> u32 msg_id_off; >>> @@ -40,6 +88,30 @@ struct hsmp_mbaddr_info { >>> u32 size; >>> }; >>> +/* >>> + * Per-platform description of the mailbox and the message set behind it. >>> + * Resolved once from the running CPU and reached through hsmp_pdev->desc, so >>> + * neither the message plumbing nor the probe paths have to re-derive which >>> + * platform they are on. >>> + * >>> + * @mbinfo: fixed SMN mailbox addresses. The ACPI path uses these >>> + * only where firmware exposes no _CRS/_DSD to read them >>> + * from, i.e. on the client. >>> + * @msg_desc: message descriptor table, indexed by message ID. >>> + * @num_msgs: number of entries in @msg_desc, and so the exclusive >>> + * upper bound on a valid message ID. >>> + * @test_msg: no-op message used to probe the mailbox. Also the >>> + * lowest valid message ID, as ID 0 is reserved. >>> + * @proto_ver_msg: returns the interface version. >>> + */ >>> +struct hsmp_plat_desc { >>> + const struct hsmp_mbaddr_info *mbinfo; >>> + const struct hsmp_msg_desc *msg_desc; >>> + u32 num_msgs; >>> + u32 test_msg; >>> + u32 proto_ver_msg; >>> +}; >>> + >>> struct hsmp_socket { >>> struct bin_attribute hsmp_attr; >>> struct hsmp_mbaddr_info mbinfo; >>> @@ -59,6 +131,7 @@ struct hsmp_socket { >>> struct hsmp_plat_device { >>> struct miscdevice mdev; >>> struct hsmp_socket *sock; >>> + const struct hsmp_plat_desc *desc; >>> u32 proto_ver; >>> u16 num_sockets; >>> }; >>> diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c >>> index e9b2b809c0f5..7423c98a7b3e 100644 >>> --- a/drivers/platform/x86/amd/hsmp/plat.c >>> +++ b/drivers/platform/x86/amd/hsmp/plat.c >>> @@ -29,17 +29,6 @@ >>> #define DRIVER_NAME "amd_hsmp" >>> -/* >>> - * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox >>> - * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg. >>> - * Below are required SMN address for HSMP Mailbox register offsets in SMU address space >>> - */ >>> -#define SMN_HSMP_BASE 0x3B00000 >>> -#define SMN_HSMP_MSG_ID 0x0010534 >>> -#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 >>> -#define SMN_HSMP_MSG_RESP 0x0010980 >>> -#define SMN_HSMP_MSG_DATA 0x00109E0 >>> - >>> static struct hsmp_plat_device *hsmp_pdev; >>> static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, >>> @@ -157,20 +146,18 @@ static int init_platform_device(struct device *dev) >>> sock = &hsmp_pdev->sock[i]; >>> sock->sock_ind = i; >>> sock->dev = dev; >>> - sock->mbinfo.base_addr = SMN_HSMP_BASE; >>> sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; >>> + sock->mbinfo = *hsmp_pdev->desc->mbinfo; >>> /* >>> * This is a transitional change from non-ACPI to ACPI, only >>> * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI. >>> + * It is the one part whose message-id register sits at a >>> + * different offset on this path than the descriptor gives. >>> */ >>> if (is_f1a_m0h()) >>> sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; >>> - else >>> - sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; >>> - sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; >>> - sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; >>> sema_init(&sock->hsmp_sem, 1); >>> /* Test the hsmp interface on each socket */ >>> @@ -184,8 +171,18 @@ static int init_platform_device(struct device *dev) >>> ret = hsmp_cache_proto_ver(i); >>> if (ret) { >>> - dev_err(dev, "Failed to read HSMP protocol version\n"); >>> - return ret; >>> + /* >>> + * Some client SMU builds reject the interface-version >>> + * query with "invalid message" even though the mailbox >>> + * is functional, as the test message above just >>> + * showed. Keep going there instead of failing the >>> + * probe: proto_ver only gates server-side features. >>> + */ >>> + if (!is_client_platform()) { >>> + dev_err(dev, "Failed to read HSMP protocol version\n"); >>> + return ret; >>> + } >>> + dev_warn(dev, "Interface version query unsupported on client SMU; continuing\n"); >>> } >>> if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { >>> @@ -317,6 +314,12 @@ static bool legacy_hsmp_support(void) >>> case 0x1A: >>> switch (boot_cpu_data.x86_model) { >>> case 0x00 ... 0x0F: >>> + /* >>> + * Client platforms: 80h-87h (Medusa1), 88h-8Fh (Olympic >>> + * Ridge), E0h-E3h (Medusa2) >>> + */ >>> + case 0x80 ... 0x8F: >>> + case 0xE0 ... 0xE3: >>> return true; >>> default: >>> return false; > ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/7] platform/x86/amd/hsmp: Route metric table through the client messages 2026-07-29 16:40 [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support Muralidhara M K 2026-07-29 16:40 ` [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah Muralidhara M K @ 2026-07-29 16:40 ` Muralidhara M K 2026-07-29 21:29 ` Mario Limonciello 2026-07-29 16:40 ` [PATCH 3/7] platform/x86/amd/hsmp: Add ACPI client support via the SMNR method Muralidhara M K ` (4 subsequent siblings) 6 siblings, 1 reply; 22+ messages in thread From: Muralidhara M K @ 2026-07-29 16:40 UTC (permalink / raw) To: ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam, Muralidhara M K Wire the client metric table and metrics DRAM address messages into the metric table read path for the Family 1Ah client platforms (Models 80h-8Fh and E0h-E3h), and initialise the metric table on the platform driver probe. The client reaches its metric table through the Ryzen Master SMC message set, so add the two message IDs to struct hsmp_plat_desc and have hsmp_metric_tbl_read_locked() and hsmp_get_tbl_dram_base() take them from there. Unlike the test and version queries, these two are not numbered alike in the two sets - 24h/25h on the server against 05h/06h on the client - which is why they have to come from the descriptor rather than a shared constant. Fetch the metric table on the client whatever interface version was reported. That query is already non-fatal there, since some client SMU builds reject it while the mailbox works, so hsmp_pdev->proto_ver cannot gate the client metric table the way HSMP_PROTO_VER6 gates it on the server. Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> --- drivers/platform/x86/amd/hsmp/hsmp.c | 16 ++++++++++++---- drivers/platform/x86/amd/hsmp/hsmp.h | 4 ++++ drivers/platform/x86/amd/hsmp/plat.c | 7 ++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 9b65dbd47965..2326506929a4 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -65,6 +65,10 @@ static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { * are also the only ones the two message sets have in common. Messages named * by userspace in an ioctl need no entry here, as that path already resolves * the ID against the running platform's table. + * + * Being common does not mean being numbered alike. The test and version + * queries are 01h and 03h in both sets, but the two metric table messages are + * 24h/25h on the server against 05h/06h on the client. */ static const struct hsmp_plat_desc hsmp_desc_server = { .mbinfo = &hsmp_mbinfo_server, @@ -72,6 +76,8 @@ static const struct hsmp_plat_desc hsmp_desc_server = { .num_msgs = HSMP_MSG_ID_MAX, .test_msg = HSMP_TEST, .proto_ver_msg = HSMP_GET_PROTO_VER, + .metric_tbl_msg = HSMP_GET_METRIC_TABLE, + .metric_dram_msg = HSMP_GET_METRIC_TABLE_DRAM_ADDR, }; /* The client drives a different mailbox with the Ryzen Master SMC message set */ @@ -81,6 +87,8 @@ static const struct hsmp_plat_desc hsmp_desc_client = { .num_msgs = HSMP_CLIENT_MSG_ID_MAX, .test_msg = HSMP_CLIENT_TEST, .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, + .metric_tbl_msg = HSMP_CLIENT_GET_METRICS_TABLE, + .metric_dram_msg = HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR, }; static struct hsmp_plat_device hsmp_pdev = { @@ -643,11 +651,11 @@ static ssize_t hsmp_metric_tbl_read_locked(struct hsmp_socket *sock, char *buf, return -EINVAL; } - msg.msg_id = HSMP_GET_METRIC_TABLE; + msg.msg_id = hsmp_pdev.desc->metric_tbl_msg; msg.sock_ind = sock->sock_ind; /* - * HSMP_GET_METRIC_TABLE makes firmware refill this socket's shared + * The metric table message makes firmware refill this socket's shared * metric DRAM region, which is then copied out below. Hold the * per-socket lock across the fill-and-copy so concurrent readers of the * same socket cannot return a torn snapshot. @@ -713,8 +721,8 @@ int hsmp_get_tbl_dram_base(u16 sock_ind) int ret; msg.sock_ind = sock_ind; - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_METRIC_TABLE_DRAM_ADDR].response_sz; - msg.msg_id = HSMP_GET_METRIC_TABLE_DRAM_ADDR; + msg.msg_id = hsmp_pdev.desc->metric_dram_msg; + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; ret = hsmp_send_message_locked(&msg); if (ret) diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h index b4bd9800dee1..c46d53dbcd43 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.h +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -103,6 +103,8 @@ struct hsmp_mbaddr_info { * @test_msg: no-op message used to probe the mailbox. Also the * lowest valid message ID, as ID 0 is reserved. * @proto_ver_msg: returns the interface version. + * @metric_tbl_msg: refills this socket's metric table in DRAM. + * @metric_dram_msg: returns the DRAM address of the metric table. */ struct hsmp_plat_desc { const struct hsmp_mbaddr_info *mbinfo; @@ -110,6 +112,8 @@ struct hsmp_plat_desc { u32 num_msgs; u32 test_msg; u32 proto_ver_msg; + u32 metric_tbl_msg; + u32 metric_dram_msg; }; struct hsmp_socket { diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index 7423c98a7b3e..bc29b0ec18f7 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -185,7 +185,12 @@ static int init_platform_device(struct device *dev) dev_warn(dev, "Interface version query unsupported on client SMU; continuing\n"); } - if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { + /* + * On the client the metric table is fetched with + * HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR whatever interface + * version was reported, since that query may be unsupported. + */ + if (is_client_platform() || hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { ret = hsmp_get_tbl_dram_base(i); if (ret) dev_info(dev, "Failed to init metric table\n"); -- 2.34.1 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 2/7] platform/x86/amd/hsmp: Route metric table through the client messages 2026-07-29 16:40 ` [PATCH 2/7] platform/x86/amd/hsmp: Route metric table through the client messages Muralidhara M K @ 2026-07-29 21:29 ` Mario Limonciello 2026-07-30 9:42 ` M K, Muralidhara 0 siblings, 1 reply; 22+ messages in thread From: Mario Limonciello @ 2026-07-29 21:29 UTC (permalink / raw) To: Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/29/26 11:40, Muralidhara M K wrote: > Wire the client metric table and metrics DRAM address messages into the > metric table read path for the Family 1Ah client platforms (Models > 80h-8Fh and E0h-E3h), and initialise the metric table on the platform > driver probe. > > The client reaches its metric table through the Ryzen Master SMC message > set, so add the two message IDs to struct hsmp_plat_desc and have > hsmp_metric_tbl_read_locked() and hsmp_get_tbl_dram_base() take them > from there. Unlike the test and version queries, these two are not > numbered alike in the two sets - 24h/25h on the server against 05h/06h > on the client - which is why they have to come from the descriptor > rather than a shared constant. > > Fetch the metric table on the client whatever interface version was > reported. That query is already non-fatal there, since some client SMU > builds reject it while the mailbox works, so hsmp_pdev->proto_ver cannot > gate the client metric table the way HSMP_PROTO_VER6 gates it on the > server. > > Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> > --- > drivers/platform/x86/amd/hsmp/hsmp.c | 16 ++++++++++++---- > drivers/platform/x86/amd/hsmp/hsmp.h | 4 ++++ > drivers/platform/x86/amd/hsmp/plat.c | 7 ++++++- > 3 files changed, 22 insertions(+), 5 deletions(-) > > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c > index 9b65dbd47965..2326506929a4 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.c > +++ b/drivers/platform/x86/amd/hsmp/hsmp.c > @@ -65,6 +65,10 @@ static const struct hsmp_mbaddr_info hsmp_mbinfo_client = { > * are also the only ones the two message sets have in common. Messages named > * by userspace in an ioctl need no entry here, as that path already resolves > * the ID against the running platform's table. > + * > + * Being common does not mean being numbered alike. The test and version > + * queries are 01h and 03h in both sets, but the two metric table messages are > + * 24h/25h on the server against 05h/06h on the client. > */ > static const struct hsmp_plat_desc hsmp_desc_server = { > .mbinfo = &hsmp_mbinfo_server, > @@ -72,6 +76,8 @@ static const struct hsmp_plat_desc hsmp_desc_server = { > .num_msgs = HSMP_MSG_ID_MAX, > .test_msg = HSMP_TEST, > .proto_ver_msg = HSMP_GET_PROTO_VER, > + .metric_tbl_msg = HSMP_GET_METRIC_TABLE, > + .metric_dram_msg = HSMP_GET_METRIC_TABLE_DRAM_ADDR, > }; > > /* The client drives a different mailbox with the Ryzen Master SMC message set */ > @@ -81,6 +87,8 @@ static const struct hsmp_plat_desc hsmp_desc_client = { > .num_msgs = HSMP_CLIENT_MSG_ID_MAX, > .test_msg = HSMP_CLIENT_TEST, > .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, > + .metric_tbl_msg = HSMP_CLIENT_GET_METRICS_TABLE, > + .metric_dram_msg = HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR, > }; > > static struct hsmp_plat_device hsmp_pdev = { > @@ -643,11 +651,11 @@ static ssize_t hsmp_metric_tbl_read_locked(struct hsmp_socket *sock, char *buf, > return -EINVAL; > } > > - msg.msg_id = HSMP_GET_METRIC_TABLE; > + msg.msg_id = hsmp_pdev.desc->metric_tbl_msg; > msg.sock_ind = sock->sock_ind; > > /* > - * HSMP_GET_METRIC_TABLE makes firmware refill this socket's shared > + * The metric table message makes firmware refill this socket's shared > * metric DRAM region, which is then copied out below. Hold the > * per-socket lock across the fill-and-copy so concurrent readers of the > * same socket cannot return a torn snapshot. > @@ -713,8 +721,8 @@ int hsmp_get_tbl_dram_base(u16 sock_ind) > int ret; > > msg.sock_ind = sock_ind; > - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_METRIC_TABLE_DRAM_ADDR].response_sz; > - msg.msg_id = HSMP_GET_METRIC_TABLE_DRAM_ADDR; > + msg.msg_id = hsmp_pdev.desc->metric_dram_msg; > + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; > > ret = hsmp_send_message_locked(&msg); > if (ret) > diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h > index b4bd9800dee1..c46d53dbcd43 100644 > --- a/drivers/platform/x86/amd/hsmp/hsmp.h > +++ b/drivers/platform/x86/amd/hsmp/hsmp.h > @@ -103,6 +103,8 @@ struct hsmp_mbaddr_info { > * @test_msg: no-op message used to probe the mailbox. Also the > * lowest valid message ID, as ID 0 is reserved. > * @proto_ver_msg: returns the interface version. > + * @metric_tbl_msg: refills this socket's metric table in DRAM. > + * @metric_dram_msg: returns the DRAM address of the metric table. > */ > struct hsmp_plat_desc { > const struct hsmp_mbaddr_info *mbinfo; > @@ -110,6 +112,8 @@ struct hsmp_plat_desc { > u32 num_msgs; > u32 test_msg; > u32 proto_ver_msg; > + u32 metric_tbl_msg; > + u32 metric_dram_msg; > }; > > struct hsmp_socket { > diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c > index 7423c98a7b3e..bc29b0ec18f7 100644 > --- a/drivers/platform/x86/amd/hsmp/plat.c > +++ b/drivers/platform/x86/amd/hsmp/plat.c > @@ -185,7 +185,12 @@ static int init_platform_device(struct device *dev) > dev_warn(dev, "Interface version query unsupported on client SMU; continuing\n"); > } > > - if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { > + /* > + * On the client the metric table is fetched with > + * HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR whatever interface > + * version was reported, since that query may be unsupported. > + */ What version was reported on client platforms? Since it's the first time client is being supported with HSMP, maybe it's best that client should be reporting 6. Then this part of the patch could get dropped later. > + if (is_client_platform() || hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { > ret = hsmp_get_tbl_dram_base(i); > if (ret) > dev_info(dev, "Failed to init metric table\n"); ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 2/7] platform/x86/amd/hsmp: Route metric table through the client messages 2026-07-29 21:29 ` Mario Limonciello @ 2026-07-30 9:42 ` M K, Muralidhara 0 siblings, 0 replies; 22+ messages in thread From: M K, Muralidhara @ 2026-07-30 9:42 UTC (permalink / raw) To: Mario Limonciello, Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/30/2026 2:59 AM, Mario Limonciello wrote: > > > On 7/29/26 11:40, Muralidhara M K wrote: >> Wire the client metric table and metrics DRAM address messages into the >> metric table read path for the Family 1Ah client platforms (Models >> 80h-8Fh and E0h-E3h), and initialise the metric table on the platform >> driver probe. >> >> The client reaches its metric table through the Ryzen Master SMC message >> set, so add the two message IDs to struct hsmp_plat_desc and have >> hsmp_metric_tbl_read_locked() and hsmp_get_tbl_dram_base() take them >> from there. Unlike the test and version queries, these two are not >> numbered alike in the two sets - 24h/25h on the server against 05h/06h >> on the client - which is why they have to come from the descriptor >> rather than a shared constant. >> >> Fetch the metric table on the client whatever interface version was >> reported. That query is already non-fatal there, since some client SMU >> builds reject it while the mailbox works, so hsmp_pdev->proto_ver cannot >> gate the client metric table the way HSMP_PROTO_VER6 gates it on the >> server. >> >> Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> >> --- >> drivers/platform/x86/amd/hsmp/hsmp.c | 16 ++++++++++++---- >> drivers/platform/x86/amd/hsmp/hsmp.h | 4 ++++ >> drivers/platform/x86/amd/hsmp/plat.c | 7 ++++++- >> 3 files changed, 22 insertions(+), 5 deletions(-) >> >> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/ >> x86/amd/hsmp/hsmp.c >> index 9b65dbd47965..2326506929a4 100644 >> --- a/drivers/platform/x86/amd/hsmp/hsmp.c >> +++ b/drivers/platform/x86/amd/hsmp/hsmp.c >> @@ -65,6 +65,10 @@ static const struct hsmp_mbaddr_info >> hsmp_mbinfo_client = { >> * are also the only ones the two message sets have in common. >> Messages named >> * by userspace in an ioctl need no entry here, as that path already >> resolves >> * the ID against the running platform's table. >> + * >> + * Being common does not mean being numbered alike. The test and >> version >> + * queries are 01h and 03h in both sets, but the two metric table >> messages are >> + * 24h/25h on the server against 05h/06h on the client. >> */ >> static const struct hsmp_plat_desc hsmp_desc_server = { >> .mbinfo = &hsmp_mbinfo_server, >> @@ -72,6 +76,8 @@ static const struct hsmp_plat_desc hsmp_desc_server = { >> .num_msgs = HSMP_MSG_ID_MAX, >> .test_msg = HSMP_TEST, >> .proto_ver_msg = HSMP_GET_PROTO_VER, >> + .metric_tbl_msg = HSMP_GET_METRIC_TABLE, >> + .metric_dram_msg = HSMP_GET_METRIC_TABLE_DRAM_ADDR, >> }; >> /* The client drives a different mailbox with the Ryzen Master SMC >> message set */ >> @@ -81,6 +87,8 @@ static const struct hsmp_plat_desc hsmp_desc_client = { >> .num_msgs = HSMP_CLIENT_MSG_ID_MAX, >> .test_msg = HSMP_CLIENT_TEST, >> .proto_ver_msg = HSMP_CLIENT_GET_INTERFACE_VER, >> + .metric_tbl_msg = HSMP_CLIENT_GET_METRICS_TABLE, >> + .metric_dram_msg = HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR, >> }; >> static struct hsmp_plat_device hsmp_pdev = { >> @@ -643,11 +651,11 @@ static ssize_t >> hsmp_metric_tbl_read_locked(struct hsmp_socket *sock, char *buf, >> return -EINVAL; >> } >> - msg.msg_id = HSMP_GET_METRIC_TABLE; >> + msg.msg_id = hsmp_pdev.desc->metric_tbl_msg; >> msg.sock_ind = sock->sock_ind; >> /* >> - * HSMP_GET_METRIC_TABLE makes firmware refill this socket's shared >> + * The metric table message makes firmware refill this socket's >> shared >> * metric DRAM region, which is then copied out below. Hold the >> * per-socket lock across the fill-and-copy so concurrent >> readers of the >> * same socket cannot return a torn snapshot. >> @@ -713,8 +721,8 @@ int hsmp_get_tbl_dram_base(u16 sock_ind) >> int ret; >> msg.sock_ind = sock_ind; >> - msg.response_sz = >> hsmp_msg_desc_table[HSMP_GET_METRIC_TABLE_DRAM_ADDR].response_sz; >> - msg.msg_id = HSMP_GET_METRIC_TABLE_DRAM_ADDR; >> + msg.msg_id = hsmp_pdev.desc->metric_dram_msg; >> + msg.response_sz = get_msg_desc(msg.msg_id)->response_sz; >> ret = hsmp_send_message_locked(&msg); >> if (ret) >> diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/ >> x86/amd/hsmp/hsmp.h >> index b4bd9800dee1..c46d53dbcd43 100644 >> --- a/drivers/platform/x86/amd/hsmp/hsmp.h >> +++ b/drivers/platform/x86/amd/hsmp/hsmp.h >> @@ -103,6 +103,8 @@ struct hsmp_mbaddr_info { >> * @test_msg: no-op message used to probe the mailbox. Also the >> * lowest valid message ID, as ID 0 is reserved. >> * @proto_ver_msg: returns the interface version. >> + * @metric_tbl_msg: refills this socket's metric table in DRAM. >> + * @metric_dram_msg: returns the DRAM address of the metric table. >> */ >> struct hsmp_plat_desc { >> const struct hsmp_mbaddr_info *mbinfo; >> @@ -110,6 +112,8 @@ struct hsmp_plat_desc { >> u32 num_msgs; >> u32 test_msg; >> u32 proto_ver_msg; >> + u32 metric_tbl_msg; >> + u32 metric_dram_msg; >> }; >> struct hsmp_socket { >> diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/ >> x86/amd/hsmp/plat.c >> index 7423c98a7b3e..bc29b0ec18f7 100644 >> --- a/drivers/platform/x86/amd/hsmp/plat.c >> +++ b/drivers/platform/x86/amd/hsmp/plat.c >> @@ -185,7 +185,12 @@ static int init_platform_device(struct device *dev) >> dev_warn(dev, "Interface version query unsupported on >> client SMU; continuing\n"); >> } >> - if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { >> + /* >> + * On the client the metric table is fetched with >> + * HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR whatever interface >> + * version was reported, since that query may be unsupported. >> + */ > > What version was reported on client platforms? Since it's the first > time client is being supported with HSMP, maybe it's best that client > should be reporting 6. > > Then this part of the patch could get dropped later. > Currently client version is different than server. So it needs a explicit condition for client programs. But, I will check with the SMU team on this. >> + if (is_client_platform() || hsmp_pdev->proto_ver == >> HSMP_PROTO_VER6) { >> ret = hsmp_get_tbl_dram_base(i); >> if (ret) >> dev_info(dev, "Failed to init metric table\n"); > ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 3/7] platform/x86/amd/hsmp: Add ACPI client support via the SMNR method 2026-07-29 16:40 [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support Muralidhara M K 2026-07-29 16:40 ` [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah Muralidhara M K 2026-07-29 16:40 ` [PATCH 2/7] platform/x86/amd/hsmp: Route metric table through the client messages Muralidhara M K @ 2026-07-29 16:40 ` Muralidhara M K 2026-07-29 21:30 ` Mario Limonciello 2026-07-29 16:40 ` [PATCH 4/7] platform/x86/amd/hsmp: Leave the hwmon power sensors off on client platforms Muralidhara M K ` (3 subsequent siblings) 6 siblings, 1 reply; 22+ messages in thread From: Muralidhara M K @ 2026-07-29 16:40 UTC (permalink / raw) To: ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam, Muralidhara M K On the Family 1Ah client platforms (Models 80h-8Fh and E0h-E3h) the ACPI HSMP device (HID AMDI0097, ACPI path \_SB_.TELD) is present but, unlike server platforms, exposes neither a _CRS memory window nor a _DSD mailbox-offset package, and names its socket differently. The probe therefore failed in three places: - hsmp_get_uid() requires the server "IDXX" _UID form and rejects anything shorter than three characters. The client device carries a bare socket number ("0"), so the probe aborted with -EINVAL before it reached any of the code below, and without logging a reason. - hsmp_read_acpi_crs()/_dsd() found no _CRS/_DSD and bailed out. - hsmp_cache_proto_ver() issued HSMP_CLIENT_GET_INTERFACE_VER (msg 0x3), which this client SMU rejects with "invalid message" (0xFE) even though the mailbox is otherwise functional. Handle the client explicitly: - hsmp_get_uid(): strip the "ID" prefix only when it is present, so both the server "IDXX" form and a bare socket number are accepted. A _UID that is neither still fails, as kstrtou16() rejects it. - hsmp_parse_acpi_table(): for is_client_platform(), skip _CRS/_DSD and take the fixed client SMN mailbox addresses from the platform descriptor, accessed through firmware. Reads go via the read-only SMNR ACPI method (TELD.SMNR -> CpmReadSmnRegister, the same accessor used by the AMDHSMP Windows driver's amdtelemetry_Acpi_SMNR / 'RNMS'); writes go via the kernel SMN helper (amd_smn_hsmp_rdwr), as this device has no ACPI SMN write method. - init_acpi(): treat the interface-version query as non-fatal on the client, and call hsmp_get_tbl_dram_base() whatever version was reported so the metric table is fetched with HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR, mirroring plat.c. As on the server path, the client branch publishes sock->dev last with smp_store_release(). sock->dev is the readiness gate the data plane tests with smp_load_acquire(), so it must not become visible before the accessor and mailbox offsets that this socket's mailbox access depends on. is_client_platform() covers every Family 1Ah client platform (Medusa1, Olympic Ridge and Medusa2), which all share the same client SMN mailbox, so one branch handles them. Server behaviour (pure _DSD/_CRS/MMIO) is unchanged: the _UID handling still strips the "ID" prefix those platforms use. Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> --- drivers/platform/x86/amd/hsmp/acpi.c | 125 +++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index 8257cd1da48e..bbf2b9a8a408 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -10,6 +10,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <asm/amd/hsmp.h> +#include <asm/amd/node.h> #include <linux/acpi.h> #include <linux/array_size.h> @@ -71,6 +72,68 @@ static int amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset, return 0; } +/* + * Family 1Ah client platforms (Models 80h-8Fh and E0h-E3h) expose the HSMP + * ACPI device (HID AMDI0097, ACPI path \_SB_.TELD) but, unlike server + * platforms, the device provides neither a _CRS memory window nor a _DSD + * mailbox-offset package. The mailbox lives in SMN register space and is + * reached through firmware: + * + * - Reads : the read-only ACPI method SMNR(addr) returns the 32-bit + * value of the SMN register at absolute SMN address 'addr' + * (TELD.SMNR -> CpmReadSmnRegister). This is the ACPI BIOS + * telemetry accessor referenced by the AMDHSMP Windows driver + * (amdtelemetry_Acpi_SMNR, method 'RNMS'). + * - Writes : there is no corresponding ACPI write method, so SMN writes + * are issued through the kernel SMN helper (amd_smn_hsmp_rdwr). + * + * The SMN mailbox addresses themselves cannot be discovered from firmware + * on the client (no _DSD), so they are fixed; the SMN_HSMP_*_RM + * addresses are defined in hsmp.h and shared with the platform driver + * (plat.c). + */ + +/* ACPI method on \_SB_.TELD that reads one SMN register: SMNR(addr) */ +#define HSMP_ACPI_SMNR_METHOD "SMNR" + +static int hsmp_acpi_smnr_read(struct hsmp_socket *sock, u32 smn_addr, u32 *value) +{ + struct acpi_object_list arg_list; + union acpi_object arg; + unsigned long long out; + acpi_status status; + + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = smn_addr; + arg_list.count = 1; + arg_list.pointer = &arg; + + status = acpi_evaluate_integer(ACPI_HANDLE(sock->dev), + HSMP_ACPI_SMNR_METHOD, &arg_list, &out); + if (ACPI_FAILURE(status)) { + dev_err_ratelimited(sock->dev, + "SMNR(0x%08x) ACPI method failed: %s\n", + smn_addr, acpi_format_exception(status)); + return -EIO; + } + + *value = (u32)out; + return 0; +} + +static int amd_hsmp_acpi_smn_rdwr(struct hsmp_socket *sock, u32 offset, + u32 *value, bool write) +{ + u32 smn_addr = sock->mbinfo.base_addr + offset; + + /* Read path uses the firmware SMNR ACPI BIOS method. */ + if (!write) + return hsmp_acpi_smnr_read(sock, smn_addr, value); + + /* No ACPI write method exists; drive SMN writes through the kernel. */ + return amd_smn_hsmp_rdwr(sock->sock_ind, smn_addr, value, true); +} + /* This is the UUID used for HSMP */ static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd, 0xa6, 0x9f, 0x4e, 0xa2, @@ -89,15 +152,19 @@ static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind) char *uid; /* - * UID (ID00, ID01..IDXX) is used for differentiating sockets, - * read it and strip the "ID" part of it and convert the remaining - * bytes to integer. + * Server firmware differentiates the sockets with "ID00", "ID01".. + * "IDXX", so strip the "ID" before converting the rest. The client + * device carries a bare socket number with no prefix to strip, so + * only skip one when it is actually there. */ uid = acpi_device_uid(ACPI_COMPANION(dev)); - if (!uid || strlen(uid) < 3) + if (!uid) return -EINVAL; - return kstrtou16(uid + 2, 10, sock_ind); + if (!strncmp(uid, "ID", 2)) + uid += 2; + + return kstrtou16(uid, 10, sock_ind); } static acpi_status hsmp_resource(struct acpi_resource *res, void *data) @@ -240,12 +307,35 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind) int ret; sock->sock_ind = sock_ind; - sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr; sema_init(&sock->hsmp_sem, 1); dev_set_drvdata(dev, sock); + /* + * On the Family 1Ah client platforms the bound ACPI device + * (\_SB_.TELD) has no _CRS/_DSD, so use the fixed client SMN mailbox + * addresses and access them via firmware - reads through the SMNR + * ACPI BIOS method, writes through the kernel SMN helper. + */ + if (is_client_platform()) { + sock->amd_hsmp_rdwr = amd_hsmp_acpi_smn_rdwr; + sock->mbinfo = *hsmp_pdev->desc->mbinfo; + dev_info(dev, + "Client platform: SMN mailbox via SMNR ACPI method (reads) + kernel SMN (writes)\n"); + /* + * Publish sock->dev last, for the same reason as the server + * path below: it is the readiness gate for the data plane, so + * it must not become visible before the accessor and the + * mailbox offsets this socket needs. + */ + smp_store_release(&sock->dev, dev); + + return 0; + } + + sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr; + /* Read MP1 base address from CRS method */ ret = hsmp_read_acpi_crs(dev, sock); if (ret) @@ -553,11 +643,28 @@ static int init_acpi(struct device *dev) ret = hsmp_cache_proto_ver(sock_ind); if (ret) { - dev_err(dev, "Failed to read HSMP protocol version\n"); - return ret; + /* + * Some client SMU builds reject the interface-version query + * with "invalid message" even though the mailbox is functional + * (the preceding test message succeeds). The client does not + * need the version to reach its metric table, so treat this as + * non-fatal there, matching the platform driver. + */ + if (is_client_platform()) { + dev_warn(dev, + "Interface version query unsupported on client SMU; continuing\n"); + } else { + dev_err(dev, "Failed to read HSMP protocol version\n"); + return ret; + } } - if (hsmp_pdev->proto_ver >= HSMP_PROTO_VER6) { + /* + * On client parts the metric table is fetched via + * HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR whatever interface version + * was reported, since that query may be unsupported, mirroring plat.c. + */ + if (is_client_platform() || hsmp_pdev->proto_ver >= HSMP_PROTO_VER6) { ret = hsmp_get_tbl_dram_base(sock_ind); if (ret) dev_info(dev, "Failed to init metric table\n"); -- 2.34.1 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 3/7] platform/x86/amd/hsmp: Add ACPI client support via the SMNR method 2026-07-29 16:40 ` [PATCH 3/7] platform/x86/amd/hsmp: Add ACPI client support via the SMNR method Muralidhara M K @ 2026-07-29 21:30 ` Mario Limonciello 2026-07-30 9:44 ` M K, Muralidhara 0 siblings, 1 reply; 22+ messages in thread From: Mario Limonciello @ 2026-07-29 21:30 UTC (permalink / raw) To: Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/29/26 11:40, Muralidhara M K wrote: > On the Family 1Ah client platforms (Models 80h-8Fh and E0h-E3h) the ACPI > HSMP device (HID AMDI0097, ACPI path \_SB_.TELD) is present but, unlike > server platforms, exposes neither a _CRS memory window nor a _DSD > mailbox-offset package, and names its socket differently. The probe > therefore failed in three places: > > - hsmp_get_uid() requires the server "IDXX" _UID form and rejects > anything shorter than three characters. The client device carries a > bare socket number ("0"), so the probe aborted with -EINVAL before > it reached any of the code below, and without logging a reason. > - hsmp_read_acpi_crs()/_dsd() found no _CRS/_DSD and bailed out. > - hsmp_cache_proto_ver() issued HSMP_CLIENT_GET_INTERFACE_VER > (msg 0x3), which this client SMU rejects with "invalid message" > (0xFE) even though the mailbox is otherwise functional. > > Handle the client explicitly: > > - hsmp_get_uid(): strip the "ID" prefix only when it is present, so > both the server "IDXX" form and a bare socket number are accepted. > A _UID that is neither still fails, as kstrtou16() rejects it. > - hsmp_parse_acpi_table(): for is_client_platform(), skip _CRS/_DSD > and take the fixed client SMN mailbox addresses from the platform > descriptor, accessed through firmware. Reads go via the read-only > SMNR ACPI method > (TELD.SMNR -> CpmReadSmnRegister, the same accessor used by the > AMDHSMP Windows driver's amdtelemetry_Acpi_SMNR / 'RNMS'); writes > go via the kernel SMN helper (amd_smn_hsmp_rdwr), as this device > has no ACPI SMN write method. > - init_acpi(): treat the interface-version query as non-fatal on the > client, and call hsmp_get_tbl_dram_base() whatever version was > reported so the metric table is fetched with > HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR, mirroring plat.c. > > As on the server path, the client branch publishes sock->dev last with > smp_store_release(). sock->dev is the readiness gate the data plane > tests with smp_load_acquire(), so it must not become visible before > the accessor and mailbox offsets that this socket's mailbox access > depends on. > > is_client_platform() covers every Family 1Ah client platform (Medusa1, > Olympic Ridge and Medusa2), which all share the same client SMN > mailbox, so one branch handles them. Server behaviour (pure > _DSD/_CRS/MMIO) is unchanged: the _UID handling still strips the "ID" > prefix those platforms use. > Why even bother with the ACPI one? Couldn't amd_smn_hsmp_rdwr() handle both read and write? > Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> > --- > drivers/platform/x86/amd/hsmp/acpi.c | 125 +++++++++++++++++++++++++-- > 1 file changed, 116 insertions(+), 9 deletions(-) > > diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c > index 8257cd1da48e..bbf2b9a8a408 100644 > --- a/drivers/platform/x86/amd/hsmp/acpi.c > +++ b/drivers/platform/x86/amd/hsmp/acpi.c > @@ -10,6 +10,7 @@ > #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > #include <asm/amd/hsmp.h> > +#include <asm/amd/node.h> > > #include <linux/acpi.h> > #include <linux/array_size.h> > @@ -71,6 +72,68 @@ static int amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset, > return 0; > } > > +/* > + * Family 1Ah client platforms (Models 80h-8Fh and E0h-E3h) expose the HSMP > + * ACPI device (HID AMDI0097, ACPI path \_SB_.TELD) but, unlike server > + * platforms, the device provides neither a _CRS memory window nor a _DSD > + * mailbox-offset package. The mailbox lives in SMN register space and is > + * reached through firmware: > + * > + * - Reads : the read-only ACPI method SMNR(addr) returns the 32-bit > + * value of the SMN register at absolute SMN address 'addr' > + * (TELD.SMNR -> CpmReadSmnRegister). This is the ACPI BIOS > + * telemetry accessor referenced by the AMDHSMP Windows driver > + * (amdtelemetry_Acpi_SMNR, method 'RNMS'). > + * - Writes : there is no corresponding ACPI write method, so SMN writes > + * are issued through the kernel SMN helper (amd_smn_hsmp_rdwr). > + * > + * The SMN mailbox addresses themselves cannot be discovered from firmware > + * on the client (no _DSD), so they are fixed; the SMN_HSMP_*_RM > + * addresses are defined in hsmp.h and shared with the platform driver > + * (plat.c). > + */ > + > +/* ACPI method on \_SB_.TELD that reads one SMN register: SMNR(addr) */ > +#define HSMP_ACPI_SMNR_METHOD "SMNR" > + > +static int hsmp_acpi_smnr_read(struct hsmp_socket *sock, u32 smn_addr, u32 *value) > +{ > + struct acpi_object_list arg_list; > + union acpi_object arg; > + unsigned long long out; > + acpi_status status; > + > + arg.type = ACPI_TYPE_INTEGER; > + arg.integer.value = smn_addr; > + arg_list.count = 1; > + arg_list.pointer = &arg; > + > + status = acpi_evaluate_integer(ACPI_HANDLE(sock->dev), > + HSMP_ACPI_SMNR_METHOD, &arg_list, &out); > + if (ACPI_FAILURE(status)) { > + dev_err_ratelimited(sock->dev, > + "SMNR(0x%08x) ACPI method failed: %s\n", > + smn_addr, acpi_format_exception(status)); > + return -EIO; > + } > + > + *value = (u32)out; > + return 0; > +} > + > +static int amd_hsmp_acpi_smn_rdwr(struct hsmp_socket *sock, u32 offset, > + u32 *value, bool write) > +{ > + u32 smn_addr = sock->mbinfo.base_addr + offset; > + > + /* Read path uses the firmware SMNR ACPI BIOS method. */ > + if (!write) > + return hsmp_acpi_smnr_read(sock, smn_addr, value); > + > + /* No ACPI write method exists; drive SMN writes through the kernel. */ > + return amd_smn_hsmp_rdwr(sock->sock_ind, smn_addr, value, true); > +} > + > /* This is the UUID used for HSMP */ > static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd, > 0xa6, 0x9f, 0x4e, 0xa2, > @@ -89,15 +152,19 @@ static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind) > char *uid; > > /* > - * UID (ID00, ID01..IDXX) is used for differentiating sockets, > - * read it and strip the "ID" part of it and convert the remaining > - * bytes to integer. > + * Server firmware differentiates the sockets with "ID00", "ID01".. > + * "IDXX", so strip the "ID" before converting the rest. The client > + * device carries a bare socket number with no prefix to strip, so > + * only skip one when it is actually there. > */ > uid = acpi_device_uid(ACPI_COMPANION(dev)); > - if (!uid || strlen(uid) < 3) > + if (!uid) > return -EINVAL; > > - return kstrtou16(uid + 2, 10, sock_ind); > + if (!strncmp(uid, "ID", 2)) > + uid += 2; > + > + return kstrtou16(uid, 10, sock_ind); > } > > static acpi_status hsmp_resource(struct acpi_resource *res, void *data) > @@ -240,12 +307,35 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind) > int ret; > > sock->sock_ind = sock_ind; > - sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr; > > sema_init(&sock->hsmp_sem, 1); > > dev_set_drvdata(dev, sock); > > + /* > + * On the Family 1Ah client platforms the bound ACPI device > + * (\_SB_.TELD) has no _CRS/_DSD, so use the fixed client SMN mailbox > + * addresses and access them via firmware - reads through the SMNR > + * ACPI BIOS method, writes through the kernel SMN helper. > + */ > + if (is_client_platform()) { > + sock->amd_hsmp_rdwr = amd_hsmp_acpi_smn_rdwr; > + sock->mbinfo = *hsmp_pdev->desc->mbinfo; > + dev_info(dev, > + "Client platform: SMN mailbox via SMNR ACPI method (reads) + kernel SMN (writes)\n"); > + /* > + * Publish sock->dev last, for the same reason as the server > + * path below: it is the readiness gate for the data plane, so > + * it must not become visible before the accessor and the > + * mailbox offsets this socket needs. > + */ > + smp_store_release(&sock->dev, dev); > + > + return 0; > + } > + > + sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr; > + > /* Read MP1 base address from CRS method */ > ret = hsmp_read_acpi_crs(dev, sock); > if (ret) > @@ -553,11 +643,28 @@ static int init_acpi(struct device *dev) > > ret = hsmp_cache_proto_ver(sock_ind); > if (ret) { > - dev_err(dev, "Failed to read HSMP protocol version\n"); > - return ret; > + /* > + * Some client SMU builds reject the interface-version query > + * with "invalid message" even though the mailbox is functional > + * (the preceding test message succeeds). The client does not > + * need the version to reach its metric table, so treat this as > + * non-fatal there, matching the platform driver. > + */ > + if (is_client_platform()) { > + dev_warn(dev, > + "Interface version query unsupported on client SMU; continuing\n"); > + } else { > + dev_err(dev, "Failed to read HSMP protocol version\n"); > + return ret; > + } > } > > - if (hsmp_pdev->proto_ver >= HSMP_PROTO_VER6) { > + /* > + * On client parts the metric table is fetched via > + * HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR whatever interface version > + * was reported, since that query may be unsupported, mirroring plat.c. > + */ > + if (is_client_platform() || hsmp_pdev->proto_ver >= HSMP_PROTO_VER6) { > ret = hsmp_get_tbl_dram_base(sock_ind); > if (ret) > dev_info(dev, "Failed to init metric table\n"); ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 3/7] platform/x86/amd/hsmp: Add ACPI client support via the SMNR method 2026-07-29 21:30 ` Mario Limonciello @ 2026-07-30 9:44 ` M K, Muralidhara 0 siblings, 0 replies; 22+ messages in thread From: M K, Muralidhara @ 2026-07-30 9:44 UTC (permalink / raw) To: Mario Limonciello, Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/30/2026 3:00 AM, Mario Limonciello wrote: > > > On 7/29/26 11:40, Muralidhara M K wrote: >> On the Family 1Ah client platforms (Models 80h-8Fh and E0h-E3h) the ACPI >> HSMP device (HID AMDI0097, ACPI path \_SB_.TELD) is present but, unlike >> server platforms, exposes neither a _CRS memory window nor a _DSD >> mailbox-offset package, and names its socket differently. The probe >> therefore failed in three places: >> >> - hsmp_get_uid() requires the server "IDXX" _UID form and rejects >> anything shorter than three characters. The client device carries a >> bare socket number ("0"), so the probe aborted with -EINVAL before >> it reached any of the code below, and without logging a reason. >> - hsmp_read_acpi_crs()/_dsd() found no _CRS/_DSD and bailed out. >> - hsmp_cache_proto_ver() issued HSMP_CLIENT_GET_INTERFACE_VER >> (msg 0x3), which this client SMU rejects with "invalid message" >> (0xFE) even though the mailbox is otherwise functional. >> >> Handle the client explicitly: >> >> - hsmp_get_uid(): strip the "ID" prefix only when it is present, so >> both the server "IDXX" form and a bare socket number are accepted. >> A _UID that is neither still fails, as kstrtou16() rejects it. >> - hsmp_parse_acpi_table(): for is_client_platform(), skip _CRS/_DSD >> and take the fixed client SMN mailbox addresses from the platform >> descriptor, accessed through firmware. Reads go via the read-only >> SMNR ACPI method >> (TELD.SMNR -> CpmReadSmnRegister, the same accessor used by the >> AMDHSMP Windows driver's amdtelemetry_Acpi_SMNR / 'RNMS'); writes >> go via the kernel SMN helper (amd_smn_hsmp_rdwr), as this device >> has no ACPI SMN write method. >> - init_acpi(): treat the interface-version query as non-fatal on the >> client, and call hsmp_get_tbl_dram_base() whatever version was >> reported so the metric table is fetched with >> HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR, mirroring plat.c. >> >> As on the server path, the client branch publishes sock->dev last with >> smp_store_release(). sock->dev is the readiness gate the data plane >> tests with smp_load_acquire(), so it must not become visible before >> the accessor and mailbox offsets that this socket's mailbox access >> depends on. >> >> is_client_platform() covers every Family 1Ah client platform (Medusa1, >> Olympic Ridge and Medusa2), which all share the same client SMN >> mailbox, so one branch handles them. Server behaviour (pure >> _DSD/_CRS/MMIO) is unchanged: the _UID handling still strips the "ID" >> prefix those platforms use. >> > > Why even bother with the ACPI one? Couldn't amd_smn_hsmp_rdwr() handle > both read and write? > Mechanically it can - I built that variant and the mailbox does come up on the Model 80h system here. But it drops the one thing firmware still provides on this part, so I would like to keep SMNR for reads. v1 did not explain why it was there, which is a fair reason for the question; v2's commit message now does. The reason is the missing _CRS. On the server the mailbox is an MMIO window: firmware describes it in _CRS, the driver maps it, and every access lands inside a region firmware has vouched for. The client supports no MMIO window at all - there is no _CRS to read and nothing to map - so the mailbox has to be reached in SMN register space at fixed addresses that no firmware object describes. SMNR is what closes that gap. It is the BIOS method that validates the offset it is handed before returning the register, so it is the client counterpart of the _CRS window the server path relies on. Going straight to amd_smn_hsmp_rdwr() for reads would mean the client is the one path that reaches its mailbox with no firmware validation anywhere in it, against hardcoded addresses. Keeping the read side on SMNR also keeps us consistent with the accessor AMD firmware exposes for this device and that the Windows driver uses, so a BIOS change to the telemetry window is picked up on both. The asymmetry you would be right to point at is writes. The device exposes no ACPI write method, so the message ID, argument and status writes in the handshake do go through amd_smn_hsmp_rdwr(). I am not claiming the write side is validated - it cannot be until firmware offers a method for it. What SMNR buys today is that the addresses we read are confirmed by BIOS rather than only by our own table, and if a future BIOS adds a write counterpart the accessor is already routed through firmware. If you would still rather not carry the ACPI round trip and the error path for that, say so and I will switch both directions to amd_smn_hsmp_rdwr(); the code is tested and it is a smaller patch. I did not want to make that call unilaterally, because it changes the client from firmware-mediated access to raw SMN. >> Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> >> --- >> drivers/platform/x86/amd/hsmp/acpi.c | 125 +++++++++++++++++++++++++-- >> 1 file changed, 116 insertions(+), 9 deletions(-) >> >> diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/ >> x86/amd/hsmp/acpi.c >> index 8257cd1da48e..bbf2b9a8a408 100644 >> --- a/drivers/platform/x86/amd/hsmp/acpi.c >> +++ b/drivers/platform/x86/amd/hsmp/acpi.c >> @@ -10,6 +10,7 @@ >> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt >> #include <asm/amd/hsmp.h> >> +#include <asm/amd/node.h> >> #include <linux/acpi.h> >> #include <linux/array_size.h> >> @@ -71,6 +72,68 @@ static int amd_hsmp_acpi_rdwr(struct hsmp_socket >> *sock, u32 offset, >> return 0; >> } >> +/* >> + * Family 1Ah client platforms (Models 80h-8Fh and E0h-E3h) expose >> the HSMP >> + * ACPI device (HID AMDI0097, ACPI path \_SB_.TELD) but, unlike server >> + * platforms, the device provides neither a _CRS memory window nor a >> _DSD >> + * mailbox-offset package. The mailbox lives in SMN register space >> and is >> + * reached through firmware: >> + * >> + * - Reads : the read-only ACPI method SMNR(addr) returns the 32-bit >> + * value of the SMN register at absolute SMN address 'addr' >> + * (TELD.SMNR -> CpmReadSmnRegister). This is the ACPI >> BIOS >> + * telemetry accessor referenced by the AMDHSMP Windows >> driver >> + * (amdtelemetry_Acpi_SMNR, method 'RNMS'). >> + * - Writes : there is no corresponding ACPI write method, so SMN >> writes >> + * are issued through the kernel SMN helper >> (amd_smn_hsmp_rdwr). >> + * >> + * The SMN mailbox addresses themselves cannot be discovered from >> firmware >> + * on the client (no _DSD), so they are fixed; the SMN_HSMP_*_RM >> + * addresses are defined in hsmp.h and shared with the platform driver >> + * (plat.c). >> + */ >> + >> +/* ACPI method on \_SB_.TELD that reads one SMN register: SMNR(addr) */ >> +#define HSMP_ACPI_SMNR_METHOD "SMNR" >> + >> +static int hsmp_acpi_smnr_read(struct hsmp_socket *sock, u32 >> smn_addr, u32 *value) >> +{ >> + struct acpi_object_list arg_list; >> + union acpi_object arg; >> + unsigned long long out; >> + acpi_status status; >> + >> + arg.type = ACPI_TYPE_INTEGER; >> + arg.integer.value = smn_addr; >> + arg_list.count = 1; >> + arg_list.pointer = &arg; >> + >> + status = acpi_evaluate_integer(ACPI_HANDLE(sock->dev), >> + HSMP_ACPI_SMNR_METHOD, &arg_list, &out); >> + if (ACPI_FAILURE(status)) { >> + dev_err_ratelimited(sock->dev, >> + "SMNR(0x%08x) ACPI method failed: %s\n", >> + smn_addr, acpi_format_exception(status)); >> + return -EIO; >> + } >> + >> + *value = (u32)out; >> + return 0; >> +} >> + >> +static int amd_hsmp_acpi_smn_rdwr(struct hsmp_socket *sock, u32 offset, >> + u32 *value, bool write) >> +{ >> + u32 smn_addr = sock->mbinfo.base_addr + offset; >> + >> + /* Read path uses the firmware SMNR ACPI BIOS method. */ >> + if (!write) >> + return hsmp_acpi_smnr_read(sock, smn_addr, value); >> + >> + /* No ACPI write method exists; drive SMN writes through the >> kernel. */ >> + return amd_smn_hsmp_rdwr(sock->sock_ind, smn_addr, value, true); >> +} >> + >> /* This is the UUID used for HSMP */ >> static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, >> 0x48bd, >> 0xa6, 0x9f, 0x4e, 0xa2, >> @@ -89,15 +152,19 @@ static inline int hsmp_get_uid(struct device >> *dev, u16 *sock_ind) >> char *uid; >> /* >> - * UID (ID00, ID01..IDXX) is used for differentiating sockets, >> - * read it and strip the "ID" part of it and convert the remaining >> - * bytes to integer. >> + * Server firmware differentiates the sockets with "ID00", "ID01".. >> + * "IDXX", so strip the "ID" before converting the rest. The client >> + * device carries a bare socket number with no prefix to strip, so >> + * only skip one when it is actually there. >> */ >> uid = acpi_device_uid(ACPI_COMPANION(dev)); >> - if (!uid || strlen(uid) < 3) >> + if (!uid) >> return -EINVAL; >> - return kstrtou16(uid + 2, 10, sock_ind); >> + if (!strncmp(uid, "ID", 2)) >> + uid += 2; >> + >> + return kstrtou16(uid, 10, sock_ind); >> } >> static acpi_status hsmp_resource(struct acpi_resource *res, void *data) >> @@ -240,12 +307,35 @@ static int hsmp_parse_acpi_table(struct device >> *dev, u16 sock_ind) >> int ret; >> sock->sock_ind = sock_ind; >> - sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr; >> sema_init(&sock->hsmp_sem, 1); >> dev_set_drvdata(dev, sock); >> + /* >> + * On the Family 1Ah client platforms the bound ACPI device >> + * (\_SB_.TELD) has no _CRS/_DSD, so use the fixed client SMN >> mailbox >> + * addresses and access them via firmware - reads through the SMNR >> + * ACPI BIOS method, writes through the kernel SMN helper. >> + */ >> + if (is_client_platform()) { >> + sock->amd_hsmp_rdwr = amd_hsmp_acpi_smn_rdwr; >> + sock->mbinfo = *hsmp_pdev->desc->mbinfo; >> + dev_info(dev, >> + "Client platform: SMN mailbox via SMNR ACPI method >> (reads) + kernel SMN (writes)\n"); >> + /* >> + * Publish sock->dev last, for the same reason as the server >> + * path below: it is the readiness gate for the data plane, so >> + * it must not become visible before the accessor and the >> + * mailbox offsets this socket needs. >> + */ >> + smp_store_release(&sock->dev, dev); >> + >> + return 0; >> + } >> + >> + sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr; >> + >> /* Read MP1 base address from CRS method */ >> ret = hsmp_read_acpi_crs(dev, sock); >> if (ret) >> @@ -553,11 +643,28 @@ static int init_acpi(struct device *dev) >> ret = hsmp_cache_proto_ver(sock_ind); >> if (ret) { >> - dev_err(dev, "Failed to read HSMP protocol version\n"); >> - return ret; >> + /* >> + * Some client SMU builds reject the interface-version query >> + * with "invalid message" even though the mailbox is functional >> + * (the preceding test message succeeds). The client does not >> + * need the version to reach its metric table, so treat this as >> + * non-fatal there, matching the platform driver. >> + */ >> + if (is_client_platform()) { >> + dev_warn(dev, >> + "Interface version query unsupported on client SMU; >> continuing\n"); >> + } else { >> + dev_err(dev, "Failed to read HSMP protocol version\n"); >> + return ret; >> + } >> } >> - if (hsmp_pdev->proto_ver >= HSMP_PROTO_VER6) { >> + /* >> + * On client parts the metric table is fetched via >> + * HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR whatever interface >> version >> + * was reported, since that query may be unsupported, mirroring >> plat.c. >> + */ >> + if (is_client_platform() || hsmp_pdev->proto_ver >= >> HSMP_PROTO_VER6) { >> ret = hsmp_get_tbl_dram_base(sock_ind); >> if (ret) >> dev_info(dev, "Failed to init metric table\n"); > ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 4/7] platform/x86/amd/hsmp: Leave the hwmon power sensors off on client platforms 2026-07-29 16:40 [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support Muralidhara M K ` (2 preceding siblings ...) 2026-07-29 16:40 ` [PATCH 3/7] platform/x86/amd/hsmp: Add ACPI client support via the SMNR method Muralidhara M K @ 2026-07-29 16:40 ` Muralidhara M K 2026-07-29 21:35 ` Mario Limonciello 2026-07-29 16:40 ` [PATCH 5/7] platform/x86/amd/hsmp: Add SMN read IOCTL support Muralidhara M K ` (2 subsequent siblings) 6 siblings, 1 reply; 22+ messages in thread From: Muralidhara M K @ 2026-07-29 16:40 UTC (permalink / raw) To: ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam, Muralidhara M K hwmon.c issues the server power message IDs unconditionally, so on the Family 1Ah client platforms the sensors address whatever the Ryzen Master SMC set puts at those numbers: power1_input 04h HSMP_CLIENT_GET_METRICS_TABLE_VER power1_cap 06h HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR Both match the num_args and response_sz that the sensors ask for, so validate_message() accepts them and the two files report a metrics-table version and the low half of a DRAM address as microwatts. The other two accesses are already rejected, as they land on a client message taking a different num_args: power1_cap_max on 07h (HSMP_CLIENT_SET_CORE_PSM_MARGIN) and the power1_cap write path, which sends HSMP_SET_SOCKET_POWER_LIMIT on 05h (HSMP_CLIENT_GET_METRICS_TABLE). No unintended firmware write is possible either way. The client message set has no power telemetry to report instead, so gate the registration on the platform in both drivers rather than teach hwmon.c two message sets. Server behaviour is unchanged. The ACPI sysfs attributes are left alone. They are hardcoded to server message IDs too, but on the client each one is either meaningful or rejected: 02h and 03h are the SMU and interface version queries in both message sets, and the num_args and response_sz checks in validate_message() reject the rest, including every ID that lands on a client SET. Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> --- drivers/platform/x86/amd/hsmp/acpi.c | 13 ++++++++++--- drivers/platform/x86/amd/hsmp/plat.c | 15 +++++++++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index bbf2b9a8a408..57758df9d353 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -670,9 +670,16 @@ static int init_acpi(struct device *dev) dev_info(dev, "Failed to init metric table\n"); } - ret = hsmp_create_sensor(dev, sock_ind); - if (ret) - dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); + /* + * The hwmon power messages are numbered as metric table queries in the + * client message set, so the sensors would report those responses as + * power readings. Leave them off there, as plat.c does. + */ + if (!is_client_platform()) { + ret = hsmp_create_sensor(dev, sock_ind); + if (ret) + dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); + } dev_set_drvdata(dev, &hsmp_pdev->sock[sock_ind]); diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index bc29b0ec18f7..17877e25f43e 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -196,10 +196,17 @@ static int init_platform_device(struct device *dev) dev_info(dev, "Failed to init metric table\n"); } - /* Register with hwmon interface for reporting power */ - ret = hsmp_create_sensor(dev, i); - if (ret) - dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); + /* + * Register with hwmon interface for reporting power. Skipped + * on the client, where the power messages the sensors issue + * are numbered as metric table queries and would report those + * responses as power readings. + */ + if (!is_client_platform()) { + ret = hsmp_create_sensor(dev, i); + if (ret) + dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); + } } return 0; -- 2.34.1 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 4/7] platform/x86/amd/hsmp: Leave the hwmon power sensors off on client platforms 2026-07-29 16:40 ` [PATCH 4/7] platform/x86/amd/hsmp: Leave the hwmon power sensors off on client platforms Muralidhara M K @ 2026-07-29 21:35 ` Mario Limonciello 2026-07-30 10:01 ` M K, Muralidhara 0 siblings, 1 reply; 22+ messages in thread From: Mario Limonciello @ 2026-07-29 21:35 UTC (permalink / raw) To: Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/29/26 11:40, Muralidhara M K wrote: > hwmon.c issues the server power message IDs unconditionally, so on the > Family 1Ah client platforms the sensors address whatever the Ryzen > Master SMC set puts at those numbers: > > power1_input 04h HSMP_CLIENT_GET_METRICS_TABLE_VER > power1_cap 06h HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR > > Both match the num_args and response_sz that the sensors ask for, so > validate_message() accepts them and the two files report a metrics-table > version and the low half of a DRAM address as microwatts. > > The other two accesses are already rejected, as they land on a client > message taking a different num_args: power1_cap_max on 07h > (HSMP_CLIENT_SET_CORE_PSM_MARGIN) and the power1_cap write path, which > sends HSMP_SET_SOCKET_POWER_LIMIT on 05h > (HSMP_CLIENT_GET_METRICS_TABLE). No unintended firmware write is > possible either way. > > The client message set has no power telemetry to report instead Is that right? I thought the metrics table has accumulator values. Which brings me to my next question - where is the format of the metrics table declared? It's probably (but not guaranteed) the same metrics table format used in PMF. IE this: https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/commit/?h=review-ilpo-next&id=641b41a7a12537f8898b1e14c62e0d85a8b872c2 But if userspace does and HSMP message to get the metrics table I think we need a kernel header that shows them how to decode the structure. > , so gate > the registration on the platform in both drivers rather than teach > hwmon.c two message sets. Server behaviour is unchanged. > > The ACPI sysfs attributes are left alone. They are hardcoded to server > message IDs too, but on the client each one is either meaningful or > rejected: 02h and 03h are the SMU and interface version queries in both > message sets, and the num_args and response_sz checks in > validate_message() reject the rest, including every ID that lands on a > client SET. > > Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> > --- > drivers/platform/x86/amd/hsmp/acpi.c | 13 ++++++++++--- > drivers/platform/x86/amd/hsmp/plat.c | 15 +++++++++++---- > 2 files changed, 21 insertions(+), 7 deletions(-) > > diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c > index bbf2b9a8a408..57758df9d353 100644 > --- a/drivers/platform/x86/amd/hsmp/acpi.c > +++ b/drivers/platform/x86/amd/hsmp/acpi.c > @@ -670,9 +670,16 @@ static int init_acpi(struct device *dev) > dev_info(dev, "Failed to init metric table\n"); > } > > - ret = hsmp_create_sensor(dev, sock_ind); > - if (ret) > - dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); > + /* > + * The hwmon power messages are numbered as metric table queries in the > + * client message set, so the sensors would report those responses as > + * power readings. Leave them off there, as plat.c does. > + */ > + if (!is_client_platform()) { > + ret = hsmp_create_sensor(dev, sock_ind); > + if (ret) > + dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); > + } > > dev_set_drvdata(dev, &hsmp_pdev->sock[sock_ind]); > > diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c > index bc29b0ec18f7..17877e25f43e 100644 > --- a/drivers/platform/x86/amd/hsmp/plat.c > +++ b/drivers/platform/x86/amd/hsmp/plat.c > @@ -196,10 +196,17 @@ static int init_platform_device(struct device *dev) > dev_info(dev, "Failed to init metric table\n"); > } > > - /* Register with hwmon interface for reporting power */ > - ret = hsmp_create_sensor(dev, i); > - if (ret) > - dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); > + /* > + * Register with hwmon interface for reporting power. Skipped > + * on the client, where the power messages the sensors issue > + * are numbered as metric table queries and would report those > + * responses as power readings. > + */ > + if (!is_client_platform()) { > + ret = hsmp_create_sensor(dev, i); > + if (ret) > + dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); > + } > } > > return 0; ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 4/7] platform/x86/amd/hsmp: Leave the hwmon power sensors off on client platforms 2026-07-29 21:35 ` Mario Limonciello @ 2026-07-30 10:01 ` M K, Muralidhara 0 siblings, 0 replies; 22+ messages in thread From: M K, Muralidhara @ 2026-07-30 10:01 UTC (permalink / raw) To: Mario Limonciello, Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/30/2026 3:05 AM, Mario Limonciello wrote: > > > On 7/29/26 11:40, Muralidhara M K wrote: >> hwmon.c issues the server power message IDs unconditionally, so on the >> Family 1Ah client platforms the sensors address whatever the Ryzen >> Master SMC set puts at those numbers: >> >> power1_input 04h HSMP_CLIENT_GET_METRICS_TABLE_VER >> power1_cap 06h HSMP_CLIENT_GET_METRICS_TABLE_DRAM_ADDR >> >> Both match the num_args and response_sz that the sensors ask for, so >> validate_message() accepts them and the two files report a metrics-table >> version and the low half of a DRAM address as microwatts. >> >> The other two accesses are already rejected, as they land on a client >> message taking a different num_args: power1_cap_max on 07h >> (HSMP_CLIENT_SET_CORE_PSM_MARGIN) and the power1_cap write path, which >> sends HSMP_SET_SOCKET_POWER_LIMIT on 05h >> (HSMP_CLIENT_GET_METRICS_TABLE). No unintended firmware write is >> possible either way. >> >> The client message set has no power telemetry to report instead > > Is that right? I thought the metrics table has accumulator values. > You are right and that sentence was wrong; it is fixed in v2. The metric table does carry power - system_power_acc, apu_power_acc, npu_power_acc and the per-rail vddcr_*_telemetry_power fields. What I should have written is narrower. The problem is not that the client has no power figure, it is that hwmon's power1_* attributes reach for it by issuing the server power message IDs, and those numbers are metric-table queries in the client set. I registered the sensors on the Model 80h system with the guard removed to show exactly what comes out: power1_input 5000 = table version 5, x1000 power1_cap 1574441292000 = low word of the table DRAM address 0x5DD8114C, x1000 power1_cap_max -EINVAL That is 5 mW and 1.57 MW. Both correlate exactly with what those message IDs return on this part (04h -> 0x5, 06h -> 0x5DD8114C). I also checked the write path, since that is the one that would matter: writing power1_cap sends HSMP_SET_SOCKET_POWER_LIMIT (05h), which is GET_METRICS_TABLE on the client and takes a different argument count, so validate_message() rejects it with -EINVAL before a message is built. dmesg stays empty across the attempt - nothing reaches firmware. Getting a real reading is not a matter of substituting a message ID: the table's power fields are monotonic accumulators, so power1_input would have to read the table, difference two snapshots and normalise by accumulation_counter. That is a different shape of sensor from the one-message-per-read hwmon path here, so I would rather add it deliberately on top than bend this patch into it. This one just keeps the misleading files from appearing. > Which brings me to my next question - where is the format of the metrics > table declared? > > It's probably (but not guaranteed) the same metrics table format used in > PMF. IE this: > > https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers- > x86.git/commit/?h=review-ilpo- > next&id=641b41a7a12537f8898b1e14c62e0d85a8b872c2 > > But if userspace does and HSMP message to get the metrics table I think > we need a kernel header that shows them how to decode the structure. > Agreed on the principle - userspace should not have to guess the layout. It is not the PMF layout. I compared against smu_pmf_metrics_v2: that is a table of instantaneous u16 values in mW, MHz and centi-C delivered over the PMF-SMU mailbox. The HSMP client table is a different structure - u64 accumulators, per-CCX per-core arrays for residency, frequency, temperature and power, per-rail set and telemetry voltages, per-throttler limit and residency groups, and the overclocking state. Same SoC, but different producers and different layouts, so sharing a struct would be wrong. Where I would push back is on declaring it in a kernel header. There is no single layout to declare: the model variants this series enables carry different telemetry tables, and each new model can add another. A header that covered them would need one large struct per table version, named after the version because there is nothing else to name it after, which reads badly and has to grow with every part. The driver decodes none of it - it copies the table out as bytes - so those declarations would exist only for userspace, in a header the kernel itself cannot validate against the firmware that produces the data. The AMD Public PPR documents the telemetry table per model, with the field order, widths and units. That is the reference a tool should decode against, and it stays correct as models are added without the kernel tracking a firmware data structure it never interprets. The driver's part is getting the bytes out, which the metric table ioctl already does. amd_hsmp.rst says this in v2: it describes how to obtain the table, and for the client sends the reader to the telemetry table for the model in the PPR to decode it. >> , so gate >> the registration on the platform in both drivers rather than teach >> hwmon.c two message sets. Server behaviour is unchanged. >> >> The ACPI sysfs attributes are left alone. They are hardcoded to server >> message IDs too, but on the client each one is either meaningful or >> rejected: 02h and 03h are the SMU and interface version queries in both >> message sets, and the num_args and response_sz checks in >> validate_message() reject the rest, including every ID that lands on a >> client SET. >> >> Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> >> --- >> drivers/platform/x86/amd/hsmp/acpi.c | 13 ++++++++++--- >> drivers/platform/x86/amd/hsmp/plat.c | 15 +++++++++++---- >> 2 files changed, 21 insertions(+), 7 deletions(-) >> >> diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/ >> x86/amd/hsmp/acpi.c >> index bbf2b9a8a408..57758df9d353 100644 >> --- a/drivers/platform/x86/amd/hsmp/acpi.c >> +++ b/drivers/platform/x86/amd/hsmp/acpi.c >> @@ -670,9 +670,16 @@ static int init_acpi(struct device *dev) >> dev_info(dev, "Failed to init metric table\n"); >> } >> - ret = hsmp_create_sensor(dev, sock_ind); >> - if (ret) >> - dev_info(dev, "Failed to register HSMP sensors with hwmon\n"); >> + /* >> + * The hwmon power messages are numbered as metric table queries >> in the >> + * client message set, so the sensors would report those >> responses as >> + * power readings. Leave them off there, as plat.c does. >> + */ >> + if (!is_client_platform()) { >> + ret = hsmp_create_sensor(dev, sock_ind); >> + if (ret) >> + dev_info(dev, "Failed to register HSMP sensors with >> hwmon\n"); >> + } >> dev_set_drvdata(dev, &hsmp_pdev->sock[sock_ind]); >> diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/ >> x86/amd/hsmp/plat.c >> index bc29b0ec18f7..17877e25f43e 100644 >> --- a/drivers/platform/x86/amd/hsmp/plat.c >> +++ b/drivers/platform/x86/amd/hsmp/plat.c >> @@ -196,10 +196,17 @@ static int init_platform_device(struct device *dev) >> dev_info(dev, "Failed to init metric table\n"); >> } >> - /* Register with hwmon interface for reporting power */ >> - ret = hsmp_create_sensor(dev, i); >> - if (ret) >> - dev_info(dev, "Failed to register HSMP sensors with >> hwmon\n"); >> + /* >> + * Register with hwmon interface for reporting power. Skipped >> + * on the client, where the power messages the sensors issue >> + * are numbered as metric table queries and would report those >> + * responses as power readings. >> + */ >> + if (!is_client_platform()) { >> + ret = hsmp_create_sensor(dev, i); >> + if (ret) >> + dev_info(dev, "Failed to register HSMP sensors with >> hwmon\n"); >> + } >> } >> return 0; > ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 5/7] platform/x86/amd/hsmp: Add SMN read IOCTL support 2026-07-29 16:40 [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support Muralidhara M K ` (3 preceding siblings ...) 2026-07-29 16:40 ` [PATCH 4/7] platform/x86/amd/hsmp: Leave the hwmon power sensors off on client platforms Muralidhara M K @ 2026-07-29 16:40 ` Muralidhara M K 2026-07-29 16:40 ` [PATCH 6/7] platform/x86/amd/hsmp: Add MSR " Muralidhara M K 2026-07-29 16:40 ` [PATCH 7/7] Documentation/arch/x86/amd_hsmp: Document the SMN and MSR IOCTLs Muralidhara M K 6 siblings, 0 replies; 22+ messages in thread From: Muralidhara M K @ 2026-07-29 16:40 UTC (permalink / raw) To: ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam, Muralidhara M K Bringing up a client platform means reading SMN registers that the HSMP message set does not cover, and today that means either a debugfs interface that is not present on production kernels or an out-of-tree module. Add HSMP_IOCTL_SMN_CMD so a privileged tool can issue a 32-bit SMN read for a given socket through /dev/hsmp, routed through the same amd_smn_hsmp_rdwr() helper the driver already uses for its mailbox. The ioctl is read-only. struct hsmp_smn_message carries a @write flag so write support could be added later without changing the layout, but a request that sets it is rejected with -EPERM regardless of the caller's privilege or open mode: an SMN write can have side effects anywhere in the SoC, and nothing in this driver can bound them. Reads still require CAP_SYS_RAWIO, because even reading can touch registers with read side effects. Every field of the request falls on its natural alignment under the surrounding #pragma pack(4), so the struct is a tight 12 bytes with the same wire layout for 32-bit and 64-bit callers, and the reserved byte is rejected when non-zero so a future kernel can repurpose it without breaking deployed userspace. The handler takes hsmp_sock_rwsem for read across the bounds check and the access. /dev/hsmp is a singleton character device that outlives an individual socket unbind, so an ioctl on an already-open fd can run concurrently with teardown, and remove takes the same lock for write while it tears the socket array down; without the lock the check against num_sockets could be made against a count that is already being torn down. The copy_to_user() is deliberately outside the lock, because faulting in a userfaultfd-backed destination can block indefinitely and would otherwise leave a socket unbind waiting for the write lock. This mirrors HSMP_IOCTL_GET_TELEMETRY_DATA. The user-controlled socket index is clamped with array_index_nospec() before it is used to reach per-node data, mitigating Spectre v1 (CVE-2017-5753). Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> --- arch/x86/include/uapi/asm/amd_hsmp.h | 34 ++++++++++++++ drivers/platform/x86/amd/hsmp/hsmp.c | 69 ++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h index b4c3ddb9d1c1..9c4ad22e47ae 100644 --- a/arch/x86/include/uapi/asm/amd_hsmp.h +++ b/arch/x86/include/uapi/asm/amd_hsmp.h @@ -79,6 +79,31 @@ struct hsmp_message { __u16 sock_ind; /* socket number */ }; +/** + * struct hsmp_smn_message - Request descriptor for the HSMP SMN read IOCTL + * @smn_address: Input. SMN address to read. + * @value: Output. Populated by the kernel with the value read. + * @sock_ind: Input. Socket index the read is issued on. + * @write: Must be zero. This IOCTL is read-only, so a request with + * @write set is rejected with -EPERM whatever the caller's + * privilege or open mode. The field is kept so that write + * support could be added later without changing the layout. + * @reserved: Reserved for future use. Callers must set this to zero; a + * non-zero value is rejected with -EINVAL so future kernels can + * repurpose the field without breaking deployed userspace. + * + * Every field falls on its natural alignment under the surrounding + * #pragma pack(4), so the struct is a tight 12 bytes with the same wire + * layout on 32-bit and 64-bit userspace. + */ +struct hsmp_smn_message { + __u32 smn_address; + __u32 value; + __u16 sock_ind; + __u8 write; + __u8 reserved; +}; + enum hsmp_msg_type { HSMP_RSVD = -1, HSMP_SET = 0, @@ -664,6 +689,15 @@ struct hsmp_telemetry_data { #define HSMP_IOCTL_GET_TELEMETRY_DATA \ _IOW(HSMP_BASE_IOCTL_NR, 1, struct hsmp_telemetry_data) +/* + * Read a 32-bit SMN register on a given socket. This reaches registers + * outside the HSMP message ABI, so it requires CAP_SYS_RAWIO. + * + * The direction is _IOWR because the kernel reads the request struct and + * writes the value it read back into the same struct. + */ +#define HSMP_IOCTL_SMN_CMD _IOWR(HSMP_BASE_IOCTL_NR, 2, struct hsmp_smn_message) + /* * Client HSMP messages supported on the Family 1Ah client platforms: * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 2326506929a4..ec11c5840b87 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -8,8 +8,10 @@ */ #include <asm/amd/hsmp.h> +#include <asm/amd/node.h> #include <linux/acpi.h> +#include <linux/capability.h> #include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> @@ -430,6 +432,71 @@ static bool is_get_msg(struct hsmp_message *msg) return false; } +/* + * Read a 32-bit SMN register on the requested socket. + * + * Raw SMN access reaches registers that are not part of the HSMP message ABI + * and whose side effects the driver cannot reason about, so it is gated on + * CAP_SYS_RAWIO. Writes are not offered at all: the request carries a @write + * flag so support could be added later, and a request that sets it today is + * refused whatever the caller's privilege or open mode. + */ +static long hsmp_ioctl_smn(struct file *fp, unsigned long arg) +{ + void __user *arguser = (void __user *)arg; + struct hsmp_smn_message smn; + unsigned int sock_ind; + int ret; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* The value read travels back in the request struct. */ + if (!(fp->f_mode & FMODE_READ)) + return -EPERM; + + if (copy_from_user(&smn, arguser, sizeof(smn))) + return -EFAULT; + + if (smn.write) + return -EPERM; + + if (smn.reserved) + return -EINVAL; + + /* + * /dev/hsmp is a singleton character device that outlives an individual + * socket unbind, so an ioctl on an already-open fd can run concurrently + * with socket teardown. Hold hsmp_sock_rwsem for read across the bounds + * check and the access, since remove takes the same lock for write + * while it tears the socket array down. + */ + scoped_guard(rwsem_read, &hsmp_sock_rwsem) { + if (!hsmp_pdev.sock || smn.sock_ind >= hsmp_pdev.num_sockets) + return -ENODEV; + + /* + * Sanitize the user-controlled socket index against speculative + * execution. The bounds check above retires the out-of-range + * case with -ENODEV, but a mispredicted branch can still let + * the CPU speculatively use the index to reach per-node data + * and pull arbitrary kernel memory into the cache (Spectre v1, + * CVE-2017-5753). + */ + sock_ind = array_index_nospec(smn.sock_ind, hsmp_pdev.num_sockets); + + ret = amd_smn_hsmp_rdwr(sock_ind, smn.smn_address, &smn.value, false); + } + + if (ret) + return ret; + + if (copy_to_user(arguser, &smn, sizeof(smn))) + return -EFAULT; + + return 0; +} + static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) { int __user *arguser = (int __user *)arg; @@ -620,6 +687,8 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) return hsmp_ioctl_msg(fp, arg); case HSMP_IOCTL_GET_TELEMETRY_DATA: return hsmp_ioctl_get_telemetry(fp, arg); + case HSMP_IOCTL_SMN_CMD: + return hsmp_ioctl_smn(fp, arg); default: return -ENOTTY; } -- 2.34.1 ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 6/7] platform/x86/amd/hsmp: Add MSR read IOCTL support 2026-07-29 16:40 [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support Muralidhara M K ` (4 preceding siblings ...) 2026-07-29 16:40 ` [PATCH 5/7] platform/x86/amd/hsmp: Add SMN read IOCTL support Muralidhara M K @ 2026-07-29 16:40 ` Muralidhara M K 2026-07-29 16:40 ` [PATCH 7/7] Documentation/arch/x86/amd_hsmp: Document the SMN and MSR IOCTLs Muralidhara M K 6 siblings, 0 replies; 22+ messages in thread From: Muralidhara M K @ 2026-07-29 16:40 UTC (permalink / raw) To: ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam, Muralidhara M K Add HSMP_IOCTL_MSR_CMD to read a 64-bit MSR through /dev/hsmp, either on a caller-chosen logical CPU or, with HSMP_MSR_ANY_CPU, on whichever CPU the ioctl runs on. The read goes through rdmsrq_safe() and rdmsrq_safe_on_cpu(), so an unimplemented MSR returns an error rather than taking a #GP. This lets a tool that is already talking to the HSMP mailbox read the few core MSRs it needs to interpret what the mailbox reports, without opening a second interface and without needing the CPU it queries to be the one it happens to be running on. Like the SMN read, the ioctl is read-only and requires CAP_SYS_RAWIO. struct hsmp_msr_message carries a @write flag so write support could be added later without changing the layout, and a request that sets it is rejected with -EPERM regardless of privilege or open mode. @value is placed after the two __u32 fields so it lands on an 8-byte boundary, giving a tight 20-byte struct with the same wire layout on 32-bit and 64-bit userspace under the surrounding #pragma pack(4). The value is staged through a u64 local rather than read straight into the request struct, because that struct is packed to 4 bytes and its __u64 member is therefore not guaranteed to be 8-byte aligned. All three reserved bytes are rejected when non-zero so a future kernel can repurpose them without breaking deployed userspace. A caller-supplied CPU index must refer to an online CPU, and is clamped with array_index_nospec() before the IPI that carries the read reaches per-CPU data, mitigating Spectre v1 (CVE-2017-5753). The CPU may go offline between the check and the read; that races harmlessly, as rdmsrq_safe_on_cpu() then fails. Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> --- arch/x86/include/uapi/asm/amd_hsmp.h | 39 +++++++++++++++ drivers/platform/x86/amd/hsmp/hsmp.c | 75 ++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h index 9c4ad22e47ae..32d269e41620 100644 --- a/arch/x86/include/uapi/asm/amd_hsmp.h +++ b/arch/x86/include/uapi/asm/amd_hsmp.h @@ -104,6 +104,38 @@ struct hsmp_smn_message { __u8 reserved; }; +/* + * Sentinel for hsmp_msr_message.core_id meaning "read on the calling CPU" + * rather than scheduling the read on a specific logical CPU. + */ +#define HSMP_MSR_ANY_CPU ((__u32)-1) + +/** + * struct hsmp_msr_message - Request descriptor for the HSMP MSR read IOCTL + * @msr_address: Input. MSR index to read. + * @core_id: Input. Logical CPU to read the MSR on, or HSMP_MSR_ANY_CPU to + * read it on whichever CPU the ioctl runs on. + * @value: Output. Populated by the kernel with the value read. + * @write: Must be zero. This IOCTL is read-only, so a request with + * @write set is rejected with -EPERM whatever the caller's + * privilege or open mode. The field is kept so that write + * support could be added later without changing the layout. + * @reserved: Reserved for future use. Callers must set every byte to zero; + * a non-zero value is rejected with -EINVAL so future kernels + * can repurpose the field without breaking deployed userspace. + * + * Placing @value after the two __u32 fields lands it on an 8-byte boundary, so + * under the surrounding #pragma pack(4) the struct is a tight 20 bytes with + * the same wire layout on 32-bit and 64-bit userspace. + */ +struct hsmp_msr_message { + __u32 msr_address; + __u32 core_id; + __u64 value; + __u8 write; + __u8 reserved[3]; +}; + enum hsmp_msg_type { HSMP_RSVD = -1, HSMP_SET = 0, @@ -698,6 +730,13 @@ struct hsmp_telemetry_data { */ #define HSMP_IOCTL_SMN_CMD _IOWR(HSMP_BASE_IOCTL_NR, 2, struct hsmp_smn_message) +/* + * Read a 64-bit MSR on a given logical CPU. Like the SMN read above this + * requires CAP_SYS_RAWIO and is encoded _IOWR because the value read is + * written back into the request struct. + */ +#define HSMP_IOCTL_MSR_CMD _IOWR(HSMP_BASE_IOCTL_NR, 3, struct hsmp_msr_message) + /* * Client HSMP messages supported on the Family 1Ah client platforms: * Models 80h-87h (Medusa1), Models 88h-8Fh (Olympic Ridge) and diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index ec11c5840b87..4467a0eb0224 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -9,10 +9,12 @@ #include <asm/amd/hsmp.h> #include <asm/amd/node.h> +#include <asm/msr.h> #include <linux/acpi.h> #include <linux/capability.h> #include <linux/cleanup.h> +#include <linux/cpumask.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/io.h> @@ -21,6 +23,7 @@ #include <linux/rwsem.h> #include <linux/semaphore.h> #include <linux/slab.h> +#include <linux/string.h> #include <linux/sysfs.h> #include <linux/uaccess.h> @@ -497,6 +500,76 @@ static long hsmp_ioctl_smn(struct file *fp, unsigned long arg) return 0; } +/* + * Read a 64-bit MSR, either on a caller-chosen logical CPU or on whichever CPU + * the ioctl happens to run on. + * + * Gated on CAP_SYS_RAWIO and read-only on the same terms as the SMN read + * above. No socket state is involved, so unlike that path this needs no + * serialisation against socket teardown. + */ +static long hsmp_ioctl_msr(struct file *fp, unsigned long arg) +{ + void __user *arguser = (void __user *)arg; + struct hsmp_msr_message msr; + u64 val; + int ret; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* The value read travels back in the request struct. */ + if (!(fp->f_mode & FMODE_READ)) + return -EPERM; + + if (copy_from_user(&msr, arguser, sizeof(msr))) + return -EFAULT; + + if (msr.write) + return -EPERM; + + /* + * Require the padding to be zero so that it stays available for + * future fields. Callers that leave garbage here would otherwise + * have it misread as a request once the bytes gain a meaning. + */ + if (memchr_inv(msr.reserved, 0, sizeof(msr.reserved))) + return -EINVAL; + + if (msr.core_id == HSMP_MSR_ANY_CPU) { + ret = rdmsrq_safe(msr.msr_address, &val); + } else { + unsigned int cpu; + + if (msr.core_id >= nr_cpu_ids || !cpu_online(msr.core_id)) + return -EINVAL; + + /* + * Sanitize the user-controlled CPU index against speculative + * execution, as it reaches per-CPU data through the IPI that + * carries the read (Spectre v1, CVE-2017-5753). The CPU may + * go offline between the check and the read; that races + * harmlessly, as rdmsrq_safe_on_cpu() then fails. + */ + cpu = array_index_nospec(msr.core_id, nr_cpu_ids); + ret = rdmsrq_safe_on_cpu(cpu, msr.msr_address, &val); + } + if (ret) + return ret; + + /* + * Copy through a u64 local rather than reading straight into + * msr.value: the request struct is packed to 4 bytes, so its __u64 + * member is not guaranteed to be 8-byte aligned. + */ + msr.value = val; + + if (copy_to_user(arguser, &msr, sizeof(msr))) + return -EFAULT; + + return 0; +} + static long hsmp_ioctl_msg(struct file *fp, unsigned long arg) { int __user *arguser = (int __user *)arg; @@ -689,6 +762,8 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) return hsmp_ioctl_get_telemetry(fp, arg); case HSMP_IOCTL_SMN_CMD: return hsmp_ioctl_smn(fp, arg); + case HSMP_IOCTL_MSR_CMD: + return hsmp_ioctl_msr(fp, arg); default: return -ENOTTY; } -- 2.34.1 ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 7/7] Documentation/arch/x86/amd_hsmp: Document the SMN and MSR IOCTLs 2026-07-29 16:40 [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support Muralidhara M K ` (5 preceding siblings ...) 2026-07-29 16:40 ` [PATCH 6/7] platform/x86/amd/hsmp: Add MSR " Muralidhara M K @ 2026-07-29 16:40 ` Muralidhara M K 2026-07-29 21:41 ` Mario Limonciello 6 siblings, 1 reply; 22+ messages in thread From: Muralidhara M K @ 2026-07-29 16:40 UTC (permalink / raw) To: ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam, Muralidhara M K Describe HSMP_IOCTL_SMN_CMD and HSMP_IOCTL_MSR_CMD: the payload structs, the CAP_SYS_RAWIO and open-mode requirements, and the rule that both are read-only, so a request with @write set is refused with -EPERM even for a fully privileged caller on an O_RDWR handle. Also note that reading the protocol_version attribute can fail on the Fam1Ah client platforms, because some client SMU builds reject the interface-version query, which is why the telemetry ioctl rather than that attribute is the way to reach the client telemetry layout. Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> --- Documentation/arch/x86/amd_hsmp.rst | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst index 8a9d6fe7d4cf..0f03201a9f68 100644 --- a/Documentation/arch/x86/amd_hsmp.rst +++ b/Documentation/arch/x86/amd_hsmp.rst @@ -229,6 +229,50 @@ The following IOCTLs are defined: ``sizeof(struct hsmp_metric_table)``. Later version metrics table layout is documented in the Public PPR. + On the Fam1Ah client platforms reading ``protocol_version`` can fail, + because some client SMU builds reject the interface-version query. + Those parts have their own telemetry layout and this ioctl is the way + to read it. + +``ioctl(file, HSMP_IOCTL_SMN_CMD, struct hsmp_smn_message *smn)`` + Read a 32-bit SMN register on the socket given by ``sock_ind``. This + reaches registers outside the HSMP message ABI, so it requires + ``CAP_SYS_RAWIO`` as well as a handle opened for reading, the latter + because the value read is written back into the request struct. + + The ioctl is read-only. ``write`` must be zero, and a request that + sets it is rejected with ``-EPERM`` even with ``CAP_SYS_RAWIO`` and an + ``O_RDWR`` handle; the field exists only so that write support could be + added later without changing the layout. ``reserved`` must be zero. + Argument:: + + struct hsmp_smn_message { + __u32 smn_address; /* SMN address to read */ + __u32 value; /* Value read, filled in by the kernel */ + __u16 sock_ind; /* Socket index */ + __u8 write; /* Must be zero */ + __u8 reserved; /* Reserved, must be zero */ + }; + +``ioctl(file, HSMP_IOCTL_MSR_CMD, struct hsmp_msr_message *msr)`` + Read a 64-bit MSR on the logical CPU given by ``core_id``, or on + whichever CPU the ioctl runs on when ``core_id`` is + ``HSMP_MSR_ANY_CPU`` (``(__u32)-1``). Any other ``core_id`` must refer + to an online CPU, otherwise the call fails with ``-EINVAL``. Requires + ``CAP_SYS_RAWIO`` and a handle opened for reading. + + Read-only on the same terms as ``HSMP_IOCTL_SMN_CMD``: a non-zero + ``write`` is rejected with ``-EPERM``, and every byte of ``reserved`` + must be zero. Argument:: + + struct hsmp_msr_message { + __u32 msr_address; /* MSR index to read */ + __u32 core_id; /* Logical CPU, or HSMP_MSR_ANY_CPU */ + __u64 value; /* Value read, filled in by the kernel */ + __u8 write; /* Must be zero */ + __u8 reserved[3]; /* Reserved, must be zero */ + }; + The ioctl would return a non-zero on failure; you can read errno to see what happened. The transaction returns 0 on success. -- 2.34.1 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 7/7] Documentation/arch/x86/amd_hsmp: Document the SMN and MSR IOCTLs 2026-07-29 16:40 ` [PATCH 7/7] Documentation/arch/x86/amd_hsmp: Document the SMN and MSR IOCTLs Muralidhara M K @ 2026-07-29 21:41 ` Mario Limonciello 2026-07-30 10:05 ` M K, Muralidhara 0 siblings, 1 reply; 22+ messages in thread From: Mario Limonciello @ 2026-07-29 21:41 UTC (permalink / raw) To: Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/29/26 11:40, Muralidhara M K wrote: > Describe HSMP_IOCTL_SMN_CMD and HSMP_IOCTL_MSR_CMD: the payload structs, > the CAP_SYS_RAWIO and open-mode requirements, and the rule that both are > read-only, so a request with @write set is refused with -EPERM even for a > fully privileged caller on an O_RDWR handle. > > Also note that reading the protocol_version attribute can fail on the > Fam1Ah client platforms, because some client SMU builds reject the > interface-version query, which is why the telemetry ioctl rather than > that attribute is the way to reach the client telemetry layout. I'm happy that these are (at least) defaulting to read-only, but I don't understand their purpose. We already have /dev/cpu/CPUNUM/msr, and we also have amd-node which provides access to SMN via /sys/kernel/debug/x86/. Can you better describe the needs of why these new messages must exist and we can't have structured data for them? Even if amd_hsmp needs to use SMN or MSR to fetch specific data data, this feels a lot safer to have a structured specific fetch method than such a wide (albeit permissioned) pipe. > > Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> > --- > Documentation/arch/x86/amd_hsmp.rst | 44 +++++++++++++++++++++++++++++ > 1 file changed, 44 insertions(+) > > diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst > index 8a9d6fe7d4cf..0f03201a9f68 100644 > --- a/Documentation/arch/x86/amd_hsmp.rst > +++ b/Documentation/arch/x86/amd_hsmp.rst > @@ -229,6 +229,50 @@ The following IOCTLs are defined: > ``sizeof(struct hsmp_metric_table)``. Later version metrics table > layout is documented in the Public PPR. > > + On the Fam1Ah client platforms reading ``protocol_version`` can fail, > + because some client SMU builds reject the interface-version query. > + Those parts have their own telemetry layout and this ioctl is the way > + to read it. It seems to me you don't actually describe the telemetry layout or what should be done with SMN. > + > +``ioctl(file, HSMP_IOCTL_SMN_CMD, struct hsmp_smn_message *smn)`` > + Read a 32-bit SMN register on the socket given by ``sock_ind``. This > + reaches registers outside the HSMP message ABI, so it requires > + ``CAP_SYS_RAWIO`` as well as a handle opened for reading, the latter > + because the value read is written back into the request struct. > + > + The ioctl is read-only. ``write`` must be zero, and a request that > + sets it is rejected with ``-EPERM`` even with ``CAP_SYS_RAWIO`` and an > + ``O_RDWR`` handle; the field exists only so that write support could be > + added later without changing the layout. ``reserved`` must be zero. > + Argument:: > + > + struct hsmp_smn_message { > + __u32 smn_address; /* SMN address to read */ > + __u32 value; /* Value read, filled in by the kernel */ > + __u16 sock_ind; /* Socket index */ > + __u8 write; /* Must be zero */ > + __u8 reserved; /* Reserved, must be zero */ > + }; > + > +``ioctl(file, HSMP_IOCTL_MSR_CMD, struct hsmp_msr_message *msr)`` > + Read a 64-bit MSR on the logical CPU given by ``core_id``, or on > + whichever CPU the ioctl runs on when ``core_id`` is > + ``HSMP_MSR_ANY_CPU`` (``(__u32)-1``). Any other ``core_id`` must refer > + to an online CPU, otherwise the call fails with ``-EINVAL``. Requires > + ``CAP_SYS_RAWIO`` and a handle opened for reading. > + > + Read-only on the same terms as ``HSMP_IOCTL_SMN_CMD``: a non-zero > + ``write`` is rejected with ``-EPERM``, and every byte of ``reserved`` > + must be zero. Argument:: > + > + struct hsmp_msr_message { > + __u32 msr_address; /* MSR index to read */ > + __u32 core_id; /* Logical CPU, or HSMP_MSR_ANY_CPU */ > + __u64 value; /* Value read, filled in by the kernel */ > + __u8 write; /* Must be zero */ > + __u8 reserved[3]; /* Reserved, must be zero */ > + }; > + > The ioctl would return a non-zero on failure; you can read errno to see > what happened. The transaction returns 0 on success. > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 7/7] Documentation/arch/x86/amd_hsmp: Document the SMN and MSR IOCTLs 2026-07-29 21:41 ` Mario Limonciello @ 2026-07-30 10:05 ` M K, Muralidhara 0 siblings, 0 replies; 22+ messages in thread From: M K, Muralidhara @ 2026-07-30 10:05 UTC (permalink / raw) To: Mario Limonciello, Muralidhara M K, ilpo.jarvinen Cc: platform-driver-x86, linux-kernel, muthusamy.ramalingam On 7/30/2026 3:11 AM, Mario Limonciello wrote: > > > On 7/29/26 11:40, Muralidhara M K wrote: >> Describe HSMP_IOCTL_SMN_CMD and HSMP_IOCTL_MSR_CMD: the payload structs, >> the CAP_SYS_RAWIO and open-mode requirements, and the rule that both are >> read-only, so a request with @write set is refused with -EPERM even for a >> fully privileged caller on an O_RDWR handle. >> >> Also note that reading the protocol_version attribute can fail on the >> Fam1Ah client platforms, because some client SMU builds reject the >> interface-version query, which is why the telemetry ioctl rather than >> that attribute is the way to reach the client telemetry layout. > > I'm happy that these are (at least) defaulting to read-only, but I don't > understand their purpose. > > We already have /dev/cpu/CPUNUM/msr, and we also have amd-node which > provides access to SMN via /sys/kernel/debug/x86/. > > Can you better describe the needs of why these new messages must exist > and we can't have structured data for them? > Thanks for the input. Yes, I will describe more on why we need them. > Even if amd_hsmp needs to use SMN or MSR to fetch specific data data, > this feels a lot safer to have a structured specific fetch method than > such a wide (albeit permissioned) pipe. > >> >> Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com> >> --- >> Documentation/arch/x86/amd_hsmp.rst | 44 +++++++++++++++++++++++++++++ >> 1 file changed, 44 insertions(+) >> >> diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/ >> x86/amd_hsmp.rst >> index 8a9d6fe7d4cf..0f03201a9f68 100644 >> --- a/Documentation/arch/x86/amd_hsmp.rst >> +++ b/Documentation/arch/x86/amd_hsmp.rst >> @@ -229,6 +229,50 @@ The following IOCTLs are defined: >> ``sizeof(struct hsmp_metric_table)``. Later version metrics table >> layout is documented in the Public PPR. >> + On the Fam1Ah client platforms reading ``protocol_version`` can fail, >> + because some client SMU builds reject the interface-version query. >> + Those parts have their own telemetry layout and this ioctl is the way >> + to read it. > > It seems to me you don't actually describe the telemetry layout or what > should be done with SMN. > >> + >> +``ioctl(file, HSMP_IOCTL_SMN_CMD, struct hsmp_smn_message *smn)`` >> + Read a 32-bit SMN register on the socket given by ``sock_ind``. This >> + reaches registers outside the HSMP message ABI, so it requires >> + ``CAP_SYS_RAWIO`` as well as a handle opened for reading, the latter >> + because the value read is written back into the request struct. >> + >> + The ioctl is read-only. ``write`` must be zero, and a request that >> + sets it is rejected with ``-EPERM`` even with ``CAP_SYS_RAWIO`` and an >> + ``O_RDWR`` handle; the field exists only so that write support >> could be >> + added later without changing the layout. ``reserved`` must be zero. >> + Argument:: >> + >> + struct hsmp_smn_message { >> + __u32 smn_address; /* SMN address to read */ >> + __u32 value; /* Value read, filled in by the kernel */ >> + __u16 sock_ind; /* Socket index */ >> + __u8 write; /* Must be zero */ >> + __u8 reserved; /* Reserved, must be zero */ >> + }; >> + >> +``ioctl(file, HSMP_IOCTL_MSR_CMD, struct hsmp_msr_message *msr)`` >> + Read a 64-bit MSR on the logical CPU given by ``core_id``, or on >> + whichever CPU the ioctl runs on when ``core_id`` is >> + ``HSMP_MSR_ANY_CPU`` (``(__u32)-1``). Any other ``core_id`` must >> refer >> + to an online CPU, otherwise the call fails with ``-EINVAL``. Requires >> + ``CAP_SYS_RAWIO`` and a handle opened for reading. >> + >> + Read-only on the same terms as ``HSMP_IOCTL_SMN_CMD``: a non-zero >> + ``write`` is rejected with ``-EPERM``, and every byte of ``reserved`` >> + must be zero. Argument:: >> + >> + struct hsmp_msr_message { >> + __u32 msr_address; /* MSR index to read */ >> + __u32 core_id; /* Logical CPU, or HSMP_MSR_ANY_CPU */ >> + __u64 value; /* Value read, filled in by the kernel */ >> + __u8 write; /* Must be zero */ >> + __u8 reserved[3]; /* Reserved, must be zero */ >> + }; >> + >> The ioctl would return a non-zero on failure; you can read errno to see >> what happened. The transaction returns 0 on success. > ^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2026-07-31 10:29 UTC | newest] Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-07-29 16:40 [PATCH 0/7] platform/x86/amd/hsmp: Family 1Ah client support Muralidhara M K 2026-07-29 16:40 ` [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah Muralidhara M K 2026-07-29 21:27 ` Mario Limonciello 2026-07-30 9:37 ` M K, Muralidhara 2026-07-30 16:43 ` Armin Wolf 2026-07-31 4:27 ` M K, Muralidhara 2026-07-31 7:00 ` Shyam Sundar S K 2026-07-31 10:29 ` Armin Wolf 2026-07-29 16:40 ` [PATCH 2/7] platform/x86/amd/hsmp: Route metric table through the client messages Muralidhara M K 2026-07-29 21:29 ` Mario Limonciello 2026-07-30 9:42 ` M K, Muralidhara 2026-07-29 16:40 ` [PATCH 3/7] platform/x86/amd/hsmp: Add ACPI client support via the SMNR method Muralidhara M K 2026-07-29 21:30 ` Mario Limonciello 2026-07-30 9:44 ` M K, Muralidhara 2026-07-29 16:40 ` [PATCH 4/7] platform/x86/amd/hsmp: Leave the hwmon power sensors off on client platforms Muralidhara M K 2026-07-29 21:35 ` Mario Limonciello 2026-07-30 10:01 ` M K, Muralidhara 2026-07-29 16:40 ` [PATCH 5/7] platform/x86/amd/hsmp: Add SMN read IOCTL support Muralidhara M K 2026-07-29 16:40 ` [PATCH 6/7] platform/x86/amd/hsmp: Add MSR " Muralidhara M K 2026-07-29 16:40 ` [PATCH 7/7] Documentation/arch/x86/amd_hsmp: Document the SMN and MSR IOCTLs Muralidhara M K 2026-07-29 21:41 ` Mario Limonciello 2026-07-30 10:05 ` M K, Muralidhara
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®