Re: [PATCH 1/7] platform/x86/amd/hsmp: Add HSMP client support for Family 1Ah

Armin Wolf <[email protected]> Thu, 30 Jul 2026 18:43:14 +0200
Newsgroups org.kernel.vger.platform-driver-x86,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <=3D 0x0F and the client models ar=
e
> all >=3D 0x80. The server mailbox addresses move from plat.c to hsmp.h s=
o
> 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-versio=
n
> 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 SM=
C
message ids and the AMD_ACPI WMI device:

[WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"), Description("Clas=
s 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 in=
it.")] void Init([out, Description("status")] uint8 status);
   [WmiMethodId(3), Implemented, read, write, Description("Get BIOS ACPI s=
upport function table.")] void GetObjectID([out, Description("Get BIOS sup=
port Object ID table")] OIDPackage pack);
   [WmiMethodId(4), Implemented, read, write, Description("Bios request da=
ta")] 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, Descr=
iption("Out buffer")] OutBuffer Outbuf);
   [WmiMethodId(6), Implemented, read, write, Description("Get Min/Max/Ste=
pping values")] void GetMinMaxSteppingValues([in, Description("ID used to =
identify")] uint32 ID, [out, Description("status")] GetMinMaxSteppingPacka=
ge pack);
   [WmiMethodId(7), Implemented, read, write, Description("Get DIMM SPD da=
ta")] void GetDimmSpdData([in, Description("DIMM Index")] uint32 Index, [o=
ut, Description("SPD Data Buffer")] DimmSpdBuffer DimmSpdData);
   [WmiMethodId(8), Implemented, read, write, Description("Get BIOS ACPI s=
upport function table2.")] void GetObjectID2([out, Description("Get BIOS s=
upport 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, D=
escription("Opp Profile")] OPPProfole pack);
};

I own multiple devices with such an interface, and i would _love_ to add s=
upport
for them.

Thanks,
Armin Wolf

> Signed-off-by: Muralidhara M K <[email protected]>
> ---
>   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/x8=
6/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, 0xa=
0-0xaf),
>   Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD suppor=
t
>   system management functionality via HSMP (Host System Management Port)=
.
>  =20
> +The Fam1Ah client platforms are supported as well:
> +
> +  =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D  =3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
> +  Model          Platform
> +  =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D  =3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
> +  0x80-0x87      Medusa1
> +  0x88-0x8f      Olympic Ridge
> +  0xe0-0xe3      Medusa2
> +  =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D  =3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
> +
> +They share one mailbox and speak the Ryzen Master SMC message set inste=
ad
> +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
>  =20
>  =20
> -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) a=
nd
> +the Fam1Ah client platforms listed above.
>  =20
>  =20
>   HSMP device
> diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uap=
i/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)
>  =20
> +/*
> + * 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 =3D 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 addr=
ess */
> +	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 limi=
t */
> +	HSMP_CLIENT_SET_VRM_VDD_MAX_CURRENT_LIMIT,/* 0Bh Set VRM VDD max curre=
nt 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-fl=
y */
> +	HSMP_CLIENT_GET_CORE_PERF_ORDER,	/* 14h Get core performance order */
> +	HSMP_CLIENT_SET_SUSTAINED_POWER_LIMIT,	/* 15h Set sustained power limi=
t */
> +	HSMP_CLIENT_SET_SLOW_PPT_LIMIT,		/* 16h Set slow PPT limit */
> +	HSMP_CLIENT_SET_VRM_GFX_MAX_CURRENT_LIMIT,/* 17h Set VRM GFX max curre=
nt limit */
> +	HSMP_CLIENT_SET_VRM_SOC_CURRENT_LIMIT,	/* 18h Set VRM SOC current limi=
t */
> +	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 overdri=
ve 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 of=
fset */
> +	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)) =3D {
> +	/* 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)
>  =20
> -static struct hsmp_plat_device hsmp_pdev;
> +static const struct hsmp_mbaddr_info hsmp_mbinfo_server =3D {
> +	.base_addr	=3D SMN_HSMP_BASE,
> +	.msg_id_off	=3D SMN_HSMP_MSG_ID,
> +	.msg_resp_off	=3D SMN_HSMP_MSG_RESP,
> +	.msg_arg_off	=3D SMN_HSMP_MSG_DATA,
> +};
> +
> +/* RM: Ryzen Master, the SMC interface these client addresses belong to=
 */
> +static const struct hsmp_mbaddr_info hsmp_mbinfo_client =3D {
> +	.base_addr	=3D SMN_HSMP_BASE_RM,
> +	.msg_id_off	=3D SMN_HSMP_MSG_ID_RM,
> +	.msg_resp_off	=3D SMN_HSMP_MSG_RESP_RM,
> +	.msg_arg_off	=3D 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.  Message=
s named
> + * by userspace in an ioctl need no entry here, as that path already re=
solves
> + * the ID against the running platform's table.
> + */
> +static const struct hsmp_plat_desc hsmp_desc_server =3D {
> +	.mbinfo			=3D &hsmp_mbinfo_server,
> +	.msg_desc		=3D hsmp_msg_desc_table,
> +	.num_msgs		=3D HSMP_MSG_ID_MAX,
> +	.test_msg		=3D HSMP_TEST,
> +	.proto_ver_msg		=3D HSMP_GET_PROTO_VER,
> +};
> +
> +/* The client drives a different mailbox with the Ryzen Master SMC mess=
age set */
> +static const struct hsmp_plat_desc hsmp_desc_client =3D {
> +	.mbinfo			=3D &hsmp_mbinfo_client,
> +	.msg_desc		=3D hsmp_client_msg_desc_table,
> +	.num_msgs		=3D HSMP_CLIENT_MSG_ID_MAX,
> +	.test_msg		=3D HSMP_CLIENT_TEST,
> +	.proto_ver_msg		=3D HSMP_CLIENT_GET_INTERFACE_VER,
> +};
> +
> +static struct hsmp_plat_device hsmp_pdev =3D {
> +	/*
> +	 * 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 =3D &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 =3D hsmp_pdev.desc;
> +
> +	if (msg_id >=3D desc->num_msgs)
> +		return NULL;
> +
> +	return &desc->msg_desc[msg_id];
> +}
>  =20
>   /*
>    * 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
>  =20
>   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 >=3D HSMP_MSG_ID_MAX)
> +	if (msg->msg_id < hsmp_pdev.desc->test_msg ||
> +	    msg->msg_id >=3D hsmp_pdev.desc->num_msgs)
> +		return -ENOMSG;
> +
> +	desc =3D get_msg_desc(msg->msg_id);
> +	if (!desc)
>   		return -ENOMSG;
>  =20
>   	/* msg_id is a reserved message ID */
> -	if (hsmp_msg_desc_table[msg->msg_id].type =3D=3D HSMP_RSVD)
> +	if (desc->type =3D=3D HSMP_RSVD)
>   		return -ENOMSG;
>  =20
>   	/*
>   	 * num_args passed by user should match the num_args specified in
>   	 * message description table.
>   	 */
> -	if (msg->num_args !=3D hsmp_msg_desc_table[msg->msg_id].num_args)
> +	if (msg->num_args !=3D desc->num_args)
>   		return -EINVAL;
>  =20
>   	/*
>   	 * 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;
>  =20
>   	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	=3D HSMP_TEST;
> +	msg.msg_id	=3D hsmp_pdev.desc->test_msg;
>   	msg.num_args	=3D 1;
>   	msg.response_sz	=3D 1;
>   	msg.args[0]	=3D value;
> @@ -340,10 +407,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP");
>  =20
>   static bool is_get_msg(struct hsmp_message *msg)
>   {
> -	if (hsmp_msg_desc_table[msg->msg_id].type =3D=3D HSMP_GET)
> +	const struct hsmp_msg_desc *desc =3D get_msg_desc(msg->msg_id);
> +
> +	if (!desc)
> +		return false;
> +
> +	if (desc->type =3D=3D HSMP_GET)
>   		return true;
>  =20
> -	if (hsmp_msg_desc_table[msg->msg_id].type =3D=3D HSMP_SET_GET &&
> +	if (desc->type =3D=3D HSMP_SET_GET &&
>   	    (msg->args[0] & CHECK_GET_BIT))
>   		return true;
>  =20
> @@ -354,6 +426,7 @@ static long hsmp_ioctl_msg(struct file *fp, unsigned=
 long arg)
>   {
>   	int __user *arguser =3D (int  __user *)arg;
>   	struct hsmp_message msg =3D { 0 };
> +	const struct hsmp_msg_desc *desc;
>   	int ret;
>  =20
>   	if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct h=
smp_message)))
> @@ -361,23 +434,28 @@ static long hsmp_ioctl_msg(struct file *fp, unsign=
ed long arg)
>  =20
>   	/*
>   	 * 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 >=3D HSMP_MSG_ID_MAX)
> +	if (msg.msg_id < hsmp_pdev.desc->test_msg ||
> +	    msg.msg_id >=3D hsmp_pdev.desc->num_msgs)
>   		return -ENOMSG;
>  =20
>   	/*
>   	 * 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 =3D array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX);
> +	msg.msg_id =3D array_index_nospec(msg.msg_id, hsmp_pdev.desc->num_msgs=
);
> +
> +	desc =3D get_msg_desc(msg.msg_id);
> +	if (!desc || desc->type =3D=3D HSMP_RSVD)
> +		return -ENOMSG;
>  =20
>   	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;
>  =20
> -	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 =3D { 0 };
>   	int ret;
>  =20
> -	msg.msg_id	=3D HSMP_GET_PROTO_VER;
> +	msg.msg_id	=3D hsmp_pdev.desc->proto_ver_msg;
>   	msg.sock_ind	=3D sock_ind;
> -	msg.response_sz =3D hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_s=
z;
> +	msg.response_sz	=3D get_msg_desc(msg.msg_id)->response_sz;
>  =20
>   	ret =3D hsmp_send_message_locked(&msg);
>   	if (!ret)
> @@ -735,6 +813,15 @@ EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, "AMD_HSM=
P");
>  =20
>   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 =3D &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
>  =20
> +#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 @@
>  =20
>   #define DRIVER_VERSION		"2.6"
>  =20
> +/*
> + * To access a specific HSMP mailbox register, s/w writes the SMN addre=
ss 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 regis=
ters in
> + * SMU address space, as consumed by the fixed mailbox descriptors in h=
smp.c.
> + *
> + * The server set is used by the non-ACPI path only; the ACPI path disc=
overs the
> + * equivalent values from _CRS/_DSD.  The _RM set is the client one, na=
med for
> + * the Ryzen Master SMC (RMSMC) interface whose mailbox it addresses, a=
nd is
> + * used by both paths because the client exposes no _CRS/_DSD to discov=
er them
> + * from.  The two sets share a base and differ only in the register off=
sets
> + * 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) mailbo=
x 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 sam=
e
> + * 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 =3D=3D 0x1A &&
> +	       ((boot_cpu_data.x86_model >=3D 0x80 && boot_cpu_data.x86_model =
<=3D 0x8F) ||
> +		(boot_cpu_data.x86_model >=3D 0xE0 && boot_cpu_data.x86_model <=3D 0x=
E3));
> +}
> +
>   struct hsmp_mbaddr_info {
>   	u32 base_addr;
>   	u32 msg_id_off;
> @@ -40,6 +88,30 @@ struct hsmp_mbaddr_info {
>   	u32 size;
>   };
>  =20
> +/*
> + * Per-platform description of the mailbox and the message set behind i=
t.
> + * Resolved once from the running CPU and reached through hsmp_pdev->de=
sc, so
> + * neither the message plumbing nor the probe paths have to re-derive w=
hich
> + * 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 @@
>  =20
>   #define DRIVER_NAME		"amd_hsmp"
>  =20
> -/*
> - * 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;
>  =20
>   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 =3D &hsmp_pdev->sock[i];
>   		sock->sock_ind			=3D i;
>   		sock->dev			=3D dev;
> -		sock->mbinfo.base_addr		=3D SMN_HSMP_BASE;
>   		sock->amd_hsmp_rdwr		=3D amd_hsmp_pci_rdwr;
> +		sock->mbinfo			=3D *hsmp_pdev->desc->mbinfo;
>  =20
>   		/*
>   		 * This is a transitional change from non-ACPI to ACPI, only
>   		 * family 0x1A, model 0x00 platform is supported for both ACPI and n=
on-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	=3D SMN_HSMP_MSG_ID_F1A_M0H;
> -		else
> -			sock->mbinfo.msg_id_off	=3D SMN_HSMP_MSG_ID;
>  =20
> -		sock->mbinfo.msg_resp_off	=3D SMN_HSMP_MSG_RESP;
> -		sock->mbinfo.msg_arg_off	=3D SMN_HSMP_MSG_DATA;
>   		sema_init(&sock->hsmp_sem, 1);
>  =20
>   		/* Test the hsmp interface on each socket */
> @@ -184,8 +171,18 @@ static int init_platform_device(struct device *dev)
>  =20
>   		ret =3D 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; co=
ntinuing\n");
>   		}
>  =20
>   		if (hsmp_pdev->proto_ver =3D=3D 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;