[PATCH v3 3/6] x86/amd_node: Restrict SMN root enumeration to AMD vendor
From: Lin Wang
Date: Thu Jul 09 2026 - 04:00:58 EST
Drop X86_VENDOR_HYGON / PCI_VENDOR_ID_HYGON support from the AMD SMN
init path:
- get_next_root(): only match PCI_VENDOR_ID_AMD host bridges.
- amd_smn_init(): add boot_cpu_data.x86_vendor != X86_VENDOR_AMD to
the early-return condition, alongside the existing X86_FEATURE_ZEN
check (X86_FEATURE_ZEN is set on Hygon Dhyana too, so the feature
check alone is not sufficient to gate AMD-only init).
This is functionally required by the Hygon DF node and SMN access
implementation added in subsequent patches. Before this cleanup, on
a Hygon Family 0x18 platform:
amd_smn_init (fs_initcall)
-> get_next_root() matched Hygon host bridges
-> pci_request_config_region_exclusive() reserved Hygon root
PCI config space
-> smn_exclusive = true
That left Hygon's own SMN setup unable to reserve the same config
space, failing with -EEXIST. Restricting both sites to AMD vendor
leaves Hygon root devices untouched by the AMD path so the
Hygon-specific SMN implementation in arch/x86/kernel/hygon_node.c
can reserve them itself.
Together, these changes keep the AMD SMN path AMD-only: root
discovery matches AMD host bridges only, and amd_smn_init() returns
early on non-AMD CPUs.
Gating amd_smn_init() on vendor also changes a precondition the SMN
access path relied on. On Hygon, amd_smn_init() now returns early, so
amd_roots stays NULL and smn_exclusive stays false, while
amd_num_nodes() (derived from CPU topology) remains non-zero. Harden
__amd_smn_rw() to test smn_exclusive before indexing amd_roots[], so a
caller that has not yet been converted to the Hygon SMN path receives
-ENODEV instead of dereferencing a NULL amd_roots[]. Additionally,
amd_smn_read() now returns the underlying error directly instead of
reading back a possibly-uninitialised *value, and zeroes *value on any
failure. Neither change affects AMD SMN access after successful
initialization.
Signed-off-by: Lin Wang <wanglin@xxxxxxxxxxxxxx>
---
arch/x86/kernel/amd_node.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kernel/amd_node.c b/arch/x86/kernel/amd_node.c
index 0be01725a2a4..62086b8b5804 100644
--- a/arch/x86/kernel/amd_node.c
+++ b/arch/x86/kernel/amd_node.c
@@ -88,16 +88,17 @@ static int __amd_smn_rw(u8 i_off, u8 d_off, u16 node, u32 address, u32 *value, b
struct pci_dev *root;
int err = -ENODEV;
- if (node >= amd_num_nodes())
+ /*
+ * smn_exclusive is set only after amd_roots is allocated and the
+ * root config space reserved; test it before indexing amd_roots[].
+ */
+ if (!smn_exclusive || node >= amd_num_nodes())
return err;
root = amd_roots[node];
if (!root)
return err;
- if (!smn_exclusive)
- return err;
-
guard(mutex)(&smn_mutex);
err = pci_write_config_dword(root, i_off, address);
@@ -116,9 +117,15 @@ int __must_check amd_smn_read(u16 node, u32 address, u32 *value)
{
int err = __amd_smn_rw(SMN_INDEX_OFFSET, SMN_DATA_OFFSET, node, address, value, false);
+ /* On error *value may be untouched; do not read it back. */
+ if (err) {
+ *value = 0;
+ return err;
+ }
+
if (PCI_POSSIBLE_ERROR(*value)) {
- err = -ENODEV;
*value = 0;
+ return -ENODEV;
}
return err;
@@ -225,8 +232,7 @@ static struct pci_dev *get_next_root(struct pci_dev *root)
if (root->devfn)
continue;
- if (root->vendor != PCI_VENDOR_ID_AMD &&
- root->vendor != PCI_VENDOR_ID_HYGON)
+ if (root->vendor != PCI_VENDOR_ID_AMD)
continue;
break;
@@ -249,7 +255,8 @@ static int __init amd_smn_init(void)
u16 count, num_roots, roots_per_node, node, num_nodes;
struct pci_dev *root;
- if (!cpu_feature_enabled(X86_FEATURE_ZEN))
+ if (!cpu_feature_enabled(X86_FEATURE_ZEN) ||
+ boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
return 0;
guard(mutex)(&smn_mutex);
--
2.43.0