mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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

* [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

* [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

* [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

* [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 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 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 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 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 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 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 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

* 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

* 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

* 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

* 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

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®