* [PATCH v4 0/5] x86/hygon: Add Family 0x18 DF node enumeration and SMN access
@ 2026-09-01 7:16 Lin Wang
2026-09-01 7:16 ` [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs Lin Wang
` (4 more replies)
0 siblings, 5 replies; 21+ messages in thread
From: Lin Wang @ 2026-09-01 7:16 UTC (permalink / raw)
To: Borislav Petkov, yazen.ghannam, mario.limonciello
Cc: tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86,
linux-pci, linux-kernel
This revision addresses the Sashiko comments on v3.
Sashiko noted that v3 disabled AMD NB and SMN initialization on Hygon
while amd64_edac, ATL and k10temp could still call those interfaces. V4
restores the pre-v3 AMD NB behavior and leaves those drivers unchanged.
The broad SMN exclusion is replaced by a model-specific handoff to the
Hygon SMN setup.
However, restoring AMD NB initialization does not make the AMD paths
usable for the Family 0x18 topology covered here. AMD NB maps nodes 0
through 7 to fixed PCI slots. Hygon discovers F3 devices by PCI ID, may
expose more than eight DF nodes, reports sparse CPU NodeIds, and groups
SMN roots by socket.
This series adds the Hygon node and SMN interfaces first so the Hygon
paths in those drivers can be converted separately.
The AMD-side changes check smn_exclusive before accessing amd_roots.
On these models, amd_smn_init() does not reserve the roots needed by
Hygon SMN setup. AMD SMN calls return -ENODEV until their callers use
the Hygon interfaces. The remaining Hygon handling in amd_nb.c and
amd_node.c can then be removed separately.
Sashiko also found that v3 retained pci_get_class() iterator devices
without owning references. The Hygon setup now holds a reference for
every reserved root and releases all references and config regions on
failure.
The corresponding reference and roots_per_node issues in
amd_smn_init() predate this series. Yazen confirmed them and is handling
the AMD fixes separately.
The node and SMN implementation has been tested on a Family 0x18 Model
0x07 system with 2 sockets, 12 CDDs and 4 IODs.
Changes in v4:
- Drop the v3 AMD NB vendor restriction. (Sashiko)
- Replace the vendor-wide AMD SMN exclusion with a model-scoped
handoff. (Sashiko)
- Hold references for reserved Hygon SMN roots and release all
resources on setup failure. (Sashiko)
- Drop the SMN debugfs interface.
Links:
Sashiko v3 review:
https://sashiko.dev/#/patchset/20260709075549.626095-1-wanglin%40open-hieco.net
RFC v1:
https://lore.kernel.org/all/20260402111515.1155505-1-wanglin@open-hieco.net/
RFC v2:
https://lore.kernel.org/all/20260423060420.1785357-1-wanglin@open-hieco.net/
v3:
https://lore.kernel.org/all/20260709075549.626095-1-wanglin@open-hieco.net/
Lin Wang (5):
pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs
x86/hygon: Add Family 0x18 DF node enumeration
x86/hygon: Map CPU NodeIds to DF nodes
x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08
x86/hygon: Add Family 0x18 SMN access
MAINTAINERS | 3 +
arch/x86/Kconfig | 4 +
arch/x86/include/asm/hygon/node.h | 207 ++++++
arch/x86/kernel/Makefile | 1 +
arch/x86/kernel/amd_node.c | 18 +-
arch/x86/kernel/hygon_node.c | 1011 +++++++++++++++++++++++++++++
include/linux/pci_ids.h | 6 +
7 files changed, 1247 insertions(+), 3 deletions(-)
create mode 100644 arch/x86/include/asm/hygon/node.h
create mode 100644 arch/x86/kernel/hygon_node.c
base-commit: abdf623ddb75b24659018d3952d8f61937306ae5
--
2.43.0
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs 2026-09-01 7:16 [PATCH v4 0/5] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang @ 2026-09-01 7:16 ` Lin Wang 2026-09-01 7:34 ` sashiko-bot 2026-09-03 22:10 ` Bjorn Helgaas 2026-09-01 7:16 ` [PATCH v4 2/5] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang ` (3 subsequent siblings) 4 siblings, 2 replies; 21+ messages in thread From: Lin Wang @ 2026-09-01 7:16 UTC (permalink / raw) To: Borislav Petkov, yazen.ghannam, mario.limonciello Cc: tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel Hygon Family 0x18 node discovery identifies Data Fabric misc (F3) and link (F4) functions by PCI ID rather than a fixed PCI slot. Models 0x04 use 0x1463/0x1464, with 0x1493/0x1494 on the mixed-silicon variant. Models 0x05 through 0x08 use 0x14b3/0x14b4. Some IDs have the same numeric values as AMD DF devices. PCI identity also includes the vendor, so Hygon-prefixed names keep the two vendors' definitions independent. Signed-off-by: Lin Wang <wanglin@open-hieco.net> --- include/linux/pci_ids.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 63774ae2bf71..d9f3e01033f9 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2615,6 +2615,12 @@ #define PCI_VENDOR_ID_ROCKCHIP 0x1d87 #define PCI_VENDOR_ID_HYGON 0x1d94 +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3 0x1463 +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4 0x1464 +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3B 0x1493 +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4B 0x1494 +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3 0x14b3 +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4 0x14b4 #define PCI_VENDOR_ID_META 0x1d9b -- 2.43.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs 2026-09-01 7:16 ` [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs Lin Wang @ 2026-09-01 7:34 ` sashiko-bot 2026-09-03 22:10 ` Bjorn Helgaas 1 sibling, 0 replies; 21+ messages in thread From: sashiko-bot @ 2026-09-01 7:34 UTC (permalink / raw) To: Lin Wang; +Cc: linux-pci > Hygon Family 0x18 node discovery identifies Data Fabric misc (F3) and > link (F4) functions by PCI ID rather than a fixed PCI slot. > > Models 0x04 use 0x1463/0x1464, with 0x1493/0x1494 on the mixed-silicon > variant. Models 0x05 through 0x08 use 0x14b3/0x14b4. > > Some IDs have the same numeric values as AMD DF devices. PCI identity > also includes the vendor, so Hygon-prefixed names keep the two vendors' > definitions independent. > > Signed-off-by: Lin Wang <wanglin@open-hieco.net> Sashiko has reviewed this patch and found no issues. It looks great! -- Sashiko AI review · https://sashiko.dev/#/patchset/20260901071700.3255382-1-wanglin@open-hieco.net?part=1 ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs 2026-09-01 7:16 ` [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs Lin Wang 2026-09-01 7:34 ` sashiko-bot @ 2026-09-03 22:10 ` Bjorn Helgaas 2026-09-04 3:18 ` Lin Wang 1 sibling, 1 reply; 21+ messages in thread From: Bjorn Helgaas @ 2026-09-03 22:10 UTC (permalink / raw) To: Lin Wang Cc: Borislav Petkov, yazen.ghannam, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On Tue, Sep 01, 2026 at 03:16:56PM +0800, Lin Wang wrote: > Hygon Family 0x18 node discovery identifies Data Fabric misc (F3) and > link (F4) functions by PCI ID rather than a fixed PCI slot. > > Models 0x04 use 0x1463/0x1464, with 0x1493/0x1494 on the mixed-silicon > variant. Models 0x05 through 0x08 use 0x14b3/0x14b4. > > Some IDs have the same numeric values as AMD DF devices. PCI identity > also includes the vendor, so Hygon-prefixed names keep the two vendors' > definitions independent. > > Signed-off-by: Lin Wang <wanglin@open-hieco.net> > --- > include/linux/pci_ids.h | 6 ++++++ > 1 file changed, 6 insertions(+) > > diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h > index 63774ae2bf71..d9f3e01033f9 100644 > --- a/include/linux/pci_ids.h > +++ b/include/linux/pci_ids.h > @@ -2615,6 +2615,12 @@ > #define PCI_VENDOR_ID_ROCKCHIP 0x1d87 > > #define PCI_VENDOR_ID_HYGON 0x1d94 > +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3 0x1463 > +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4 0x1464 > +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3B 0x1493 > +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4B 0x1494 > +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3 0x14b3 > +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4 0x14b4 It looks like these are only needed in one place, so per the note: * Do not add new entries to this file unless the definitions * are shared between multiple drivers. the #defines should probably be added in the arch/x86/kernel/hygon_node.c (or just use the raw hex values). This helps reduce conflicts when backporting changes like this. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs 2026-09-03 22:10 ` Bjorn Helgaas @ 2026-09-04 3:18 ` Lin Wang 0 siblings, 0 replies; 21+ messages in thread From: Lin Wang @ 2026-09-04 3:18 UTC (permalink / raw) To: Bjorn Helgaas Cc: Borislav Petkov, yazen.ghannam, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On 9/4/2026 6:10 AM, Bjorn Helgaas wrote: >> +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3 0x1463 >> +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4 0x1464 >> +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3B 0x1493 >> +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4B 0x1494 >> +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3 0x14b3 >> +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4 0x14b4 > It looks like these are only needed in one place, so per the note: > > * Do not add new entries to this file unless the definitions > * are shared between multiple drivers. > > the #defines should probably be added in the > arch/x86/kernel/hygon_node.c (or just use the raw hex values). > > This helps reduce conflicts when backporting changes like this. Hi Bjorn, Thanks for pointing this out. These IDs are only used by the Hygon node code in this series. I will move the definitions to arch/x86/kernel/hygon_node.c. They can be added to pci_ids.h later when needed by in-tree users. Thanks & Br; Lin ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v4 2/5] x86/hygon: Add Family 0x18 DF node enumeration 2026-09-01 7:16 [PATCH v4 0/5] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang 2026-09-01 7:16 ` [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs Lin Wang @ 2026-09-01 7:16 ` Lin Wang 2026-09-01 7:29 ` sashiko-bot 2026-09-01 7:16 ` [PATCH v4 3/5] x86/hygon: Map CPU NodeIds to DF nodes Lin Wang ` (2 subsequent siblings) 4 siblings, 1 reply; 21+ messages in thread From: Lin Wang @ 2026-09-01 7:16 UTC (permalink / raw) To: Borislav Petkov, yazen.ghannam, mario.limonciello Cc: tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel The AMD node code locates function 3 for node N at PCI slot 0x18 + N. Hygon has no equivalent node-index-to-slot mapping: DF F3 devices are discovered by PCI ID and the PCI BDF is only an access point. F1x200 provides the socket ID, SocketPresent mask and, on Models 0x04 and 0x05, the DFID. Models 0x06 through 0x08 provide the DFID in F5x180. The resulting (socket ID, DFID) pair identifies a DF node, while SocketPresent verifies that enumeration covers every populated socket. Order CDDs by socket and DFID, followed by IODs, and assign dense node indices. Expose node counts, identity, F3/F4 lookup and PCI-device lookup through <asm/hygon/node.h>. Signed-off-by: Lin Wang <wanglin@open-hieco.net> --- MAINTAINERS | 3 + arch/x86/Kconfig | 4 + arch/x86/include/asm/hygon/node.h | 152 +++++++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/hygon_node.c | 685 ++++++++++++++++++++++++++++++ 5 files changed, 845 insertions(+) create mode 100644 arch/x86/include/asm/hygon/node.h create mode 100644 arch/x86/kernel/hygon_node.c diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..44cab306636e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12138,9 +12138,12 @@ F: drivers/input/touchscreen/hycon-hy46xx.c HYGON PROCESSOR SUPPORT M: Pu Wen <puwen@hygon.cn> +M: Lin Wang <wanglin@open-hieco.net> L: linux-kernel@vger.kernel.org S: Maintained +F: arch/x86/include/asm/hygon/ F: arch/x86/kernel/cpu/hygon.c +F: arch/x86/kernel/hygon_node.c HYNIX HI556 SENSOR DRIVER M: Sakari Ailus <sakari.ailus@linux.intel.com> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 15fd9ec5ecac..6d72e62d87f7 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -3078,6 +3078,10 @@ config AMD_NODE def_bool y depends on CPU_SUP_AMD && PCI +config HYGON_NODE + def_bool y + depends on CPU_SUP_HYGON && PCI + endmenu menu "Binary Emulations" diff --git a/arch/x86/include/asm/hygon/node.h b/arch/x86/include/asm/hygon/node.h new file mode 100644 index 000000000000..6e15c133b774 --- /dev/null +++ b/arch/x86/include/asm/hygon/node.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Hygon Family 0x18 Data Fabric node enumeration API + * + * This header declares Hygon Fam18h DF node enumeration and function + * access interfaces. + */ +#ifndef _ASM_X86_HYGON_NODE_H +#define _ASM_X86_HYGON_NODE_H + +#include <linux/errno.h> +#include <linux/processor.h> +#include <linux/types.h> + +struct pci_dev; + +/* DF function indices supported by hygon_node_get_func(). */ +#define HYGON_DF_F3 3 /* misc */ +#define HYGON_DF_F4 4 /* link */ + +/* + * Hygon Core and DDR Dies (CDDs) start at DFID 4. Each CDD contains + * CPU cores and UMCs. I/O Dies (IODs) occupy DFIDs 0-3. + */ +#define HYGON_CDD_DFID_BASE 4 + +/** + * enum hygon_node_type - DF node type, derived from DFID + * @HYGON_NODE_IOD: I/O die (DFID < HYGON_CDD_DFID_BASE); no UMC. + * @HYGON_NODE_CDD: Core and DDR Die (DFID >= HYGON_CDD_DFID_BASE); + * contains CPU cores and UMC controllers. + */ +enum hygon_node_type { + HYGON_NODE_IOD = 0, + HYGON_NODE_CDD = 1, +}; + +/** + * struct hygon_node_info - identity snapshot for a DF node + * @socket_id: physical socket ID, F1x200[30:28] + * @dfid: Data Fabric ID, model-dependent source + * @type: HYGON_NODE_CDD or HYGON_NODE_IOD + */ +struct hygon_node_info { + u8 socket_id; + u8 dfid; + enum hygon_node_type type; +}; + +#ifdef CONFIG_HYGON_NODE + +/** + * hygon_node_num() - total number of DF nodes (CDD + IOD) + * + * This is the upper bound for every node index in this API. + * + * Return: total node count, or 0 if the cache is not ready or this is + * not a Hygon Fam18h platform. + */ +u16 hygon_node_num(void); + +/** + * hygon_cdd_num() - number of Core and DDR Dies (CDD) + * + * A CDD (DFID >= 4) has CPU cores and UMC controllers; an IOD + * (DFID < 4) has neither. CDDs occupy nodes [0, hygon_cdd_num()). + * + * Return: CDD count, or 0 if the cache is not ready. + */ +u16 hygon_cdd_num(void); + +/** + * hygon_node_get_info() - read identity snapshot for a DF node + * @node: DF node index in [0, hygon_node_num()) + * @info: output structure (socket_id, dfid, type) + * + * The identity fields are fixed after enumeration and are returned + * together in one lookup. + * + * Return: 0 on success, -EINVAL if @node is out of range or @info is + * NULL, -ENODEV if the cache is not ready. + */ +int hygon_node_get_info(u16 node, struct hygon_node_info *info); + +/** + * hygon_node_get_func() - get DF function PCI device for a node + * @node: DF node index in [0, hygon_node_num()) + * @func: HYGON_DF_F3 or HYGON_DF_F4 + * + * Return: referenced pci_dev on success. NULL if @node is out of + * range, @func is unsupported, or the cache is not ready. The + * caller must release the reference with pci_dev_put(). + */ +struct pci_dev *hygon_node_get_func(u16 node, u8 func); + +/** + * hygon_pci_dev_to_df_node() - find DF node owning the given PCI device + * @pdev: PCI device on the same domain, bus and slot as one of the DF + * nodes (typically a sibling function of the DF misc device, + * e.g. a UMC channel) + * + * Looks up the DF node whose misc (F3) device shares the same PCI + * domain, bus and slot as @pdev, for drivers that hold a sibling + * function and need the containing node index. + * + * Return: DF node index in [0, hygon_node_num()) on success, -EINVAL + * if @pdev is NULL, or -ENODEV if no matching node is found or the + * cache is not ready. + */ +int hygon_pci_dev_to_df_node(struct pci_dev *pdev); + +#else /* !CONFIG_HYGON_NODE */ + +static inline u16 hygon_node_num(void) +{ + return 0; +} + +static inline u16 hygon_cdd_num(void) +{ + return 0; +} + +static inline int hygon_node_get_info(u16 node, struct hygon_node_info *info) +{ + return -ENODEV; +} + +static inline struct pci_dev *hygon_node_get_func(u16 node, u8 func) +{ + return NULL; +} + +static inline int hygon_pci_dev_to_df_node(struct pci_dev *pdev) +{ + return -ENODEV; +} + +#endif /* CONFIG_HYGON_NODE */ + +/* Inline helpers, available regardless of CONFIG_HYGON_NODE. */ + +/** + * is_hygon_f18h() - true on Hygon Family 0x18 CPUs + */ +static inline bool is_hygon_f18h(void) +{ + return boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && + boot_cpu_data.x86 == 0x18; +} + +#endif /* _ASM_X86_HYGON_NODE_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 31f46fd00527..b8430e5e53fb 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -136,6 +136,7 @@ obj-$(CONFIG_HPET_TIMER) += hpet.o obj-$(CONFIG_AMD_NB) += amd_nb.o obj-$(CONFIG_AMD_NODE) += amd_node.o +obj-$(CONFIG_HYGON_NODE) += hygon_node.o obj-$(CONFIG_DEBUG_NMI_SELFTEST) += nmi_selftest.o obj-$(CONFIG_KVM_GUEST) += kvm.o kvmclock.o diff --git a/arch/x86/kernel/hygon_node.c b/arch/x86/kernel/hygon_node.c new file mode 100644 index 000000000000..85835938315b --- /dev/null +++ b/arch/x86/kernel/hygon_node.c @@ -0,0 +1,685 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Hygon Family 0x18 Data Fabric node enumeration + * + * A DF instance exposes sibling PCI functions in one slot. Function 3 + * is the misc device used for enumeration, function 4 is the link + * device, and functions 1 and 5 provide the socket and DF identity. + * + * Enumerate F3 devices by PCI ID, validate their F4 siblings, read each + * node's socket ID and DFID, and validate the enumerated socket set + * against SocketPresent. Sort the nodes into a dense cache with Core + * and DDR Dies (CDDs) before I/O Dies (IODs), then by socket ID and + * DFID. + */ + +#define pr_fmt(fmt) "hygon_node: " fmt + +#include <linux/bitops.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/processor.h> +#include <linux/slab.h> +#include <linux/sort.h> + +#include <asm/cpu_device_id.h> +#include <asm/hygon/node.h> + +/* Maximum socket count this implementation supports. */ +#define HYGON_MAX_SOCKETS 8 + +/* + * DF register offsets used for node identity discovery. + * + * F1x200 (SystemCfg) -- present on all models: + * [30:28] MySocketId - hardware socket ID + * [23:20] MyDieId - die ID (equals DFID on some models, see below) + * + * F5x180 (FabricBlockInstanceInformation3_CS) -- Model 06h-08h only: + * [19:16] DFID - Data Fabric ID for UMC/SMN addressing + * + * DFID source by model: + * Model 04h/05h: F1x200[23:20] (MyDieId == DFID) + * Model 06h-08h: F5x180[19:16] (MyDieId != DFID, different numbering) + */ +#define DF_F1_SYSTEM_CFG 0x200 +#define DF_F5_FABRIC_ID 0x180 + +/* DF function numbers for sibling device access (internal use). */ +#define HYGON_DF_F1 1 /* SystemCfg: socket and die identity */ +#define HYGON_DF_F5 5 /* FabricId: DFID on Model 06h-08h */ + +/* DF sibling device IDs used only within this file for identity reads. */ +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1 0x1491 +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1 0x14b1 +#define PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5 0x14b5 + +/* + * Cached identity for one DF instance. After sorting, CDDs occupy + * nodes[0..num_cdd-1]. + * + * The PCI BDF is the access point for a DF node, not its identity. + * socket_id and dfid, read from DF registers, together identify the + * node in hardware. + */ +struct hygon_node { + struct pci_dev *misc; /* DF function 3 */ + struct pci_dev *link; /* DF function 4 */ + u8 socket_id; /* F1x200[30:28] */ + u8 dfid; /* model-dependent DFID */ + bool is_cdd; /* DFID >= 4 */ +}; + +struct hygon_node_cache { + struct hygon_node *nodes; /* sorted: CDD first, then IOD */ + u16 num_nodes; /* CDD + IOD = total */ + u16 num_cdd; /* CDD only */ + u16 num_sockets; + + /* Set after DF node collection, sorting and validation complete. */ + bool ready; +}; + +struct hygon_df_id { + u8 socket_id; + u8 dfid; +}; + +/* DF sibling device IDs used to read node identity. */ +struct hygon_df_cfg { + u16 f1_id; + u16 f5_id; /* 0 = not available */ +}; + +/* DF misc (F3) device IDs for all supported Hygon Family 0x18 models. */ +static const struct pci_device_id hygon_nb_misc_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3) }, + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3B) }, + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3) }, + {} +}; + +/* DF link (F4) device IDs, parallel to hygon_nb_misc_ids[]. */ +static const struct pci_device_id hygon_nb_link_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4) }, + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4B) }, + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4) }, + {} +}; + +static const struct hygon_df_cfg hygon_m04_df_cfg __initconst = { + .f1_id = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1, +}; + +static const struct hygon_df_cfg hygon_m05_df_cfg __initconst = { + .f1_id = PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1, +}; + +static const struct hygon_df_cfg hygon_m06_m08_df_cfg __initconst = { + .f1_id = PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1, + .f5_id = PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5, +}; + +static const struct x86_cpu_id hygon_df_cpuids[] __initconst = { + X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x04, &hygon_m04_df_cfg), + X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x05, &hygon_m05_df_cfg), + X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x06, &hygon_m06_m08_df_cfg), + X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x07, &hygon_m06_m08_df_cfg), + X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x08, &hygon_m06_m08_df_cfg), + {} +}; + +static struct hygon_node_cache hygon_cache; + +static void __init hygon_dump_nodes(const struct hygon_node_cache *cache, + const char *phase) +{ + u16 i; + + pr_debug("%s: %u nodes\n", phase, cache->num_nodes); + + for (i = 0; i < cache->num_nodes; i++) { + const struct hygon_node *node = &cache->nodes[i]; + + pr_debug("%s: node[%u] %04x:%02x:%02x.%u socket=%u dfid=%u type=%s\n", + phase, i, pci_domain_nr(node->misc->bus), + node->misc->bus->number, + PCI_SLOT(node->misc->devfn), + PCI_FUNC(node->misc->devfn), + node->socket_id, node->dfid, + node->is_cdd ? "CDD" : "IOD"); + } +} + +/* + * Iterate Hygon PCI devices, returning the next one that matches @ids. + * Follows the pci_get_device() convention: @from is consumed (its + * reference is dropped) and the returned device has an elevated + * reference count. + */ +static struct pci_dev * __init next_hygon_dev(struct pci_dev *from, + const struct pci_device_id *ids) +{ + while ((from = pci_get_device(PCI_VENDOR_ID_HYGON, PCI_ANY_ID, from))) { + if (pci_match_id(ids, from)) + return from; + } + + return NULL; +} + +/* + * Find the DF link (function 4) sibling of a DF misc (function 3) + * device. Both functions share the same PCI bus and slot. + */ +static struct pci_dev * __init hygon_get_link(struct pci_dev *misc) +{ + struct pci_dev *link; + + link = pci_get_domain_bus_and_slot(pci_domain_nr(misc->bus), + misc->bus->number, + PCI_DEVFN(PCI_SLOT(misc->devfn), + HYGON_DF_F4)); + if (!link) + return NULL; + + if (!pci_match_id(hygon_nb_link_ids, link)) { + pci_dev_put(link); + return NULL; + } + + return link; +} + +/* Find the DF configuration for the boot CPU. */ +static const struct hygon_df_cfg * __init hygon_get_df_cfg(void) +{ + const struct x86_cpu_id *id = x86_match_cpu(hygon_df_cpuids); + + return id ? (const void *)id->driver_data : NULL; +} + +/* + * Read a config register from a DF sibling function on the same PCI + * slot as @misc. Only functions 1 (F1, SystemCfg) and 5 (F5, + * FabricId) are supported. + */ +static int __init hygon_read_df_reg(struct pci_dev *misc, u8 func, + int offset, u32 *value) +{ + const struct hygon_df_cfg *cfg; + struct pci_dev *sibling; + u16 expected_device; + int err; + + cfg = hygon_get_df_cfg(); + if (!cfg) + return -ENODEV; + + if (func == HYGON_DF_F1) { + expected_device = cfg->f1_id; + + /* + * Model 5 can expose an older mixed-silicon variant where + * the F1 sibling still uses the M04H device ID. + */ + if (boot_cpu_data.x86_model == 0x5 && + misc->device != PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3) + expected_device = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1; + } else if (func == HYGON_DF_F5) { + expected_device = cfg->f5_id; + } else { + return -EINVAL; + } + + if (!expected_device) + return -ENODEV; + + sibling = pci_get_domain_bus_and_slot(pci_domain_nr(misc->bus), + misc->bus->number, + PCI_DEVFN(PCI_SLOT(misc->devfn), + func)); + if (!sibling) + return -ENODEV; + + if (sibling->vendor != PCI_VENDOR_ID_HYGON || + sibling->device != expected_device) { + pci_dev_put(sibling); + return -ENODEV; + } + + err = pci_read_config_dword(sibling, offset, value); + pci_dev_put(sibling); + + if (err) { + pr_warn("error reading %04x:%02x:%02x.%u offset 0x%x\n", + pci_domain_nr(misc->bus), misc->bus->number, + PCI_SLOT(misc->devfn), func, offset); + return pcibios_err_to_errno(err); + } + + /* Do not parse a PCI error response as DF identity. */ + if (PCI_POSSIBLE_ERROR(*value)) { + pr_warn("error response reading %04x:%02x:%02x.%u offset 0x%x\n", + pci_domain_nr(misc->bus), misc->bus->number, + PCI_SLOT(misc->devfn), func, offset); + return -ENODEV; + } + + return 0; +} + +/* + * Read the hardware identity for one DF misc device from its sibling + * functions. + * + * All models expose F1x200 (SystemCfg): socket_id from [30:28] and a + * die identifier (MyDieId) from [23:20]. + * On Model 06h-08h MyDieId differs from the DFID used by UMC and SMN + * addressing, so an additional F5x180 (FabricId) read obtains the DFID + * from [19:16]. + * + * All DF instances on a Hygon system are the same model, so + * boot_cpu_data.x86_model is representative for all devices. + */ +static int __init hygon_read_df_id(struct pci_dev *misc, + struct hygon_df_id *id) +{ + const struct hygon_df_cfg *cfg = hygon_get_df_cfg(); + u32 reg; + int ret; + + if (!cfg) + return -ENODEV; + + ret = hygon_read_df_reg(misc, HYGON_DF_F1, DF_F1_SYSTEM_CFG, ®); + if (ret) + return ret; + + id->socket_id = (reg >> 28) & 0x7; + id->dfid = (reg >> 20) & 0xf; + + /* Read DFID from F5x180 on models that provide an F5 sibling. */ + if (cfg->f5_id) { + ret = hygon_read_df_reg(misc, HYGON_DF_F5, DF_F5_FABRIC_ID, + ®); + if (ret) + return ret; + id->dfid = (reg >> 16) & 0xf; + } + + return 0; +} + +/* + * Read the system-wide SocketPresent mask from F1x200[7:0]. + * + * SocketPresent is identical for every DF instance. Read it once from + * the first DF misc device. A zero mask is invalid on supported models. + */ +static int __init hygon_read_socket_present(struct pci_dev *misc, + u8 *socket_present) +{ + u32 reg; + int ret; + + ret = hygon_read_df_reg(misc, HYGON_DF_F1, DF_F1_SYSTEM_CFG, ®); + if (ret) + return ret; + + *socket_present = reg & 0xff; + if (!*socket_present) { + pr_warn("SocketPresent is zero\n"); + return -EINVAL; + } + + return 0; +} + +static void __init hygon_release_nodes(struct hygon_node *nodes, u16 count) +{ + u16 i; + + for (i = 0; i < count; i++) { + pci_dev_put(nodes[i].misc); + pci_dev_put(nodes[i].link); + } + + kfree(nodes); +} + +/* + * Walk all DF misc (F3) devices and read per-node identity (socket_id, + * dfid) from each, collecting them into a flat array. The system-wide + * SocketPresent mask is sampled once, and the enumerated socket set is + * validated against it; socket IDs must also be dense (0..N-1). + */ +static int __init hygon_collect_nodes(struct hygon_node_cache *cache) +{ + struct hygon_node *nodes; + struct pci_dev *misc; + u16 capacity = 0, count = 0; + u8 observed_socket_mask = 0; + u8 socket_present_mask = 0; + int ret; + + misc = NULL; + while ((misc = next_hygon_dev(misc, hygon_nb_misc_ids))) + capacity++; + + if (!capacity) + return -ENODEV; + + nodes = kcalloc(capacity, sizeof(*nodes), GFP_KERNEL); + if (!nodes) + return -ENOMEM; + + misc = NULL; + while ((misc = next_hygon_dev(misc, hygon_nb_misc_ids))) { + struct hygon_df_id id; + struct pci_dev *link; + + link = hygon_get_link(misc); + if (!link) { + pci_dev_put(misc); + ret = -ENODEV; + goto fail; + } + + ret = hygon_read_df_id(misc, &id); + if (ret) { + pci_dev_put(link); + pci_dev_put(misc); + goto fail; + } + + /* SocketPresent is system-wide, so sample it once. */ + if (!count) { + ret = hygon_read_socket_present(misc, + &socket_present_mask); + if (ret) { + pci_dev_put(link); + pci_dev_put(misc); + goto fail; + } + } + + if (count >= capacity) { + pci_dev_put(link); + pci_dev_put(misc); + ret = -ENODEV; + goto fail; + } + + pr_debug("collect: %04x:%02x:%02x.%u socket=%u dfid=%u\n", + pci_domain_nr(misc->bus), misc->bus->number, + PCI_SLOT(misc->devfn), PCI_FUNC(misc->devfn), + id.socket_id, id.dfid); + + nodes[count].misc = pci_dev_get(misc); + nodes[count].link = link; + nodes[count].socket_id = id.socket_id; + nodes[count].dfid = id.dfid; + nodes[count].is_cdd = (id.dfid >= HYGON_CDD_DFID_BASE); + count++; + + observed_socket_mask |= BIT(id.socket_id); + } + + if (count != capacity) { + pr_warn("DF enumeration changed: expected %u nodes, got %u\n", + capacity, count); + ret = -ENODEV; + goto fail; + } + + /* + * Every populated socket must contribute at least one enumerated DF + * misc device. socket_present_mask is guaranteed to be non-zero. + */ + if (observed_socket_mask != socket_present_mask) { + pr_warn("SocketPresent mismatch: observed=0x%x present=0x%x\n", + observed_socket_mask, socket_present_mask); + ret = -EINVAL; + goto fail; + } + + cache->nodes = nodes; + cache->num_nodes = count; + cache->num_sockets = hweight8(socket_present_mask); + + if (socket_present_mask != GENMASK(cache->num_sockets - 1, 0)) { + pr_warn("sparse socket IDs not supported (SocketPresent=0x%x)\n", + socket_present_mask); + ret = -EINVAL; + goto fail; + } + + return 0; + +fail: + hygon_release_nodes(nodes, count); + cache->nodes = NULL; + cache->num_nodes = 0; + return ret; +} + +/* + * Sort CDD nodes before IOD nodes, then order by hardware + * (socket_id, dfid). + */ +static int __init hygon_node_cmp(const void *a, const void *b) +{ + const struct hygon_node *left = a; + const struct hygon_node *right = b; + + if (left->is_cdd != right->is_cdd) + return right->is_cdd - left->is_cdd; + + if (left->socket_id != right->socket_id) + return (int)left->socket_id - right->socket_id; + + return (int)left->dfid - (int)right->dfid; +} + +/* + * Classify the sorted node array and validate the CDD layout. + * + * A dense CDD mapping requires every socket to contribute the same + * number of compute dies. + */ +static int __init hygon_sort_and_classify(struct hygon_node_cache *cache) +{ + u16 cdd_per_socket; + u16 i; + u8 per_sock_count[HYGON_MAX_SOCKETS] = { 0 }; + + hygon_dump_nodes(cache, "before-sort"); + + sort(cache->nodes, cache->num_nodes, sizeof(*cache->nodes), + hygon_node_cmp, NULL); + + for (i = 1; i < cache->num_nodes; i++) { + const struct hygon_node *prev = &cache->nodes[i - 1]; + const struct hygon_node *node = &cache->nodes[i]; + + if (node->socket_id == prev->socket_id && + node->dfid == prev->dfid) { + pr_warn("duplicate DF node: socket=%u dfid=%u\n", + node->socket_id, node->dfid); + return -EINVAL; + } + } + + for (i = 0; i < cache->num_nodes; i++) { + if (!cache->nodes[i].is_cdd) + break; + } + + cache->num_cdd = i; + + if (!cache->num_cdd) + return -ENODEV; + + if (cache->num_cdd % cache->num_sockets) { + pr_warn("CDD count %u not divisible by %u sockets\n", + cache->num_cdd, cache->num_sockets); + return -EINVAL; + } + + cdd_per_socket = cache->num_cdd / cache->num_sockets; + if (!cdd_per_socket) + return -EINVAL; + + for (i = 0; i < cache->num_cdd; i++) { + u8 socket_id = cache->nodes[i].socket_id; + + if (socket_id >= cache->num_sockets) + return -EINVAL; + per_sock_count[socket_id]++; + } + + for (i = 0; i < cache->num_sockets; i++) { + if (per_sock_count[i] != cdd_per_socket) { + pr_warn("socket %u: %u CDDs, expected %u\n", + i, per_sock_count[i], cdd_per_socket); + return -EINVAL; + } + } + + hygon_dump_nodes(cache, "after-sort"); + + return 0; +} + +/* + * Build the global DF node cache. + * + * Called once from hygon_node_init() at fs_initcall, so no locking is + * required. Set ready after node collection and sorting. + */ +static int __init hygon_build_cache(void) +{ + int err; + + err = hygon_collect_nodes(&hygon_cache); + if (err) + return err; + + err = hygon_sort_and_classify(&hygon_cache); + if (err) + goto fail; + + hygon_cache.ready = true; + + return 0; + +fail: + hygon_release_nodes(hygon_cache.nodes, hygon_cache.num_nodes); + hygon_cache.nodes = NULL; + hygon_cache.num_nodes = 0; + hygon_cache.num_cdd = 0; + hygon_cache.num_sockets = 0; + return err; +} + +u16 hygon_node_num(void) +{ + return hygon_cache.ready ? hygon_cache.num_nodes : 0; +} +EXPORT_SYMBOL_GPL(hygon_node_num); + +u16 hygon_cdd_num(void) +{ + return hygon_cache.ready ? hygon_cache.num_cdd : 0; +} +EXPORT_SYMBOL_GPL(hygon_cdd_num); + +int hygon_node_get_info(u16 node, struct hygon_node_info *info) +{ + const struct hygon_node *n; + + if (!info) + return -EINVAL; + + if (!hygon_cache.ready) + return -ENODEV; + + if (node >= hygon_cache.num_nodes) + return -EINVAL; + + n = &hygon_cache.nodes[node]; + info->socket_id = n->socket_id; + info->dfid = n->dfid; + info->type = n->is_cdd ? HYGON_NODE_CDD : HYGON_NODE_IOD; + return 0; +} +EXPORT_SYMBOL_GPL(hygon_node_get_info); + +struct pci_dev *hygon_node_get_func(u16 node, u8 func) +{ + if (!hygon_cache.ready) + return NULL; + + if (node >= hygon_cache.num_nodes) + return NULL; + + switch (func) { + case HYGON_DF_F3: + return pci_dev_get(hygon_cache.nodes[node].misc); + case HYGON_DF_F4: + return pci_dev_get(hygon_cache.nodes[node].link); + default: + return NULL; + } +} +EXPORT_SYMBOL_GPL(hygon_node_get_func); + +int hygon_pci_dev_to_df_node(struct pci_dev *pdev) +{ + u16 i; + + if (!pdev) + return -EINVAL; + + if (!hygon_cache.ready) + return -ENODEV; + + for (i = 0; i < hygon_cache.num_nodes; i++) { + struct pci_dev *misc = hygon_cache.nodes[i].misc; + + if (pci_domain_nr(misc->bus) == pci_domain_nr(pdev->bus) && + misc->bus->number == pdev->bus->number && + PCI_SLOT(misc->devfn) == PCI_SLOT(pdev->devfn)) + return i; + } + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(hygon_pci_dev_to_df_node); + +/* + * Build the Hygon DF node cache at fs_initcall. + */ +static int __init hygon_node_init(void) +{ + int ret; + + if (!hygon_get_df_cfg()) { + if (is_hygon_f18h()) + pr_info("Hygon Fam18h model 0x%x is not supported by the node layer\n", + boot_cpu_data.x86_model); + return 0; + } + + ret = hygon_build_cache(); + if (ret) { + pr_warn("DF node cache build failed: %d\n", ret); + return ret; + } + + return 0; +} +fs_initcall(hygon_node_init); -- 2.43.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v4 2/5] x86/hygon: Add Family 0x18 DF node enumeration 2026-09-01 7:16 ` [PATCH v4 2/5] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang @ 2026-09-01 7:29 ` sashiko-bot 0 siblings, 0 replies; 21+ messages in thread From: sashiko-bot @ 2026-09-01 7:29 UTC (permalink / raw) To: Lin Wang; +Cc: linux-pci > The AMD node code locates function 3 for node N at PCI slot 0x18 + N. > Hygon has no equivalent node-index-to-slot mapping: DF F3 devices are > discovered by PCI ID and the PCI BDF is only an access point. > > F1x200 provides the socket ID, SocketPresent mask and, on Models 0x04 > and 0x05, the DFID. Models 0x06 through 0x08 provide the DFID in > F5x180. The resulting (socket ID, DFID) pair identifies a DF node, while > SocketPresent verifies that enumeration covers every populated socket. > > Order CDDs by socket and DFID, followed by IODs, and assign dense node > indices. Expose node counts, identity, F3/F4 lookup and PCI-device > lookup through <asm/hygon/node.h>. > > Signed-off-by: Lin Wang <wanglin@open-hieco.net> Sashiko has reviewed this patch and found no issues. It looks great! -- Sashiko AI review · https://sashiko.dev/#/patchset/20260901071700.3255382-1-wanglin@open-hieco.net?part=2 ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v4 3/5] x86/hygon: Map CPU NodeIds to DF nodes 2026-09-01 7:16 [PATCH v4 0/5] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang 2026-09-01 7:16 ` [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs Lin Wang 2026-09-01 7:16 ` [PATCH v4 2/5] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang @ 2026-09-01 7:16 ` Lin Wang 2026-09-01 7:32 ` sashiko-bot 2026-09-01 7:16 ` [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 Lin Wang 2026-09-01 7:17 ` [PATCH v4 5/5] x86/hygon: Add Family 0x18 SMN access Lin Wang 4 siblings, 1 reply; 21+ messages in thread From: Lin Wang @ 2026-09-01 7:16 UTC (permalink / raw) To: Borislav Petkov, yazen.ghannam, mario.limonciello Cc: tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On bare-metal Hygon systems, CPUID 0x8000001E ECX[7:0] encodes a CPU's socket and local CDD ordinal. The value is sparse across sockets and cannot be used as an index into the dense DF node cache. Translate the CPUID value through the CDDs sorted by socket and DFID. The NodeId reported by CPUID in a guest may not describe the physical DF topology. Return -ENODEV in guests or when the NodeId does not identify an enumerated CDD. Signed-off-by: Lin Wang <wanglin@open-hieco.net> --- arch/x86/include/asm/hygon/node.h | 21 ++++++++++++ arch/x86/kernel/hygon_node.c | 53 +++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/arch/x86/include/asm/hygon/node.h b/arch/x86/include/asm/hygon/node.h index 6e15c133b774..bc4e588ab405 100644 --- a/arch/x86/include/asm/hygon/node.h +++ b/arch/x86/include/asm/hygon/node.h @@ -82,6 +82,22 @@ u16 hygon_cdd_num(void); */ int hygon_node_get_info(u16 node, struct hygon_node_info *info); +/** + * hygon_cpu_to_df_node() - map CPU to dense DF CDD index + * @cpu: CPU index + * + * Hygon Fam18h exposes sparse physical node IDs via CPUID 8000001E[7:0]. + * This function translates the per-CPU physical node ID into a dense + * DF CDD index in [0, hygon_cdd_num()). The NodeId reported by CPUID in + * a guest may not describe the physical DF topology, so the translation + * is unavailable in guests. + * + * Return: DF CDD index on success, -EINVAL if @cpu is out of range, + * -ENODEV if CPU-to-DF mapping is unavailable or the physical node ID + * does not map to a known DF node. + */ +int hygon_cpu_to_df_node(unsigned int cpu); + /** * hygon_node_get_func() - get DF function PCI device for a node * @node: DF node index in [0, hygon_node_num()) @@ -126,6 +142,11 @@ static inline int hygon_node_get_info(u16 node, struct hygon_node_info *info) return -ENODEV; } +static inline int hygon_cpu_to_df_node(unsigned int cpu) +{ + return -ENODEV; +} + static inline struct pci_dev *hygon_node_get_func(u16 node, u8 func) { return NULL; diff --git a/arch/x86/kernel/hygon_node.c b/arch/x86/kernel/hygon_node.c index 85835938315b..e2aa7de180e9 100644 --- a/arch/x86/kernel/hygon_node.c +++ b/arch/x86/kernel/hygon_node.c @@ -16,6 +16,8 @@ #define pr_fmt(fmt) "hygon_node: " fmt #include <linux/bitops.h> +#include <linux/cpu.h> +#include <linux/cpufeature.h> #include <linux/export.h> #include <linux/init.h> #include <linux/pci.h> @@ -23,6 +25,7 @@ #include <linux/processor.h> #include <linux/slab.h> #include <linux/sort.h> +#include <linux/topology.h> #include <asm/cpu_device_id.h> #include <asm/hygon/node.h> @@ -554,6 +557,40 @@ static int __init hygon_sort_and_classify(struct hygon_node_cache *cache) return 0; } +/* + * Translate a Hygon Fam18h phys_node_id (CPUID 8000001E ECX[7:0]) to a + * dense DF CDD index. On supported models, the node layer uses + * this encoding: + * + * phys_node_id = (socket_id << 4) | local_cdd_index_in_dfid_order + * + * The cache->nodes[] CDD region is sorted by (socket_id ASC, dfid ASC), + * so walk it and return the actual cache index of the CDD whose + * socket_id matches and whose socket-local ordinal is @local. + * + * Return -ENODEV if the cache is unavailable or no CDD matches. + */ +static int hygon_phys_nid_to_df_node(unsigned int phys_nid) +{ + unsigned int socket = phys_nid >> 4; + unsigned int local = phys_nid & 0xf; + unsigned int ordinal = 0; + u16 i; + + if (!hygon_cache.ready) + return -ENODEV; + + for (i = 0; i < hygon_cache.num_cdd; i++) { + if (hygon_cache.nodes[i].socket_id != socket) + continue; + + if (ordinal++ == local) + return i; + } + + return -ENODEV; +} + /* * Build the global DF node cache. * @@ -618,6 +655,22 @@ int hygon_node_get_info(u16 node, struct hygon_node_info *info) } EXPORT_SYMBOL_GPL(hygon_node_get_info); +int hygon_cpu_to_df_node(unsigned int cpu) +{ + if (cpu >= nr_cpu_ids) + return -EINVAL; + + /* + * The NodeId reported by CPUID in a guest may not describe the + * physical DF topology. + */ + if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR)) + return -ENODEV; + + return hygon_phys_nid_to_df_node(topology_amd_node_id(cpu)); +} +EXPORT_SYMBOL_GPL(hygon_cpu_to_df_node); + struct pci_dev *hygon_node_get_func(u16 node, u8 func) { if (!hygon_cache.ready) -- 2.43.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v4 3/5] x86/hygon: Map CPU NodeIds to DF nodes 2026-09-01 7:16 ` [PATCH v4 3/5] x86/hygon: Map CPU NodeIds to DF nodes Lin Wang @ 2026-09-01 7:32 ` sashiko-bot 0 siblings, 0 replies; 21+ messages in thread From: sashiko-bot @ 2026-09-01 7:32 UTC (permalink / raw) To: Lin Wang; +Cc: linux-pci > On bare-metal Hygon systems, CPUID 0x8000001E ECX[7:0] encodes a > CPU's socket and local CDD ordinal. The value is sparse across sockets > and cannot be used as an index into the dense DF node cache. > > Translate the CPUID value through the CDDs sorted by socket and DFID. > The NodeId reported by CPUID in a guest may not describe the physical > DF topology. Return -ENODEV in guests or when the NodeId does not > identify an enumerated CDD. > > Signed-off-by: Lin Wang <wanglin@open-hieco.net> Sashiko has reviewed this patch and found no issues. It looks great! -- Sashiko AI review · https://sashiko.dev/#/patchset/20260901071700.3255382-1-wanglin@open-hieco.net?part=3 ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-01 7:16 [PATCH v4 0/5] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang ` (2 preceding siblings ...) 2026-09-01 7:16 ` [PATCH v4 3/5] x86/hygon: Map CPU NodeIds to DF nodes Lin Wang @ 2026-09-01 7:16 ` Lin Wang 2026-09-01 7:34 ` sashiko-bot 2026-09-02 2:48 ` Borislav Petkov 2026-09-01 7:17 ` [PATCH v4 5/5] x86/hygon: Add Family 0x18 SMN access Lin Wang 4 siblings, 2 replies; 21+ messages in thread From: Lin Wang @ 2026-09-01 7:16 UTC (permalink / raw) To: Borislav Petkov, yazen.ghannam, mario.limonciello Cc: tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel amd_smn_init() distributes host bridge roots according to amd_num_nodes() and PCI enumeration order. Hygon Fam18h models 0x04-0x08 have multiple DF nodes per socket and group SMN roots by socket, so the AMD mapping cannot provide their node-to-root map. Leave the roots unclaimed on these models regardless of CONFIG_HYGON_NODE; the AMD mapping does not apply to their topology. Since amd_num_nodes() is independent of the root array, check smn_exclusive before indexing the array so AMD SMN calls return -ENODEV while its setup is inactive. When built, the Hygon node layer provides the node-to-root mapping. Other Hygon models keep the existing AMD SMN setup. amd_smn_init() can become AMD-only after the remaining Hygon callers have moved to Hygon-specific interfaces. Signed-off-by: Lin Wang <wanglin@open-hieco.net> --- arch/x86/kernel/amd_node.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/amd_node.c b/arch/x86/kernel/amd_node.c index 0be01725a2a4..a0cfd74ada03 100644 --- a/arch/x86/kernel/amd_node.c +++ b/arch/x86/kernel/amd_node.c @@ -88,6 +88,9 @@ 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 (!smn_exclusive) + return err; + if (node >= amd_num_nodes()) return err; @@ -95,9 +98,6 @@ static int __amd_smn_rw(u8 i_off, u8 d_off, u16 node, u32 address, u32 *value, b if (!root) return err; - if (!smn_exclusive) - return err; - guard(mutex)(&smn_mutex); err = pci_write_config_dword(root, i_off, address); @@ -252,6 +252,18 @@ static int __init amd_smn_init(void) if (!cpu_feature_enabled(X86_FEATURE_ZEN)) return 0; + /* + * Hygon models outside this range, including legacy parts, keep the + * existing AMD SMN setup. Models 0x04-0x08 group SMN roots by socket + * instead of amd_num_nodes(), so leave AMD SMN inactive. The Hygon node + * layer, when built, owns their node-to-root mapping. + */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && + boot_cpu_data.x86 == 0x18 && + boot_cpu_data.x86_model >= 0x04 && + boot_cpu_data.x86_model <= 0x08) + return 0; + guard(mutex)(&smn_mutex); if (amd_roots) -- 2.43.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-01 7:16 ` [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 Lin Wang @ 2026-09-01 7:34 ` sashiko-bot 2026-09-02 2:48 ` Borislav Petkov 1 sibling, 0 replies; 21+ messages in thread From: sashiko-bot @ 2026-09-01 7:34 UTC (permalink / raw) To: Lin Wang; +Cc: linux-pci Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: Pre-existing issues: - [High] amd_smn_init() stores pci_dev pointers in the amd_roots array without acquiring independent references, leading to an under-refcounted device and a use-after-free risk. - [High] amd_smn_read() unconditionally evaluates uninitialized memory when __amd_smn_rw() fails early, causing Undefined Behavior. - [High] Kernel panic due to a modulo by zero if the number of PCI root bridges is less than the number of AMD nodes. -- commit 64cdde27c04c50425e1c00a79b17abd2a38904c9 Author: Lin Wang <wanglin@open-hieco.net> x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 This commit skips AMD SMN setup on specific Hygon Fam18h models (0x04-0x08). It prevents incorrect node-to-root mappings by leaving the roots unclaimed, returning -ENODEV for SMN calls while setup is inactive. > diff --git a/arch/x86/kernel/amd_node.c b/arch/x86/kernel/amd_node.c > index 0be01725a2a45..a0cfd74ada036 100644 > --- a/arch/x86/kernel/amd_node.c > +++ b/arch/x86/kernel/amd_node.c > @@ -88,6 +88,9 @@ 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 (!smn_exclusive) > + return err; > + [Severity: High] This isn't a bug introduced by this patch, but this patch guarantees it will trigger routinely on the excluded Hygon models. Does this early return result in an uninitialized variable read? When __amd_smn_rw() returns early here because smn_exclusive is false (which this patch forces for specific Hygon models), the output parameter value is never written to. Callers typically pass a pointer to an uninitialized local variable, for example in amd_smn_read(): arch/x86/kernel/amd_node.c:amd_smn_read() { int err = __amd_smn_rw(..., value, false); if (PCI_POSSIBLE_ERROR(*value)) { err = -ENODEV; *value = 0; } ... } Evaluating PCI_POSSIBLE_ERROR(*value) when *value is uninitialized causes Undefined Behavior and can lead to information leaks or KMSAN kernel panics. [ ... ] > @@ -252,6 +252,18 @@ static int __init amd_smn_init(void) > if (!cpu_feature_enabled(X86_FEATURE_ZEN)) > return 0; > > + /* > + * Hygon models outside this range, including legacy parts, keep the > + * existing AMD SMN setup. Models 0x04-0x08 group SMN roots by socket > + * instead of amd_num_nodes(), so leave AMD SMN inactive. The Hygon node > + * layer, when built, owns their node-to-root mapping. > + */ > + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && > + boot_cpu_data.x86 == 0x18 && > + boot_cpu_data.x86_model >= 0x04 && > + boot_cpu_data.x86_model <= 0x08) > + return 0; > + > guard(mutex)(&smn_mutex); > > if (amd_roots) [Severity: High] This is a pre-existing issue, but it remains a severe flaw for other configurations (and is not fixed in subsequent patches in the series). Can this cause a kernel panic due to a modulo by zero? In amd_smn_init(), roots_per_node is calculated as num_roots / num_nodes. If num_roots is less than num_nodes (e.g. due to virtualized hardware, a broken BIOS, or unusual topologies), roots_per_node evaluates to 0. The subsequent loop performs a modulo operation: arch/x86/kernel/amd_node.c:amd_smn_init() { ... if (count++ % roots_per_node) continue; ... } This division by zero triggers a Divide Error (#DE) trap, leading to an immediate kernel panic during boot on affected hardware environments. [Severity: High] This isn't a bug introduced by this patch, but it remains a severe flaw (and is not fixed in subsequent patches in the series). Is there a missing reference count increment when storing device pointers in the amd_roots array? In the amd_smn_init() loop, the device pointer is saved without acquiring an independent reference: arch/x86/kernel/amd_node.c:amd_smn_init() { ... while (node < num_nodes && (root = get_next_root(root))) { if (count++ % roots_per_node) continue; pci_dbg(root, "is root for AMD node %u\n", node); amd_roots[node++] = root; } ... } On the next loop iteration, get_next_root(root) is called, which delegates to pci_get_class() and implicitly drops the reference count of the passed device. Since this executes unconditionally during a successful boot on AMD hardware, the array holds pointers to under-refcounted devices, creating a use-after-free risk for PCI host bridge devices. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260901071700.3255382-1-wanglin@open-hieco.net?part=4 ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-01 7:16 ` [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 Lin Wang 2026-09-01 7:34 ` sashiko-bot @ 2026-09-02 2:48 ` Borislav Petkov 2026-09-02 6:22 ` Lin Wang 1 sibling, 1 reply; 21+ messages in thread From: Borislav Petkov @ 2026-09-02 2:48 UTC (permalink / raw) To: Lin Wang Cc: yazen.ghannam, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On Tue, Sep 01, 2026 at 03:16:59PM +0800, Lin Wang wrote: > @@ -252,6 +252,18 @@ static int __init amd_smn_init(void) > if (!cpu_feature_enabled(X86_FEATURE_ZEN)) This driver should exit on Hygon right here. Why doesn't it do that? I know why - you set X86_FEATURE_ZEN. Why? What do you really need it for? -- Regards/Gruss, Boris. https://people.kernel.org/tglx/notes-about-netiquette ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-02 2:48 ` Borislav Petkov @ 2026-09-02 6:22 ` Lin Wang 2026-09-02 15:13 ` Borislav Petkov 0 siblings, 1 reply; 21+ messages in thread From: Lin Wang @ 2026-09-02 6:22 UTC (permalink / raw) To: Borislav Petkov Cc: yazen.ghannam, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On 9/2/2026 10:48 AM, Borislav Petkov wrote: > On Tue, Sep 01, 2026 at 03:16:59PM +0800, Lin Wang wrote: >> @@ -252,6 +252,18 @@ static int __init amd_smn_init(void) >> if (!cpu_feature_enabled(X86_FEATURE_ZEN)) > > This driver should exit on Hygon right here. > > Why doesn't it do that? > > I know why - you set X86_FEATURE_ZEN. > > Why? > > What do you really need it for? > Hi Boris, Thanks for pointing this out. X86_FEATURE_ZEN has been set in init_hygon() since the initial Hygon CPU support. I knew that it made amd_smn_init() run on Hygon, but I treated it as part of the existing CPU setup and did not revisit whether Hygon still needed it. However, removing it would change other existing code paths that use this feature. That would need a separate audit and hardware testing, which I did not do for this series. I am therefore not sure that changing the CPU capability here only to avoid an explicit Hygon check in the SMN code would be safe. Please let me know how you would prefer this to be handled. Thanks & Br; Lin ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-02 6:22 ` Lin Wang @ 2026-09-02 15:13 ` Borislav Petkov 2026-09-03 1:49 ` Lin Wang 0 siblings, 1 reply; 21+ messages in thread From: Borislav Petkov @ 2026-09-02 15:13 UTC (permalink / raw) To: Lin Wang Cc: yazen.ghannam, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On Wed, Sep 02, 2026 at 02:22:13PM +0800, Lin Wang wrote: > Please let me know how you would prefer this to be handled. Well, X86_FEATURE_ZEN isn't Hygon apparently. Otherwise, we wouldn't be having this conversation. And we wouldn't need ugly workarounds like that. Which means, you cannot set it on Hygon. Instead, you should check vendor or define your own X86_FEATURE_HYGON if you think it would be useful to denote common set of features between all Hygon cores. Reusing an AMD-specific flag but then having different hardware which needs hacks and workarounds to the code is not what you do. Hygon is a different vendor and it should be treated as such - not piggyback on AMD code. Unless you wanna piggyback. But then you have to extract it properly and make it generic so that it can be shared. I have been asking all people submitting Hygon code to do that and you are starting to do it but then you need to stick to it and do only that. Thx. -- Regards/Gruss, Boris. https://people.kernel.org/tglx/notes-about-netiquette ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-02 15:13 ` Borislav Petkov @ 2026-09-03 1:49 ` Lin Wang 2026-09-03 17:34 ` Yazen Ghannam 2026-09-04 5:36 ` Borislav Petkov 0 siblings, 2 replies; 21+ messages in thread From: Lin Wang @ 2026-09-03 1:49 UTC (permalink / raw) To: Borislav Petkov Cc: yazen.ghannam, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On 9/2/2026 11:13 PM, Borislav Petkov wrote: > On Wed, Sep 02, 2026 at 02:22:13PM +0800, Lin Wang wrote: >> Please let me know how you would prefer this to be handled. > > Well, X86_FEATURE_ZEN isn't Hygon apparently. Otherwise, we wouldn't be having > this conversation. And we wouldn't need ugly workarounds like that. > > Which means, you cannot set it on Hygon. Instead, you should check vendor or > define your own X86_FEATURE_HYGON if you think it would be useful to denote > common set of features between all Hygon cores. > > Reusing an AMD-specific flag but then having different hardware which needs > hacks and workarounds to the code is not what you do. Hygon is a different > vendor and it should be treated as such - not piggyback on AMD code. > > Unless you wanna piggyback. But then you have to extract it properly and make > it generic so that it can be shared. I have been asking all people submitting > Hygon code to do that and you are starting to do it but then you need to stick > to it and do only that. > Hi Boris, Thanks for clarifying the direction. My proposed approach is to use explicit vendor checks to make init_amd_nbs() and amd_smn_init() AMD-only. Hygon node enumeration and SMN setup would remain in Hygon code. Sashiko's concern with v3 was that the AMD initializers were restricted while Hygon call sites remained. However, as currently implemented, the AMD NB and SMN paths are based on AMD topology assumptions and cannot provide the Hygon node and SMN mappings. I would first stop the affected Hygon call sites from entering those AMD paths, and then make the two initializers AMD-only in the same series. Later driver support would use the Hygon interfaces. Would this ordering address the Sashiko concern? Thanks & Br, Lin ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-03 1:49 ` Lin Wang @ 2026-09-03 17:34 ` Yazen Ghannam 2026-09-04 2:43 ` Lin Wang 2026-09-04 5:36 ` Borislav Petkov 1 sibling, 1 reply; 21+ messages in thread From: Yazen Ghannam @ 2026-09-03 17:34 UTC (permalink / raw) To: Lin Wang Cc: Borislav Petkov, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On Thu, Sep 03, 2026 at 09:49:58AM +0800, Lin Wang wrote: > > > On 9/2/2026 11:13 PM, Borislav Petkov wrote: > > On Wed, Sep 02, 2026 at 02:22:13PM +0800, Lin Wang wrote: > > > Please let me know how you would prefer this to be handled. > > > > Well, X86_FEATURE_ZEN isn't Hygon apparently. Otherwise, we wouldn't be having > > this conversation. And we wouldn't need ugly workarounds like that. > > > > Which means, you cannot set it on Hygon. Instead, you should check vendor or > > define your own X86_FEATURE_HYGON if you think it would be useful to denote > > common set of features between all Hygon cores. > > > > Reusing an AMD-specific flag but then having different hardware which needs > > hacks and workarounds to the code is not what you do. Hygon is a different > > vendor and it should be treated as such - not piggyback on AMD code. > > > > Unless you wanna piggyback. But then you have to extract it properly and make > > it generic so that it can be shared. I have been asking all people submitting > > Hygon code to do that and you are starting to do it but then you need to stick > > to it and do only that. > > > > Hi Boris, > > Thanks for clarifying the direction. > > My proposed approach is to use explicit vendor checks to make > init_amd_nbs() and amd_smn_init() AMD-only. Hygon node enumeration and > SMN setup would remain in Hygon code. > > Sashiko's concern with v3 was that the AMD initializers were restricted > while Hygon call sites remained. However, as currently implemented, the > AMD NB and SMN paths are based on AMD topology assumptions and cannot > provide the Hygon node and SMN mappings. > The "vendor != HYGON" restriction would be fair. I don't understand how the "model-specific" restriction is better. Wouldn't it cause similar problems? > I would first stop the affected Hygon call sites from entering those AMD > paths, and then make the two initializers AMD-only in the same series. > Later driver support would use the Hygon interfaces. > > Would this ordering address the Sashiko concern? > Right, so first you provide a Hygon-only option for all affected call sites. Then you add the vendor check. This ensures that the affected callers don't break in the middle of the patch series. Is this your intent? Regarding the Zen feature flag, I think it may be overloaded. Hygon systems are Zen systems. The same Zen1 architecture was used orginally, though I don't know how much things have diverged since then. All Hygon systems are Zen systems, but not all AMD systems are Zen systems. So we have three diverging Platforms: 1) AMD legacy (Northbridge / !Zen) 2) AMD modern (Zen) 3) Hygon (Zen by default) (2) and (3) where the same until recently. Maybe we define new feature flags that indicate a Platform/Interconnect group? That way we won't need to rely on PCI IDs or vendor+core combinations. FYI, that was the intent to use the Zen feature flag here. It was a way to distinguish (1) and (2/3) above. It worked until it didn't. :) Thanks, Yazen ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-03 17:34 ` Yazen Ghannam @ 2026-09-04 2:43 ` Lin Wang 0 siblings, 0 replies; 21+ messages in thread From: Lin Wang @ 2026-09-04 2:43 UTC (permalink / raw) To: Yazen Ghannam Cc: Borislav Petkov, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel Hi Yazen, Thanks for your feedback! On 9/4/2026 1:34 AM, Yazen Ghannam wrote: > On Thu, Sep 03, 2026 at 09:49:58AM +0800, Lin Wang wrote: >> >> My proposed approach is to use explicit vendor checks to make >> init_amd_nbs() and amd_smn_init() AMD-only. Hygon node enumeration and >> SMN setup would remain in Hygon code. >> >> Sashiko's concern with v3 was that the AMD initializers were restricted >> while Hygon call sites remained. However, as currently implemented, the >> AMD NB and SMN paths are based on AMD topology assumptions and cannot >> provide the Hygon node and SMN mappings. >> > > The "vendor != HYGON" restriction would be fair. > > I don't understand how the "model-specific" restriction is better. > Wouldn't it cause similar problems? It was intended only to limit the scope of the change and address the Sashiko finding, but it does not provide the AMD SMN code with a valid Hygon node-to-root mapping. I will drop the model-specific check. > >> I would first stop the affected Hygon call sites from entering those AMD >> paths, and then make the two initializers AMD-only in the same series. >> Later driver support would use the Hygon interfaces. >> >> Would this ordering address the Sashiko concern? >> > > Right, so first you provide a Hygon-only option for all affected call > sites. Then you add the vendor check. This ensures that the affected > callers don't break in the middle of the patch series. > > Is this your intent? Not quite. By stopping the affected call sites, I meant removing the Hygon match entries from the drivers which currently route them through the AMD NB or SMN mapping. I did not mean adding complete Hygon EDAC, ATL and k10temp support in this series. These paths do not provide usable Hygon support with the current AMD node mapping. Those implementations depend on the Hygon node and SMN interfaces. The intended order is: 1. add the Hygon node interfaces; 2. stop the affected Hygon drivers from entering the AMD paths; 3. make AMD NB and SMN initialization AMD-only; 4. add the Hygon SMN implementation. EDAC, ATL and temperature monitoring support can then be submitted separately using the Hygon interfaces. This ordering addresses the v3 Sashiko concern without making those drivers a prerequisite for the node layer. > > Regarding the Zen feature flag, I think it may be overloaded. > > Hygon systems are Zen systems. The same Zen1 architecture was used > orginally, though I don't know how much things have diverged since then. > > All Hygon systems are Zen systems, but not all AMD systems are Zen > systems. > > So we have three diverging Platforms: > 1) AMD legacy (Northbridge / !Zen) > 2) AMD modern (Zen) > 3) Hygon (Zen by default) > > (2) and (3) where the same until recently. > > Maybe we define new feature flags that indicate a Platform/Interconnect > group? That way we won't need to rely on PCI IDs or vendor+core > combinations. I agree that X86_FEATURE_ZEN describes the core architecture and should not be used to select an interconnect implementation. A separate interconnect capability may be useful, but defining it and converting its users would be broader than this node series. For this series, I would prefer to use explicit vendor checks and keep the interconnect capability work separate. That is the ordering I intended. Thanks & Br; Lin > > FYI, that was the intent to use the Zen feature flag here. It was a way > to distinguish (1) and (2/3) above. It worked until it didn't. :) > > Thanks, > Yazen ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-03 1:49 ` Lin Wang 2026-09-03 17:34 ` Yazen Ghannam @ 2026-09-04 5:36 ` Borislav Petkov 2026-09-04 6:26 ` Lin Wang 1 sibling, 1 reply; 21+ messages in thread From: Borislav Petkov @ 2026-09-04 5:36 UTC (permalink / raw) To: Lin Wang Cc: yazen.ghannam, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On Thu, Sep 03, 2026 at 09:49:58AM +0800, Lin Wang wrote: > > > On 9/2/2026 11:13 PM, Borislav Petkov wrote: > > On Wed, Sep 02, 2026 at 02:22:13PM +0800, Lin Wang wrote: > > > Please let me know how you would prefer this to be handled. > > > > Well, X86_FEATURE_ZEN isn't Hygon apparently. Otherwise, we wouldn't be having > > this conversation. And we wouldn't need ugly workarounds like that. > > > > Which means, you cannot set it on Hygon. Instead, you should check vendor or > > define your own X86_FEATURE_HYGON if you think it would be useful to denote > > common set of features between all Hygon cores. > > > > Reusing an AMD-specific flag but then having different hardware which needs > > hacks and workarounds to the code is not what you do. Hygon is a different > > vendor and it should be treated as such - not piggyback on AMD code. > > > > Unless you wanna piggyback. But then you have to extract it properly and make > > it generic so that it can be shared. I have been asking all people submitting > > Hygon code to do that and you are starting to do it but then you need to stick > > to it and do only that. > > > > Hi Boris, > > Thanks for clarifying the direction. > > My proposed approach is to use explicit vendor checks to make > init_amd_nbs() and amd_smn_init() AMD-only. Hygon node enumeration and > SMN setup would remain in Hygon code. > > Sashiko's concern with v3 was that the AMD initializers were restricted > while Hygon call sites remained. However, as currently implemented, the > AMD NB and SMN paths are based on AMD topology assumptions and cannot > provide the Hygon node and SMN mappings. > > I would first stop the affected Hygon call sites from entering those AMD > paths, and then make the two initializers AMD-only in the same series. > Later driver support would use the Hygon interfaces. > > Would this ordering address the Sashiko concern? This should address my concerns - we can then see what Sashiko complains about and sanity-check it. And I explained it already - you basically carve out/split/separate Hygon paths from AMD paths and you basically forget X86_FEATURE_ZEN. I don't care if Zen and Hygon systems are/were the same or almost the same. Obviously they will start diverging so you can carve out Hygon-specific pieces now and then keep adding new functionality to them. Just as if this is a completely different x86 CPU. This way, the code is untangled from the very beginning and there won't be any ugly and unmaintainable if (vendor) sprinkling all over the x86 AMD code where neither you nor we will be happy about going forward. Thx. -- Regards/Gruss, Boris. https://people.kernel.org/tglx/notes-about-netiquette ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 2026-09-04 5:36 ` Borislav Petkov @ 2026-09-04 6:26 ` Lin Wang 0 siblings, 0 replies; 21+ messages in thread From: Lin Wang @ 2026-09-04 6:26 UTC (permalink / raw) To: Borislav Petkov Cc: yazen.ghannam, mario.limonciello, tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel On 9/4/2026 1:36 PM, Borislav Petkov wrote: >> Would this ordering address the Sashiko concern? > This should address my concerns - we can then see what Sashiko complains > about and sanity-check it. > > And I explained it already - you basically carve out/split/separate Hygon > paths from AMD paths and you basically forget X86_FEATURE_ZEN. I don't care if > Zen and Hygon systems are/were the same or almost the same. > > Obviously they will start diverging so you can carve out Hygon-specific pieces > now and then keep adding new functionality to them. Just as if this is > a completely different x86 CPU. > > This way, the code is untangled from the very beginning and there won't be any > ugly and unmaintainable > > if (vendor) > > sprinkling all over the x86 AMD code where neither you nor we will be happy > about going forward. > Hi Boris, Thanks, this is clear. I understand that Hygon should use its own node and SMN implementation rather than adding Hygon-specific handling to the AMD paths. I will follow this separation in v5 and in the later driver support. My concern was how to address the Sashiko finding without making the later Hygon driver support a prerequisite for the node layer. This ordering resolves that concern. For v5, I will remove the existing Hygon matches which rely on the AMD NB or SMN implementation before making those initializers AMD-only. Thanks & Br, Lin ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v4 5/5] x86/hygon: Add Family 0x18 SMN access 2026-09-01 7:16 [PATCH v4 0/5] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang ` (3 preceding siblings ...) 2026-09-01 7:16 ` [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 Lin Wang @ 2026-09-01 7:17 ` Lin Wang 2026-09-01 7:28 ` sashiko-bot 4 siblings, 1 reply; 21+ messages in thread From: Lin Wang @ 2026-09-01 7:17 UTC (permalink / raw) To: Borislav Petkov, yazen.ghannam, mario.limonciello Cc: tglx, mingo, dave.hansen, H. Peter Anvin, bhelgaas, x86, linux-pci, linux-kernel Hygon Family 0x18 DF nodes in a socket share an SMN ingress. The root groups therefore need to be associated with sockets before they can be expanded to the dense DF node indices. Build the node-to-root map after DF enumeration and expose Hygon SMN read and write helpers. Hold a reference and reserve PCI config space for every root used by the built-in node layer. SMN remains unavailable if root discovery or mapping fails. Signed-off-by: Lin Wang <wanglin@open-hieco.net> --- arch/x86/include/asm/hygon/node.h | 38 ++++- arch/x86/kernel/hygon_node.c | 275 +++++++++++++++++++++++++++++- 2 files changed, 310 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/hygon/node.h b/arch/x86/include/asm/hygon/node.h index bc4e588ab405..b8a11d85eaee 100644 --- a/arch/x86/include/asm/hygon/node.h +++ b/arch/x86/include/asm/hygon/node.h @@ -2,8 +2,8 @@ /* * Hygon Family 0x18 Data Fabric node enumeration API * - * This header declares Hygon Fam18h DF node enumeration and function - * access interfaces. + * DF node identity, CPU-to-DF-node translation, DF function lookup and + * SMN access for Hygon Family 0x18. */ #ifndef _ASM_X86_HYGON_NODE_H #define _ASM_X86_HYGON_NODE_H @@ -125,6 +125,30 @@ struct pci_dev *hygon_node_get_func(u16 node, u8 func); */ int hygon_pci_dev_to_df_node(struct pci_dev *pdev); +/** + * hygon_smn_read() - read a 32-bit value from a Hygon SMN address + * @node: DF node index in [0, hygon_node_num()) + * @address: SMN address + * @value: output value + * + * Return: 0 on success, -EINVAL if @value is NULL, -ENODEV if Hygon + * SMN is not initialised or @node is out of range, or a negative + * errno from the underlying PCI config access. + */ +int __must_check hygon_smn_read(u16 node, u32 address, u32 *value); + +/** + * hygon_smn_write() - write a 32-bit value to a Hygon SMN address + * @node: DF node index in [0, hygon_node_num()) + * @address: SMN address + * @value: value to write + * + * Return: 0 on success, -ENODEV if Hygon SMN is not initialised or + * @node is out of range, or a negative errno from the underlying + * PCI config access. + */ +int __must_check hygon_smn_write(u16 node, u32 address, u32 value); + #else /* !CONFIG_HYGON_NODE */ static inline u16 hygon_node_num(void) @@ -157,6 +181,16 @@ static inline int hygon_pci_dev_to_df_node(struct pci_dev *pdev) return -ENODEV; } +static inline int hygon_smn_read(u16 node, u32 address, u32 *value) +{ + return -ENODEV; +} + +static inline int hygon_smn_write(u16 node, u32 address, u32 value) +{ + return -ENODEV; +} + #endif /* CONFIG_HYGON_NODE */ /* Inline helpers, available regardless of CONFIG_HYGON_NODE. */ diff --git a/arch/x86/kernel/hygon_node.c b/arch/x86/kernel/hygon_node.c index e2aa7de180e9..2fcd75ee71e9 100644 --- a/arch/x86/kernel/hygon_node.c +++ b/arch/x86/kernel/hygon_node.c @@ -16,10 +16,12 @@ #define pr_fmt(fmt) "hygon_node: " fmt #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/cpu.h> #include <linux/cpufeature.h> #include <linux/export.h> #include <linux/init.h> +#include <linux/mutex.h> #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/processor.h> @@ -714,8 +716,275 @@ int hygon_pci_dev_to_df_node(struct pci_dev *pdev) EXPORT_SYMBOL_GPL(hygon_pci_dev_to_df_node); /* - * Build the Hygon DF node cache at fs_initcall. + * SMN index/data register pair offsets in the host-bridge PCI config + * space. Reads and writes to a (node, address) pair are issued as a + * two-step transaction: write the SMN address to the index register, + * then read or write the value at the data register. */ +#define HYGON_SMN_INDEX_OFFSET 0x60 +#define HYGON_SMN_DATA_OFFSET 0x64 + +/* + * Runtime SMN state. hygon_smn_exclusive remains false until setup + * succeeds. hygon_smn_reserved_roots owns the independent PCI references + * and config-space reservations for the lifetime of the built-in node + * layer; hygon_smn_roots contains per-node aliases. + */ +static struct pci_dev **hygon_smn_roots; +static struct pci_dev **hygon_smn_reserved_roots; +static u16 hygon_smn_num_nodes; +static bool hygon_smn_exclusive; +static DEFINE_MUTEX(hygon_smn_mutex); + +/* Internal cache accessors used by SMN setup. */ +static u8 __init hygon_node_socket(u16 node) +{ + if (!hygon_cache.ready || node >= hygon_cache.num_nodes) + return U8_MAX; + return hygon_cache.nodes[node].socket_id; +} + +static u16 __init hygon_socket_num(void) +{ + return hygon_cache.ready ? hygon_cache.num_sockets : 0; +} + +/* + * Walk PCI host-bridge devices matching the Hygon vendor. The SMN + * index/data registers live in function 0 of each root complex. This + * follows pci_get_class() iterator semantics: @root is consumed and the + * returned device has an iterator reference. A retained device needs an + * independent reference before the iterator advances. + */ +static struct pci_dev * __init hygon_get_next_root(struct pci_dev *root) +{ + while ((root = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, root))) { + if (root->devfn) + continue; + if (root->vendor != PCI_VENDOR_ID_HYGON) + continue; + break; + } + return root; +} + +/* Release each config region before dropping its owning device reference. */ +static void __init hygon_release_reserved_roots(struct pci_dev **roots, + u16 count) +{ + u16 i; + + for (i = 0; i < count; i++) { + pci_release_config_region(roots[i], 0, PCI_CFG_SPACE_SIZE); + pci_dev_put(roots[i]); + } +} + +/* + * Select one root from each contiguous per-socket enumeration group, + * then map every DF node to the root for its socket. The root socket ID + * cannot be read back, so the grouping follows PCI enumeration order. + * + * hygon_smn_reserved_roots owns the PCI references and config regions; + * hygon_smn_roots contains per-node aliases. Enable SMN access only after + * both arrays are complete. + */ +static int __init hygon_smn_setup(void) +{ + struct pci_dev *socket_roots[HYGON_MAX_SOCKETS] = { }; + struct pci_dev **reserved_roots, **roots, *owned_root, *root; + u16 count, num_roots, roots_per_socket, node, num_nodes; + u16 num_sockets, reserved, socket; + u8 socket_id; + int ret; + + num_roots = 0; + root = NULL; + while ((root = hygon_get_next_root(root))) + num_roots++; + + pr_debug("Found %u Hygon SMN root devices\n", num_roots); + + if (!num_roots) + return -ENODEV; + + num_nodes = hygon_node_num(); + if (!num_nodes) + return -ENODEV; + + num_sockets = hygon_socket_num(); + if (!num_sockets) + return -ENODEV; + + if (num_sockets > ARRAY_SIZE(socket_roots)) { + pr_err("Socket count %u exceeds maximum %zu\n", + num_sockets, ARRAY_SIZE(socket_roots)); + return -EINVAL; + } + + if (num_roots % num_sockets) { + pr_err("Root count %u not divisible by socket count %u\n", + num_roots, num_sockets); + return -ENODEV; + } + + roots = kcalloc(num_nodes, sizeof(*roots), GFP_KERNEL); + if (!roots) + return -ENOMEM; + + reserved_roots = kcalloc(num_roots, sizeof(*reserved_roots), + GFP_KERNEL); + if (!reserved_roots) { + kfree(roots); + return -ENOMEM; + } + + /* + * Keep the first of every roots_per_socket consecutive roots and + * skip the rest. This groups roots by enumeration order, relying on + * the platform enumerating each socket's roots contiguously. Roots + * within the same socket are redundant SMN ingress points. + */ + roots_per_socket = num_roots / num_sockets; + socket = 0; + reserved = 0; + count = 0; + root = NULL; + while ((root = hygon_get_next_root(root))) { + if (reserved >= num_roots) { + ret = -ENODEV; + pci_dev_put(root); + goto err_release; + } + + pci_dbg(root, "Reserving PCI config space\n"); + + /* + * Mark the entire PCI config space kernel-exclusive because it + * contains the SMN index/data registers. + */ + if (!pci_request_config_region_exclusive(root, 0, + PCI_CFG_SPACE_SIZE, + NULL)) { + pci_err(root, "Failed to reserve config space\n"); + ret = -EEXIST; + /* This exit does not advance the iterator. */ + pci_dev_put(root); + goto err_release; + } + + owned_root = pci_dev_get(root); + reserved_roots[reserved++] = owned_root; + + if (count++ % roots_per_socket) + continue; + + if (socket >= num_sockets) { + ret = -ENODEV; + pci_dev_put(root); + goto err_release; + } + + pci_dbg(root, "is root for Hygon socket %u\n", socket); + socket_roots[socket++] = owned_root; + } + + if (reserved != num_roots || socket != num_sockets) { + pr_err("Root enumeration changed: expected %u roots/%u sockets, got %u/%u\n", + num_roots, num_sockets, reserved, socket); + ret = -ENODEV; + goto err_release; + } + + for (node = 0; node < num_nodes; node++) { + socket_id = hygon_node_socket(node); + + if (socket_id >= num_sockets) { + ret = -ENODEV; + goto err_release; + } + + pci_dbg(socket_roots[socket_id], + "is root for Hygon node %u (socket %u)\n", + node, socket_id); + roots[node] = socket_roots[socket_id]; + } + + hygon_smn_reserved_roots = reserved_roots; + hygon_smn_roots = roots; + hygon_smn_num_nodes = num_nodes; + hygon_smn_exclusive = true; + return 0; + +err_release: + hygon_release_reserved_roots(reserved_roots, reserved); + kfree(reserved_roots); + kfree(roots); + return ret; +} + +/* + * Serialize the PCI index/data pair between in-kernel SMN users. + * The transaction follows amd_smn_read/write(); + * hygon_smn_setup() provides the Hygon node-to-root mapping. + */ +static int __hygon_smn_rw(u16 node, u32 address, u32 *value, bool write) +{ + struct pci_dev *root; + int err; + + if (!hygon_smn_exclusive || node >= hygon_smn_num_nodes) + return -ENODEV; + + root = hygon_smn_roots[node]; + if (!root) + return -ENODEV; + + guard(mutex)(&hygon_smn_mutex); + + err = pci_write_config_dword(root, HYGON_SMN_INDEX_OFFSET, address); + if (err) { + pr_warn("SMN index write failed (addr 0x%x)\n", address); + return pcibios_err_to_errno(err); + } + + err = write ? pci_write_config_dword(root, HYGON_SMN_DATA_OFFSET, *value) + : pci_read_config_dword(root, HYGON_SMN_DATA_OFFSET, value); + + return pcibios_err_to_errno(err); +} + +int hygon_smn_read(u16 node, u32 address, u32 *value) +{ + int err; + + if (!value) + return -EINVAL; + + err = __hygon_smn_rw(node, address, value, false); + + /* Clear the output so callers do not consume a stale value. */ + if (err) { + *value = 0; + return err; + } + + /* Treat the PCI all-ones value as a missing device. */ + if (PCI_POSSIBLE_ERROR(*value)) { + *value = 0; + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_GPL(hygon_smn_read); + +int hygon_smn_write(u16 node, u32 address, u32 value) +{ + return __hygon_smn_rw(node, address, &value, true); +} +EXPORT_SYMBOL_GPL(hygon_smn_write); + static int __init hygon_node_init(void) { int ret; @@ -733,6 +1002,10 @@ static int __init hygon_node_init(void) return ret; } + ret = hygon_smn_setup(); + if (ret) + pr_warn("SMN setup failed: %d\n", ret); + return 0; } fs_initcall(hygon_node_init); -- 2.43.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v4 5/5] x86/hygon: Add Family 0x18 SMN access 2026-09-01 7:17 ` [PATCH v4 5/5] x86/hygon: Add Family 0x18 SMN access Lin Wang @ 2026-09-01 7:28 ` sashiko-bot 0 siblings, 0 replies; 21+ messages in thread From: sashiko-bot @ 2026-09-01 7:28 UTC (permalink / raw) To: Lin Wang; +Cc: linux-pci > Hygon Family 0x18 DF nodes in a socket share an SMN ingress. The root > groups therefore need to be associated with sockets before they can be > expanded to the dense DF node indices. > > Build the node-to-root map after DF enumeration and expose Hygon SMN > read and write helpers. Hold a reference and reserve PCI config space > for every root used by the built-in node layer. SMN remains unavailable > if root discovery or mapping fails. > > Signed-off-by: Lin Wang <wanglin@open-hieco.net> Sashiko has reviewed this patch and found no issues. It looks great! -- Sashiko AI review · https://sashiko.dev/#/patchset/20260901071700.3255382-1-wanglin@open-hieco.net?part=5 ^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-09-04 6:26 UTC | newest] Thread overview: 21+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-01 7:16 [PATCH v4 0/5] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang 2026-09-01 7:16 ` [PATCH v4 1/5] pci_ids: Add Hygon Family 0x18 DF F3/F4 device IDs Lin Wang 2026-09-01 7:34 ` sashiko-bot 2026-09-03 22:10 ` Bjorn Helgaas 2026-09-04 3:18 ` Lin Wang 2026-09-01 7:16 ` [PATCH v4 2/5] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang 2026-09-01 7:29 ` sashiko-bot 2026-09-01 7:16 ` [PATCH v4 3/5] x86/hygon: Map CPU NodeIds to DF nodes Lin Wang 2026-09-01 7:32 ` sashiko-bot 2026-09-01 7:16 ` [PATCH v4 4/5] x86/amd_node: Skip SMN setup on Hygon Fam18h models 0x04-0x08 Lin Wang 2026-09-01 7:34 ` sashiko-bot 2026-09-02 2:48 ` Borislav Petkov 2026-09-02 6:22 ` Lin Wang 2026-09-02 15:13 ` Borislav Petkov 2026-09-03 1:49 ` Lin Wang 2026-09-03 17:34 ` Yazen Ghannam 2026-09-04 2:43 ` Lin Wang 2026-09-04 5:36 ` Borislav Petkov 2026-09-04 6:26 ` Lin Wang 2026-09-01 7:17 ` [PATCH v4 5/5] x86/hygon: Add Family 0x18 SMN access Lin Wang 2026-09-01 7:28 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox