* [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation
@ 2026-04-03 10:57 Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 01/10] ras/amd/atl: Add Hygon DF1 Data Fabric system information helper Aichun Shi
` (10 more replies)
0 siblings, 11 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Hi all,
This is an RFC (Request For Comments) for the patch series that proposes
Hygon Family 0x18 (models 0x4-0x8) support for UMC ECC error address
translation, aligned with the existing AMD EDAC + AMD ATL layering in
mainline.
RFC intent
----------
The code is posted for architectural feedback before formal submission:
Hygon Family 0x18 Data Fabric behavior differs from AMD in several places,
and the boundary between shared AMD helpers and Hygon-only code is easier
to adjust now than after a formal series. Posting as RFC is intended to
confirm if the overall approach---Hygon backends under
drivers/ras/amd/atl/hygon/, registration through the existing amd_atl
hook, and matching amd64_edac changes---is acceptable or is there a better
approach.
------------------------------------------------------------------------
1. Background
------------------------------------------------------------------------
Linux RAS and EDAC report DRAM and memory-controller (UMC) correctable and
uncorrectable errors for operators and user space. On AMD x86 server
platforms, drivers/edac/amd64_edac.c owns UMC topology discovery, related
register access, and integration with the MCE/MCA UMC decode path for
Family 0x17 and later CPUs.
Converting a UMC MCA normalized address from hardware into a system
physical address (SPA) requires Data Fabric (DF) knowledge: DRAM regions,
interleave, hashing and dehash, die/socket routing, and offsets. That
logic is substantial, changes by generation, and is not EDAC-specific---any
subsystem needing MCA-to-SPA translation needs the same math.
Mainline therefore places that translation in the AMD Address Translation
Library (amd_atl, drivers/ras/amd/atl) and exposes a single RAS-facing
entry point that amd64_edac calls:
amd_convert_umc_mca_addr_to_sys_addr()
For AMD CPUs the implementation lives in amd_atl. EDAC stays responsible
for MC/UMC enumeration and error reporting; DF address translation stays in
amd_atl. That separation avoids duplicating DF algorithms inside EDAC and
keeps a single place to fix or extend translation---this is the decoupling
between AMD EDAC and AMD ATL.
Hygon parts use the same broad MCA/UMC model on Family 0x18 but differ in DF
revision, register layouts, and IPID/channel rules by CPU model. This
series adds Hygon backends under hygon/ and wires them through the same
hook, plus amd64_edac updates for the covered models, so UMC ECC reports can
resolve to a consistent SPA.
------------------------------------------------------------------------
2. Problem
------------------------------------------------------------------------
(1) Requirement: UMC ECC address translation on Hygon Family 0x18
Operating systems need a correct MCA-to-SPA path for UMC ECC errors on
Hygon Family 0x18 so logs, sysfs, and tooling reference a physical
address that matches the platform memory map.
(2) Gaps in upstream Linux today (without this RFC)
- amd_atl/amd64_edac implements the AMD translation path but not the
Hygon-specific handling required for the Family 0x18 models in this
series.
- Without that path, end-to-end reporting for these Hygon systems is
incomplete even if UMCs are probed.
- Folding address translation back into amd64_edac would duplicate amd_atl,
break the single registration model, and complicate maintenance and
testing.
------------------------------------------------------------------------
3. Solution
------------------------------------------------------------------------
3.1 Solution summary
- Extend the existing AMD EDAC + AMD ATL design: add Hygon code under
drivers/ras/amd/atl/hygon/, register the Hygon decoder during ATL
init, and extend amd64_edac for Hygon Family 0x18 models 0x4-0x8.
Other existing Hygon models and future models will be extended later.
- Keep changes to existing AMD EDAC/ATL code minimal: shared helpers
remain shared; Hygon-specific logic is isolated under hygon/*.c with
narrow integration points (topology difference, decoder selection,
node bounds).
3.2 Details: flow, diagram, code paths, Hygon vs AMD
End-to-end flow (conceptual):
MCE / UMC MCA record
-> amd64_edac UMC decode
-> amd_convert_umc_mca_addr_to_sys_addr() [registered by amd_atl]
-> Hygon: hygon_convert_umc_mca_addr_to_sys_addr()
-> hygon_norm_to_sys_addr()
-> Hygon DF1/DF2/3: system, map, denormalize, dehash
Sequence:
+-------------+ +-------------+ +------------------------+
| amd64_edac | | amd_atl | | hygon/ (this RFC) |
| decode path | ----> | hook / reg | ----> | DF rev, map, denorm, |
| | | | | dehash, UMC entry |
+-------------+ +-------------+ +------------------------+
Touched paths (summary):
- Hygon DF1 (models 0x4/0x5): hygon/system.c (DF1 info), hygon/map.c,
hygon/denormalize.c, hygon/dehash.c, hygon/core.c (normalized-to-SPA
pipeline), hygon/umc.c.
- Hygon DF2 (models 0x6/0x8): Hygon-specific four-channel hash
(HYGON_DF2_4CHAN_HASH), hygon_df2_get_dram_addr_map() with shared AMD
DF2 DRAM base/limit where applicable; denormalize/dehash and UMC IPID
channel/sub-channel handling for Hygon DF2.
- Hygon DF3 (model 0x7): DF3 detection and fields; DRAM map and
denormalize; reuse of DF2 DRAM map plumbing with Hygon DF3 interleave
behavior.
- Initialization: amd_atl init/exit and __df_indirect_read() extended
for Hygon node number; hygon_get_df_system_info(); register
hygon_convert_umc_mca_addr_to_sys_addr().
- EDAC: UMC bases, MCA IPID fields for Hygon, distinct MC counts for
models 0x4/0x5 where needed, amd64_edac_init()/amd64_edac_exit() node
handling for Hygon.
Hygon-specific deviations from AMD (high level):
- Explicit Hygon DF revisions (HYGON_DF1/DF2/DF3) and model-based
detection.
- Additional interleave modes (e.g. DF1 three-channel, DF2-class
four-channel hash) and coherent-station / fabric ID handling as
described in per-patch changelogs.
- DF2: coherent-station instance ID derived from MCA IPID channel and
sub-channel fields per Hygon rules.
3.3 Patches description
Patches 01-05 — Hygon DF1 backend stack:
These patches build the Hygon DF1 address-translation core in order:
DF system information and model detection, DRAM map decoding,
denormalization, dehash, and hygon/core.c tying the pipeline into a
single normalized-to-system path for models 0x4/0x5. They are
prerequisite to any UMC hook or init wiring.
Patches 06-07 — Hygon UMC MCA entry and ATL integration:
Add hygon/umc.c and hygon_convert_umc_mca_addr_to_sys_addr() as the
Hygon-specific entry that invokes the DF1 pipeline from MCA context.
Connects Hygon to amd_atl at init/exit: node number bounds, DF
discovery via hygon_get_df_system_info(), and registration of the Hygon
decoder with the existing amd_convert_umc_mca_addr_to_sys_addr() hook
without changing the AMD path.
Patches 08-09 — Hygon DF2 and DF3 extensions:
Layer additional Data Fabric revisions on top of the shared helpers:
DF2 for models 0x6/0x8 (four-channel hash, denormalize/dehash, UMC IPID
rules) and DF3 for model 0x7 (DF3 fields, DRAM map, denormalize).
Patch 10 — amd64_edac enablement:
Completes the end-to-end story in the EDAC driver: UMC bases, MCA IPID
channel handling, memory-controller counts, and node lifecycle for
Hygon Family 0x18 models 0x4-0x8 so probe/decode matches the ATL
backends above.
[01/10] ras/amd/atl: Add Hygon DF1 Data Fabric system information helper
hygon/reg_fields.h, hygon/system.c, HYGON_DF1,
hygon_determine_df_rev() for models 0x4/0x5.
[02/10] ras/amd/atl: Add Hygon DF1 DRAM address map decoding helper
hygon/map.c, hygon_chan_intlv, HYGON_DF1_3CHAN, shared map helpers.
[03/10] ras/amd/atl: Add Hygon DF1 normalized address denormalization helper
hygon/denormalize.c.
[04/10] ras/amd/atl: Add Hygon DF1 address dehash helper
hygon/dehash.c (hashed modes, DDR5-related cases).
[05/10] ras/amd/atl: Add Hygon DF1 normalized-to-system address translation
hygon/core.c (full pipeline).
[06/10] ras/amd/atl: Add Hygon UMC MCA to system address conversion support
hygon/umc.c, hygon_convert_umc_mca_addr_to_sys_addr().
[07/10] ras/amd/atl: Add Hygon DF discovery and MCA decode at initialization
amd_atl init/exit, node bounds, decoder registration.
[08/10] ras/amd/atl: Add Hygon DF2 address translation support
models 0x6/0x8, HYGON_DF2_4CHAN_HASH, denormalize/dehash/UMC.
[09/10] ras/amd/atl: Add Hygon DF3 address translation support
model 0x7 (Hygon DF3).
[10/10] EDAC/amd64: Add Hygon Family 0x18 models 0x4-0x8 support
amd64_edac integration for covered models.
3.4 Dependencies
This RFC patch series depends on the APIs exported by the "Hygon Node" RFC patch series [1].
hygon_f18h_m4h()
hygon_get_dfid()
[1] https://lore.kernel.org/lkml/20260402111515.1155505-1-wanglin@open-hieco.net/
3.5 Test
Each patch was build tested individually. The entire set was functionally
tested with the following systems.
Hygon Family 0x18 model 0x4
Hygon Family 0x18 model 0x6
3.6 Feedback
Maintainer and reviewer input on the points below would help refine a
subsequent formal revision:
- Layout and wiring: placement of Hygon code under hygon/, decoder
registration at amd_atl init, and interaction with amd64_edac.
- Shared helpers: reuse of selected AMD helpers with exports in
internal.h---whether this share is appropriate or should implement
the same helpers in hygon/ to separate Hygon and AMD code better?
- DF typing: names and mapping for HYGON_DF1/DF2/DF3 versus existing AMD
DF revision handling.
3.7 Future work
Broader hardware coverage, more tests, and possible helper unification
with AMD code after maintainer feedback.
Thanks for comments and review.
Aichun Shi
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
Aichun Shi (10):
ras/amd/atl: Add Hygon DF1 Data Fabric system information helper
ras/amd/atl: Add Hygon DF1 DRAM address map decoding helper
ras/amd/atl: Add Hygon DF1 normalized address denormalization helper
ras/amd/atl: Add Hygon DF1 address dehash helper
ras/amd/atl: Add Hygon DF1 normalized-to-system address translation
ras/amd/atl: Add Hygon UMC MCA to system address conversion support
ras/amd/atl: Add Hygon DF discovery and MCA decode at initialization
ras/amd/atl: Add Hygon DF2 address translation support
ras/amd/atl: Add Hygon DF3 address translation support
EDAC/amd64: Add Hygon Family 0x18 models 0x4-0x8 support
drivers/edac/amd64_edac.c | 68 ++++-
drivers/ras/amd/atl/Makefile | 7 +
drivers/ras/amd/atl/access.c | 9 +-
drivers/ras/amd/atl/core.c | 25 +-
drivers/ras/amd/atl/denormalize.c | 4 +-
drivers/ras/amd/atl/hygon/core.c | 52 ++++
drivers/ras/amd/atl/hygon/dehash.c | 100 +++++++
drivers/ras/amd/atl/hygon/denormalize.c | 181 +++++++++++
drivers/ras/amd/atl/hygon/map.c | 383 ++++++++++++++++++++++++
drivers/ras/amd/atl/hygon/reg_fields.h | 191 ++++++++++++
drivers/ras/amd/atl/hygon/system.c | 91 ++++++
drivers/ras/amd/atl/hygon/umc.c | 52 ++++
drivers/ras/amd/atl/internal.h | 38 +++
drivers/ras/amd/atl/map.c | 6 +-
drivers/ras/amd/atl/system.c | 4 +-
15 files changed, 1186 insertions(+), 25 deletions(-)
create mode 100644 drivers/ras/amd/atl/hygon/core.c
create mode 100644 drivers/ras/amd/atl/hygon/dehash.c
create mode 100644 drivers/ras/amd/atl/hygon/denormalize.c
create mode 100644 drivers/ras/amd/atl/hygon/map.c
create mode 100644 drivers/ras/amd/atl/hygon/reg_fields.h
create mode 100644 drivers/ras/amd/atl/hygon/system.c
create mode 100644 drivers/ras/amd/atl/hygon/umc.c
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 01/10] ras/amd/atl: Add Hygon DF1 Data Fabric system information helper
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 02/10] ras/amd/atl: Add Hygon DF1 DRAM address map decoding helper Aichun Shi
` (9 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Add Hygon Family 0x18 model 0x4/0x5 (Hygon DF1) support as the start.
Add Hygon DF1 Data Fabric system information that Hygon address
translation function depends on later.
Prepare hygon_get_df_system_info() to be called by amd_atl_init() later.
- Add hygon/reg_fields.h for Hygon-specific register field definitions
with Hygon DF1 System Fabric ID Mask defined first.
- Add hygon/system.c for Hygon-specific implementation similar to
system.c for AMD.
- Add HYGON_DF1 to enum df_revisions in internal.h.
- Add hygon_determine_df_rev() to detect models 0x4/0x5 as Hygon DF1 and
get system information.
- Use get_dram_hole_base() and dump_df_cfg() shared from system.c and
exported in internal.h to avoid duplicate code.
- Build hygon/system.c from the Makefile as an amd_atl object.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/Makefile | 2 +
drivers/ras/amd/atl/hygon/reg_fields.h | 60 +++++++++++++++++++++
drivers/ras/amd/atl/hygon/system.c | 73 ++++++++++++++++++++++++++
drivers/ras/amd/atl/internal.h | 7 +++
drivers/ras/amd/atl/system.c | 4 +-
5 files changed, 144 insertions(+), 2 deletions(-)
create mode 100644 drivers/ras/amd/atl/hygon/reg_fields.h
create mode 100644 drivers/ras/amd/atl/hygon/system.c
diff --git a/drivers/ras/amd/atl/Makefile b/drivers/ras/amd/atl/Makefile
index b56892c0c0d9..3716fe068eb6 100644
--- a/drivers/ras/amd/atl/Makefile
+++ b/drivers/ras/amd/atl/Makefile
@@ -15,6 +15,8 @@ amd_atl-y += map.o
amd_atl-y += system.o
amd_atl-y += umc.o
+amd_atl-y += hygon/system.o
+
amd_atl-$(CONFIG_AMD_ATL_PRM) += prm.o
obj-$(CONFIG_AMD_ATL) += amd_atl.o
diff --git a/drivers/ras/amd/atl/hygon/reg_fields.h b/drivers/ras/amd/atl/hygon/reg_fields.h
new file mode 100644
index 000000000000..2c1193639c6c
--- /dev/null
+++ b/drivers/ras/amd/atl/hygon/reg_fields.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AMD Address Translation Library
+ *
+ * reg_fields.h : Register field definitions for Hygon
+ *
+ * Author: AichunShi <shiaichun@open-hieco.net>
+ */
+
+/*
+ * Die ID Mask
+ *
+ * Access type: Broadcast
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F1x208 [System Fabric ID Mask]
+ * HYGON_DF1 DieIdMask [9:0]
+ */
+#define HYGON_DF1_DIE_ID_MASK GENMASK(9, 0)
+
+/*
+ * Die ID Shift
+ *
+ * Access type: Broadcast
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F1x208 [System Fabric ID Mask]
+ * HYGON_DF1 DieIdShift [15:12]
+ */
+#define HYGON_DF1_DIE_ID_SHIFT GENMASK(15, 12)
+
+/*
+ * Socket ID Mask
+ *
+ * Access type: Broadcast
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F1x208 [System Fabric ID Mask]
+ * HYGON_DF1 SocketIdMask [25:16]
+ */
+#define HYGON_DF1_SOCKET_ID_MASK GENMASK(25, 16)
+
+/*
+ * Socket ID Shift
+ *
+ * Access type: Broadcast
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F1x208 [System Fabric ID Mask]
+ * HYGON_DF1 SocketIdShift [31:28]
+ */
+#define HYGON_DF1_SOCKET_ID_SHIFT GENMASK(31, 28)
diff --git a/drivers/ras/amd/atl/hygon/system.c b/drivers/ras/amd/atl/hygon/system.c
new file mode 100644
index 000000000000..2a149cfc34c1
--- /dev/null
+++ b/drivers/ras/amd/atl/hygon/system.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Address Translation Library
+ *
+ * system.c : Functions to read and save system-wide data for Hygon
+ *
+ * Author: AichunShi <shiaichun@open-hieco.net>
+ */
+
+#include "../internal.h"
+
+static void hygon_df_get_masks_shifts(u32 mask0)
+{
+ switch (df_cfg.rev) {
+ case HYGON_DF1:
+ df_cfg.socket_id_shift = FIELD_GET(HYGON_DF1_SOCKET_ID_SHIFT, mask0);
+ df_cfg.socket_id_mask = FIELD_GET(HYGON_DF1_SOCKET_ID_MASK, mask0);
+ df_cfg.die_id_shift = FIELD_GET(HYGON_DF1_DIE_ID_SHIFT, mask0);
+ df_cfg.die_id_mask = FIELD_GET(HYGON_DF1_DIE_ID_MASK, mask0);
+ break;
+ default:
+ atl_debug_on_bad_df_rev();
+ return;
+ }
+}
+
+static int hygon_determine_df_rev(void)
+{
+ u32 fabric_id_mask0;
+
+ if (boot_cpu_data.x86_model == 0x4 || boot_cpu_data.x86_model == 0x5)
+ df_cfg.rev = HYGON_DF1;
+
+ /* Read D18F1x208 (SystemFabricIdMask). */
+ if (df_indirect_read_broadcast(0, 1, 0x208, &fabric_id_mask0))
+ return -EINVAL;
+
+ hygon_df_get_masks_shifts(fabric_id_mask0);
+
+ return 0;
+}
+
+static void hygon_get_num_maps(void)
+{
+ switch (df_cfg.rev) {
+ case HYGON_DF1:
+ df_cfg.num_coh_st_maps = 2;
+ break;
+ default:
+ atl_debug_on_bad_df_rev();
+ }
+}
+
+int hygon_get_df_system_info(void)
+{
+ int ret;
+
+ ret = hygon_determine_df_rev();
+ if (ret) {
+ pr_warn("Failed to determine DF Revision");
+ df_cfg.rev = UNKNOWN;
+ return ret;
+ }
+
+ hygon_get_num_maps();
+
+ if (get_dram_hole_base())
+ pr_warn("Failed to read DRAM hole base");
+
+ dump_df_cfg();
+
+ return 0;
+}
diff --git a/drivers/ras/amd/atl/internal.h b/drivers/ras/amd/atl/internal.h
index 82a56d9c2be1..19651cf9e978 100644
--- a/drivers/ras/amd/atl/internal.h
+++ b/drivers/ras/amd/atl/internal.h
@@ -21,6 +21,7 @@
#include <asm/amd/node.h>
#include "reg_fields.h"
+#include "hygon/reg_fields.h"
#undef pr_fmt
#define pr_fmt(fmt) "amd_atl: " fmt
@@ -42,6 +43,7 @@
enum df_revisions {
UNKNOWN,
+ HYGON_DF1,
DF2,
DF3,
DF3p5,
@@ -284,6 +286,11 @@ unsigned long convert_umc_mca_addr_to_sys_addr(struct atl_err *err);
u64 add_base_and_hole(struct addr_ctx *ctx, u64 addr);
u64 remove_base_and_hole(struct addr_ctx *ctx, u64 addr);
+int get_dram_hole_base(void);
+void dump_df_cfg(void);
+
+int hygon_get_df_system_info(void);
+
/* GUIDs for PRM handlers */
extern const guid_t norm_to_sys_guid;
diff --git a/drivers/ras/amd/atl/system.c b/drivers/ras/amd/atl/system.c
index 812a30e21d3a..eb420978febb 100644
--- a/drivers/ras/amd/atl/system.c
+++ b/drivers/ras/amd/atl/system.c
@@ -231,7 +231,7 @@ static int determine_df_rev(void)
return 0;
}
-static int get_dram_hole_base(void)
+int get_dram_hole_base(void)
{
u8 func = 0;
@@ -274,7 +274,7 @@ static void apply_node_id_shift(void)
df_cfg.socket_id_shift += df_cfg.node_id_shift;
}
-static void dump_df_cfg(void)
+void dump_df_cfg(void)
{
pr_debug("rev=0x%x", df_cfg.rev);
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 02/10] ras/amd/atl: Add Hygon DF1 DRAM address map decoding helper
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 01/10] ras/amd/atl: Add Hygon DF1 Data Fabric system information helper Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 03/10] ras/amd/atl: Add Hygon DF1 normalized address denormalization helper Aichun Shi
` (8 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Add Hygon DF1 DRAM address map decoding function that Hygon address
translation pipeline depends on later.
Prepare hygon_get_address_map() to be called by hygon/core.c later.
- Add struct hygon_chan_intlv in addr_ctx and HYGON_DF1_3CHAN in enum
intlv_modes to internal.h for Hygon DF1 three-channel interleave mode.
- Add hygon/map.c for Hygon-specific implementation similar to map.c for
AMD.
- Get the coherent-station fabric ID, handle normalized offsets, read
Hygon DF1 specific channel interleave state, and derive global
interleave metadata (modes, bit positions, die/socket/channel counts).
- Use df2_get_dram_addr_map(), valid_map(), and dump_address_map()
shared from map.c and exported in internal.h to avoid duplicate code.
- Build hygon/map.c from the Makefile as an amd_atl object.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/Makefile | 1 +
drivers/ras/amd/atl/hygon/map.c | 337 +++++++++++++++++++++++++
drivers/ras/amd/atl/hygon/reg_fields.h | 78 ++++++
drivers/ras/amd/atl/internal.h | 17 ++
drivers/ras/amd/atl/map.c | 6 +-
5 files changed, 436 insertions(+), 3 deletions(-)
create mode 100644 drivers/ras/amd/atl/hygon/map.c
diff --git a/drivers/ras/amd/atl/Makefile b/drivers/ras/amd/atl/Makefile
index 3716fe068eb6..105c11015f4c 100644
--- a/drivers/ras/amd/atl/Makefile
+++ b/drivers/ras/amd/atl/Makefile
@@ -15,6 +15,7 @@ amd_atl-y += map.o
amd_atl-y += system.o
amd_atl-y += umc.o
+amd_atl-y += hygon/map.o
amd_atl-y += hygon/system.o
amd_atl-$(CONFIG_AMD_ATL_PRM) += prm.o
diff --git a/drivers/ras/amd/atl/hygon/map.c b/drivers/ras/amd/atl/hygon/map.c
new file mode 100644
index 000000000000..2194c10ff587
--- /dev/null
+++ b/drivers/ras/amd/atl/hygon/map.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Address Translation Library
+ *
+ * map.c : Functions to read and decode DRAM address maps for Hygon
+ *
+ * Author: AichunShi <shiaichun@open-hieco.net>
+ */
+
+#include "../internal.h"
+
+static int hygon_df_get_intlv_mode(struct addr_ctx *ctx)
+{
+ ctx->map.intlv_mode = FIELD_GET(HYGON_DF1_INTLV_NUM_CHAN, ctx->map.base);
+
+ if (df_cfg.rev == HYGON_DF1 && ctx->map.intlv_mode == 2)
+ ctx->map.intlv_mode = HYGON_DF1_3CHAN;
+
+ if (ctx->map.intlv_mode == 8)
+ ctx->map.intlv_mode = DF2_2CHAN_HASH;
+
+ if (ctx->map.intlv_mode != NONE &&
+ ctx->map.intlv_mode != NOHASH_2CHAN &&
+ ctx->map.intlv_mode != HYGON_DF1_3CHAN &&
+ ctx->map.intlv_mode != NOHASH_4CHAN &&
+ ctx->map.intlv_mode != NOHASH_8CHAN &&
+ ctx->map.intlv_mode != NOHASH_16CHAN &&
+ ctx->map.intlv_mode != DF2_2CHAN_HASH)
+ return -EINVAL;
+
+ return 0;
+}
+
+static u64 hygon_get_hi_addr_offset(u32 reg_dram_offset)
+{
+ u8 shift = DF_DRAM_BASE_LIMIT_LSB;
+ u64 hi_addr_offset;
+
+ switch (df_cfg.rev) {
+ case HYGON_DF1:
+ hi_addr_offset = FIELD_GET(HYGON_DF1_HI_ADDR_OFFSET, reg_dram_offset);
+ break;
+ default:
+ hi_addr_offset = 0;
+ atl_debug_on_bad_df_rev();
+ }
+
+ return hi_addr_offset << shift;
+}
+
+/*
+ * Returns: 0 if offset is disabled.
+ * 1 if offset is enabled.
+ * -EINVAL on error.
+ */
+static int hygon_get_dram_offset(struct addr_ctx *ctx, u64 *norm_offset)
+{
+ u32 reg_dram_offset;
+ u8 map_num;
+
+ /* Should not be called for map 0. */
+ if (!ctx->map.num) {
+ atl_debug(ctx, "Trying to find DRAM offset for map 0");
+ return -EINVAL;
+ }
+
+ /*
+ * DramOffset registers don't exist for map 0, so the base register
+ * actually refers to map 1.
+ * Adjust the map_num for the register offsets.
+ */
+ map_num = ctx->map.num - 1;
+
+ if (df_cfg.rev >= HYGON_DF1) {
+ /* Read D18F0x214 (DramOffset) */
+ if (df_indirect_read_instance(ctx->node_id, 0, 0x214 + (4 * map_num),
+ ctx->inst_id, ®_dram_offset))
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ if (!FIELD_GET(DF_HI_ADDR_OFFSET_EN, reg_dram_offset))
+ return 0;
+
+ *norm_offset = hygon_get_hi_addr_offset(reg_dram_offset);
+
+ return 1;
+}
+
+#define HYGON_DF1_CHAN_ADDR_SEL BIT(24)
+#define HYGON_DF1_CHAN_HASH_ENABLE BIT(23)
+#define HYGON_DF1_DDR5_ENABLE BIT(19)
+static int hygon_df1_get_dram_addr_map(struct addr_ctx *ctx)
+{
+ if (df2_get_dram_addr_map(ctx))
+ return -EINVAL;
+
+ /* Read D18F2x48 */
+ if (df_indirect_read_instance(ctx->node_id, 2, 0x48,
+ ctx->inst_id, &ctx->map.intlv))
+ return -EINVAL;
+
+ ctx->chan_intlv_hygon.chan_addr_sel =
+ FIELD_GET(HYGON_DF1_CHAN_ADDR_SEL, ctx->map.intlv);
+ ctx->chan_intlv_hygon.chan_hash_enable =
+ FIELD_GET(HYGON_DF1_CHAN_HASH_ENABLE, ctx->map.intlv);
+ ctx->chan_intlv_hygon.ddr5_enable =
+ FIELD_GET(HYGON_DF1_DDR5_ENABLE, ctx->map.intlv);
+
+ if (ctx->chan_intlv_hygon.ddr5_enable) {
+ u64 low_addr, high_addr;
+
+ ctx->chan_intlv_hygon.start_bit = ctx->chan_intlv_hygon.chan_addr_sel ? 8 : 7;
+ low_addr = ctx->ret_addr & GENMASK_ULL(ctx->chan_intlv_hygon.start_bit - 1, 0);
+ high_addr = (ctx->ret_addr & GENMASK_ULL(63, ctx->chan_intlv_hygon.start_bit)) << 1;
+ ctx->ret_addr = high_addr | low_addr;
+ }
+
+ return 0;
+}
+
+static int hygon_get_dram_addr_map(struct addr_ctx *ctx)
+{
+ switch (df_cfg.rev) {
+ case HYGON_DF1: return hygon_df1_get_dram_addr_map(ctx);
+ default:
+ atl_debug_on_bad_df_rev();
+ return -EINVAL;
+ }
+}
+
+static int hygon_get_coh_st_fabric_id(struct addr_ctx *ctx)
+{
+ u32 reg;
+
+ /* Read D18F0x50 (FabricBlockInstanceInformation3). */
+ if (df_indirect_read_instance(ctx->node_id, 0, 0x50, ctx->inst_id, ®))
+ return -EINVAL;
+
+ switch (df_cfg.rev) {
+ case HYGON_DF1:
+ ctx->coh_st_fabric_id = FIELD_GET(HYGON_DF1_COH_ST_FABRIC_ID, reg);
+ break;
+ default:
+ atl_debug_on_bad_df_rev();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hygon_find_normalized_offset(struct addr_ctx *ctx, u64 *norm_offset)
+{
+ u64 last_offset = 0;
+ int ret;
+
+ for (ctx->map.num = 1; ctx->map.num < df_cfg.num_coh_st_maps; ctx->map.num++) {
+ ret = hygon_get_dram_offset(ctx, norm_offset);
+ if (ret < 0)
+ return ret;
+
+ /* Continue search if this map's offset is not enabled. */
+ if (!ret)
+ continue;
+
+ /* Enabled offsets should never be 0. */
+ if (*norm_offset == 0) {
+ atl_debug(ctx, "Enabled map %u offset is 0", ctx->map.num);
+ return -EINVAL;
+ }
+
+ /* Offsets should always increase from one map to the next. */
+ if (*norm_offset <= last_offset) {
+ atl_debug(ctx, "Map %u offset (0x%016llx) <= previous (0x%016llx)",
+ ctx->map.num, *norm_offset, last_offset);
+ return -EINVAL;
+ }
+
+ /* Match if this map's offset is less than the current calculated address. */
+ if (ctx->ret_addr >= *norm_offset)
+ break;
+
+ last_offset = *norm_offset;
+ }
+
+ /*
+ * Finished search without finding a match.
+ * Reset to map 0 and no offset.
+ */
+ if (ctx->map.num >= df_cfg.num_coh_st_maps) {
+ ctx->map.num = 0;
+ *norm_offset = 0;
+ }
+
+ return 0;
+}
+
+static int hygon_get_address_map_common(struct addr_ctx *ctx)
+{
+ u64 norm_offset = 0;
+
+ if (hygon_get_coh_st_fabric_id(ctx))
+ return -EINVAL;
+
+ if (hygon_find_normalized_offset(ctx, &norm_offset))
+ return -EINVAL;
+
+ ctx->ret_addr -= norm_offset;
+
+ if (hygon_get_dram_addr_map(ctx))
+ return -EINVAL;
+
+ if (!valid_map(ctx))
+ return -EINVAL;
+
+ return 0;
+}
+
+static u8 hygon_get_num_intlv_chan(struct addr_ctx *ctx)
+{
+ switch (ctx->map.intlv_mode) {
+ case NONE:
+ return 1;
+ case NOHASH_2CHAN:
+ return 2;
+ case HYGON_DF1_3CHAN:
+ return 3;
+ case NOHASH_4CHAN:
+ return 4;
+ case NOHASH_8CHAN:
+ return 8;
+ case NOHASH_16CHAN:
+ return 16;
+ case NOHASH_32CHAN:
+ return 32;
+ default:
+ atl_debug_on_bad_intlv_mode(ctx);
+ return 0;
+ }
+}
+
+static void hygon_calculate_intlv_bits(struct addr_ctx *ctx)
+{
+ ctx->map.num_intlv_chan = hygon_get_num_intlv_chan(ctx);
+
+ ctx->map.total_intlv_chan = ctx->map.num_intlv_chan;
+ ctx->map.total_intlv_chan *= ctx->map.num_intlv_dies;
+ ctx->map.total_intlv_chan *= ctx->map.num_intlv_sockets;
+
+ /*
+ * Get the number of bits needed to cover this many channels.
+ * order_base_2() rounds up automatically.
+ */
+ ctx->map.total_intlv_bits = order_base_2(ctx->map.total_intlv_chan);
+}
+
+static u8 hygon_get_intlv_bit_pos(struct addr_ctx *ctx)
+{
+ u8 addr_sel = 0;
+
+ switch (df_cfg.rev) {
+ case HYGON_DF1:
+ addr_sel = FIELD_GET(HYGON_DF1_INTLV_ADDR_SEL, ctx->map.base);
+ break;
+ default:
+ atl_debug_on_bad_df_rev();
+ break;
+ }
+
+ /* Add '8' to get the 'interleave bit position'. */
+ return addr_sel + 8;
+}
+
+static u8 hygon_get_num_intlv_dies(struct addr_ctx *ctx)
+{
+ u8 dies = 0;
+
+ switch (df_cfg.rev) {
+ case HYGON_DF1:
+ dies = FIELD_GET(HYGON_DF1_INTLV_NUM_DIES, ctx->map.limit);
+ break;
+ default:
+ atl_debug_on_bad_df_rev();
+ break;
+ }
+
+ /* Register value is log2, e.g. 0 -> 1 die, 1 -> 2 dies, etc. */
+ return 1 << dies;
+}
+
+static u8 hygon_get_num_intlv_sockets(struct addr_ctx *ctx)
+{
+ u8 sockets = 0;
+
+ switch (df_cfg.rev) {
+ case HYGON_DF1:
+ sockets = FIELD_GET(HYGON_DF1_INTLV_NUM_SOCKETS, ctx->map.base);
+ break;
+ default:
+ atl_debug_on_bad_df_rev();
+ break;
+ }
+
+ /* Register value is log2, e.g. 0 -> 1 sockets, 1 -> 2 sockets, etc. */
+ return 1 << sockets;
+}
+
+static int hygon_get_global_map_data(struct addr_ctx *ctx)
+{
+ if (hygon_df_get_intlv_mode(ctx))
+ return -EINVAL;
+
+ ctx->map.intlv_bit_pos = hygon_get_intlv_bit_pos(ctx);
+ ctx->map.num_intlv_dies = hygon_get_num_intlv_dies(ctx);
+ ctx->map.num_intlv_sockets = hygon_get_num_intlv_sockets(ctx);
+ hygon_calculate_intlv_bits(ctx);
+
+ return 0;
+}
+
+int hygon_get_address_map(struct addr_ctx *ctx)
+{
+ int ret;
+
+ ret = hygon_get_address_map_common(ctx);
+ if (ret)
+ return ret;
+
+ ret = hygon_get_global_map_data(ctx);
+ if (ret)
+ return ret;
+
+ dump_address_map(&ctx->map);
+
+ return ret;
+}
diff --git a/drivers/ras/amd/atl/hygon/reg_fields.h b/drivers/ras/amd/atl/hygon/reg_fields.h
index 2c1193639c6c..44b24593175d 100644
--- a/drivers/ras/amd/atl/hygon/reg_fields.h
+++ b/drivers/ras/amd/atl/hygon/reg_fields.h
@@ -7,6 +7,84 @@
* Author: AichunShi <shiaichun@open-hieco.net>
*/
+/*
+ * Coherent Station Fabric ID
+ *
+ * Access type: Instance
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F0x50 [Fabric Block Instance Information 3]
+ * HYGON_DF1 BlockFabricId [17:8]
+ */
+#define HYGON_DF1_COH_ST_FABRIC_ID GENMASK(17, 8)
+
+/*
+ * Interleave Number of Sockets
+ *
+ * Access type: Instance
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F0x110 [DRAM Base Address]
+ * HYGON_DF1 IntLvNumSockets [3:2]
+ */
+#define HYGON_DF1_INTLV_NUM_SOCKETS GENMASK(3, 2)
+
+/*
+ * Interleave Number of Channels
+ *
+ * Access type: Instance
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F0x110 [DRAM Base Address]
+ * HYGON_DF1 IntLvNumChan [7:4]
+ */
+#define HYGON_DF1_INTLV_NUM_CHAN GENMASK(7, 4)
+
+/*
+ * Interleave Address Select
+ *
+ * Access type: Instance
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F0x110 [DRAM Base Address]
+ * HYGON_DF1 IntLvAddrSel [10:8]
+ */
+#define HYGON_DF1_INTLV_ADDR_SEL GENMASK(10, 8)
+
+/*
+ * Interleave Number of Dies
+ *
+ * Access type: Instance
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F0x114 [DRAM Limit Address]
+ * HYGON_DF1 IntLvNumDies [11:10]
+ */
+#define HYGON_DF1_INTLV_NUM_DIES GENMASK(11, 10)
+
+/*
+ * High Address Offset
+ *
+ * Access type: Instance
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F0x214 [DRAM Offset]
+ * HYGON_DF1 HiAddrOffset [31:19]
+ */
+#define HYGON_DF1_HI_ADDR_OFFSET GENMASK(31, 19)
+
/*
* Die ID Mask
*
diff --git a/drivers/ras/amd/atl/internal.h b/drivers/ras/amd/atl/internal.h
index 19651cf9e978..d149ec6e7da9 100644
--- a/drivers/ras/amd/atl/internal.h
+++ b/drivers/ras/amd/atl/internal.h
@@ -55,6 +55,7 @@ enum df_revisions {
enum intlv_modes {
NONE = 0x00,
NOHASH_2CHAN = 0x01,
+ HYGON_DF1_3CHAN = 0x02,
NOHASH_4CHAN = 0x03,
NOHASH_8CHAN = 0x05,
DF3_6CHAN = 0x06,
@@ -246,6 +247,14 @@ struct addr_ctx_inputs {
u8 coh_st_inst_id;
};
+struct hygon_chan_intlv {
+ u8 sub_channel;
+ u8 chan_addr_sel;
+ u8 chan_hash_enable;
+ u8 ddr5_enable;
+ u8 start_bit;
+};
+
struct addr_ctx {
u64 ret_addr;
@@ -266,6 +275,9 @@ struct addr_ctx {
* System-wide ID that includes 'node' bits.
*/
u16 coh_st_fabric_id;
+
+ /* Hygon Channel Interleave */
+ struct hygon_chan_intlv chan_intlv_hygon;
};
int df_indirect_read_instance(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo);
@@ -289,7 +301,12 @@ u64 remove_base_and_hole(struct addr_ctx *ctx, u64 addr);
int get_dram_hole_base(void);
void dump_df_cfg(void);
+int df2_get_dram_addr_map(struct addr_ctx *ctx);
+bool valid_map(struct addr_ctx *ctx);
+void dump_address_map(struct dram_addr_map *map);
+
int hygon_get_df_system_info(void);
+int hygon_get_address_map(struct addr_ctx *ctx);
/* GUIDs for PRM handlers */
extern const guid_t norm_to_sys_guid;
diff --git a/drivers/ras/amd/atl/map.c b/drivers/ras/amd/atl/map.c
index 24a05af747d5..acfd0a636160 100644
--- a/drivers/ras/amd/atl/map.c
+++ b/drivers/ras/amd/atl/map.c
@@ -209,7 +209,7 @@ static int df3_6ch_get_dram_addr_map(struct addr_ctx *ctx)
return 0;
}
-static int df2_get_dram_addr_map(struct addr_ctx *ctx)
+int df2_get_dram_addr_map(struct addr_ctx *ctx)
{
/* Read D18F0x110 (DramBaseAddress). */
if (df_indirect_read_instance(ctx->node_id, 0, 0x110 + (8 * ctx->map.num),
@@ -441,7 +441,7 @@ static int find_normalized_offset(struct addr_ctx *ctx, u64 *norm_offset)
return 0;
}
-static bool valid_map(struct addr_ctx *ctx)
+bool valid_map(struct addr_ctx *ctx)
{
if (df_cfg.rev >= DF4)
return FIELD_GET(DF_ADDR_RANGE_VAL, ctx->map.ctl);
@@ -735,7 +735,7 @@ static int validate_address_map(struct addr_ctx *ctx)
return -EINVAL;
}
-static void dump_address_map(struct dram_addr_map *map)
+void dump_address_map(struct dram_addr_map *map)
{
u8 i;
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 03/10] ras/amd/atl: Add Hygon DF1 normalized address denormalization helper
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 01/10] ras/amd/atl: Add Hygon DF1 Data Fabric system information helper Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 02/10] ras/amd/atl: Add Hygon DF1 DRAM address map decoding helper Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 04/10] ras/amd/atl: Add Hygon DF1 address dehash helper Aichun Shi
` (7 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Add Hygon DF1 normalized address denormalization function that Hygon
address translation pipeline depends on later.
Prepare hygon_denormalize_address() to be called by hygon/core.c later.
- Add hygon/denormalize.c for Hygon-specific implementation similar to
denormalize.c for AMD.
- Add hygon_denormalize_address() to handle non-hashed and hashed
interleave modes, including the Hygon DF1 three-channel interleave mode.
- Use make_space_for_coh_st_id_at_intlv_bit() and
insert_coh_st_id_at_intlv_bit() shared from denormalize.c and exported
in internal.h to avoid duplicate code.
- Build hygon/denormalize.c from the Makefile as an amd_atl object.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/Makefile | 1 +
drivers/ras/amd/atl/denormalize.c | 4 +-
drivers/ras/amd/atl/hygon/denormalize.c | 176 ++++++++++++++++++++++++
drivers/ras/amd/atl/hygon/reg_fields.h | 13 ++
drivers/ras/amd/atl/internal.h | 4 +
5 files changed, 196 insertions(+), 2 deletions(-)
create mode 100644 drivers/ras/amd/atl/hygon/denormalize.c
diff --git a/drivers/ras/amd/atl/Makefile b/drivers/ras/amd/atl/Makefile
index 105c11015f4c..c4e01f3ecc7b 100644
--- a/drivers/ras/amd/atl/Makefile
+++ b/drivers/ras/amd/atl/Makefile
@@ -15,6 +15,7 @@ amd_atl-y += map.o
amd_atl-y += system.o
amd_atl-y += umc.o
+amd_atl-y += hygon/denormalize.o
amd_atl-y += hygon/map.o
amd_atl-y += hygon/system.o
diff --git a/drivers/ras/amd/atl/denormalize.c b/drivers/ras/amd/atl/denormalize.c
index d45ce753e636..cf58cfa7ab3a 100644
--- a/drivers/ras/amd/atl/denormalize.c
+++ b/drivers/ras/amd/atl/denormalize.c
@@ -41,7 +41,7 @@ static u16 get_dst_fabric_id(struct addr_ctx *ctx)
* expanded address bits: [20+n : n+p][n+p-1 : p][p-1 : 0]
* [23 : 11][10 : 8][7 : 0]
*/
-static u64 make_space_for_coh_st_id_at_intlv_bit(struct addr_ctx *ctx)
+u64 make_space_for_coh_st_id_at_intlv_bit(struct addr_ctx *ctx)
{
return expand_bits(ctx->map.intlv_bit_pos,
ctx->map.total_intlv_bits,
@@ -320,7 +320,7 @@ static u16 calculate_coh_st_id(struct addr_ctx *ctx)
}
}
-static u64 insert_coh_st_id_at_intlv_bit(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
+u64 insert_coh_st_id_at_intlv_bit(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
{
return denorm_addr | (coh_st_id << ctx->map.intlv_bit_pos);
}
diff --git a/drivers/ras/amd/atl/hygon/denormalize.c b/drivers/ras/amd/atl/hygon/denormalize.c
new file mode 100644
index 000000000000..d189799bd183
--- /dev/null
+++ b/drivers/ras/amd/atl/hygon/denormalize.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Address Translation Library
+ *
+ * denormalize.c : Functions to account for interleaving bits for Hygon
+ *
+ * Author: AichunShi <shiaichun@open-hieco.net>
+ */
+
+#include "../internal.h"
+
+static u16 hygon_get_dst_fabric_id(struct addr_ctx *ctx)
+{
+ switch (df_cfg.rev) {
+ case HYGON_DF1:
+ return FIELD_GET(HYGON_DF1_DST_FABRIC_ID, ctx->map.limit);
+ default:
+ atl_debug_on_bad_df_rev();
+ return 0;
+ }
+}
+
+static u64 hygon_make_space_for_coh_st_id_df1_3chan(struct addr_ctx *ctx)
+{
+ return ctx->ret_addr & GENMASK_ULL(ctx->map.intlv_bit_pos - 1, 0);
+}
+
+static u64 hygon_make_space_for_coh_st_id(struct addr_ctx *ctx)
+{
+ switch (ctx->map.intlv_mode) {
+ case NOHASH_2CHAN:
+ case NOHASH_4CHAN:
+ case NOHASH_8CHAN:
+ case NOHASH_16CHAN:
+ case NOHASH_32CHAN:
+ case DF2_2CHAN_HASH:
+ return make_space_for_coh_st_id_at_intlv_bit(ctx);
+
+ case HYGON_DF1_3CHAN:
+ return hygon_make_space_for_coh_st_id_df1_3chan(ctx);
+
+ default:
+ atl_debug_on_bad_intlv_mode(ctx);
+ return ~0ULL;
+ }
+}
+
+static u16 hygon_get_coh_st_id_df(struct addr_ctx *ctx)
+{
+ u8 num_socket_intlv_bits = ilog2(ctx->map.num_intlv_sockets);
+ u8 num_die_intlv_bits = ilog2(ctx->map.num_intlv_dies);
+ u16 coh_st_id = ctx->coh_st_fabric_id;
+ u16 mask;
+ u8 intlv_bit_pos = ctx->map.intlv_bit_pos;
+ u8 num_intlv_bits = order_base_2(ctx->map.num_intlv_chan);
+
+ if (ctx->map.intlv_mode == HYGON_DF1_3CHAN) {
+ u8 cs_offset;
+ u16 dst_fabric_id = hygon_get_dst_fabric_id(ctx);
+ u64 temp_addr_x;
+
+ cs_offset = (ctx->coh_st_fabric_id & 0x3) - (dst_fabric_id & 0x3);
+ if (cs_offset > 3) {
+ atl_debug(ctx,
+ "Invalid cs_offset: 0x%x coh_st_fabric_id: 0x%x dst_fabric_id: 0x%x.\n",
+ cs_offset, ctx->coh_st_fabric_id, dst_fabric_id);
+ return ~0;
+ }
+
+ temp_addr_x = (ctx->ret_addr & GENMASK_ULL(63, intlv_bit_pos)) >>
+ intlv_bit_pos;
+ temp_addr_x = temp_addr_x * 3 + cs_offset;
+ ctx->ret_addr = temp_addr_x;
+
+ coh_st_id = temp_addr_x & GENMASK_ULL(num_intlv_bits - 1, 0);
+ } else {
+ mask = GENMASK(num_intlv_bits - 1, 0);
+ coh_st_id &= mask;
+ }
+
+ /* Die interleave bits */
+ if (num_die_intlv_bits) {
+ u16 die_bits;
+
+ mask = GENMASK(num_die_intlv_bits - 1, 0);
+ die_bits = ctx->coh_st_fabric_id & df_cfg.die_id_mask;
+ die_bits >>= df_cfg.die_id_shift;
+ die_bits -= 4;
+
+ coh_st_id |= (die_bits & mask) << num_intlv_bits;
+ num_intlv_bits += num_die_intlv_bits;
+ }
+
+ /* Socket interleave bits */
+ if (num_socket_intlv_bits) {
+ u16 socket_bits;
+
+ mask = GENMASK(num_socket_intlv_bits - 1, 0);
+ socket_bits = ctx->coh_st_fabric_id & df_cfg.socket_id_mask;
+ socket_bits >>= df_cfg.socket_id_shift;
+
+ coh_st_id |= (socket_bits & mask) << num_intlv_bits;
+ }
+
+ ctx->coh_st_fabric_id = coh_st_id;
+
+ return coh_st_id;
+}
+
+static u16 hygon_calculate_coh_st_id(struct addr_ctx *ctx)
+{
+ switch (ctx->map.intlv_mode) {
+ case NOHASH_2CHAN:
+ case HYGON_DF1_3CHAN:
+ case NOHASH_4CHAN:
+ case NOHASH_8CHAN:
+ case NOHASH_16CHAN:
+ case NOHASH_32CHAN:
+ case DF2_2CHAN_HASH:
+ return hygon_get_coh_st_id_df(ctx);
+
+ default:
+ atl_debug_on_bad_intlv_mode(ctx);
+ return ~0;
+ }
+}
+
+static u64 hygon_insert_coh_st_id_df1_3chan(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
+{
+ u8 num_intlv_bits_chan = order_base_2(ctx->map.num_intlv_chan);
+ u64 temp_addr = ((ctx->ret_addr >> num_intlv_bits_chan) << num_intlv_bits_chan) | coh_st_id;
+
+ return denorm_addr | (temp_addr << ctx->map.intlv_bit_pos);
+}
+
+static u64 hygon_insert_coh_st_id(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
+{
+ switch (ctx->map.intlv_mode) {
+ case NOHASH_2CHAN:
+ case NOHASH_4CHAN:
+ case NOHASH_8CHAN:
+ case NOHASH_16CHAN:
+ case NOHASH_32CHAN:
+ case DF2_2CHAN_HASH:
+ return insert_coh_st_id_at_intlv_bit(ctx, denorm_addr, coh_st_id);
+
+ case HYGON_DF1_3CHAN:
+ return hygon_insert_coh_st_id_df1_3chan(ctx, denorm_addr, coh_st_id);
+
+ default:
+ atl_debug_on_bad_intlv_mode(ctx);
+ return ~0ULL;
+ }
+}
+
+static int hygon_denorm_addr_common(struct addr_ctx *ctx)
+{
+ u64 denorm_addr;
+ u16 coh_st_id;
+
+ denorm_addr = hygon_make_space_for_coh_st_id(ctx);
+ coh_st_id = hygon_calculate_coh_st_id(ctx);
+ ctx->ret_addr = hygon_insert_coh_st_id(ctx, denorm_addr, coh_st_id);
+ return 0;
+}
+
+int hygon_denormalize_address(struct addr_ctx *ctx)
+{
+ switch (ctx->map.intlv_mode) {
+ case NONE:
+ return 0;
+
+ default:
+ return hygon_denorm_addr_common(ctx);
+ }
+}
diff --git a/drivers/ras/amd/atl/hygon/reg_fields.h b/drivers/ras/amd/atl/hygon/reg_fields.h
index 44b24593175d..69be2d5d2240 100644
--- a/drivers/ras/amd/atl/hygon/reg_fields.h
+++ b/drivers/ras/amd/atl/hygon/reg_fields.h
@@ -59,6 +59,19 @@
*/
#define HYGON_DF1_INTLV_ADDR_SEL GENMASK(10, 8)
+/*
+ * Destination Fabric ID
+ *
+ * Access type: Instance
+ *
+ * Register
+ * Rev Fieldname Bits
+ *
+ * D18F0x114 [DRAM Limit Address]
+ * HYGON_DF1 DstFabricID [9:0]
+ */
+#define HYGON_DF1_DST_FABRIC_ID GENMASK(9, 0)
+
/*
* Interleave Number of Dies
*
diff --git a/drivers/ras/amd/atl/internal.h b/drivers/ras/amd/atl/internal.h
index d149ec6e7da9..8f9fe6710c1e 100644
--- a/drivers/ras/amd/atl/internal.h
+++ b/drivers/ras/amd/atl/internal.h
@@ -305,8 +305,12 @@ int df2_get_dram_addr_map(struct addr_ctx *ctx);
bool valid_map(struct addr_ctx *ctx);
void dump_address_map(struct dram_addr_map *map);
+u64 make_space_for_coh_st_id_at_intlv_bit(struct addr_ctx *ctx);
+u64 insert_coh_st_id_at_intlv_bit(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id);
+
int hygon_get_df_system_info(void);
int hygon_get_address_map(struct addr_ctx *ctx);
+int hygon_denormalize_address(struct addr_ctx *ctx);
/* GUIDs for PRM handlers */
extern const guid_t norm_to_sys_guid;
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 04/10] ras/amd/atl: Add Hygon DF1 address dehash helper
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
` (2 preceding siblings ...)
2026-04-03 10:57 ` [RFC PATCH 03/10] ras/amd/atl: Add Hygon DF1 normalized address denormalization helper Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 05/10] ras/amd/atl: Add Hygon DF1 normalized-to-system address translation Aichun Shi
` (6 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Add Hygon DF1 address dehash function that Hygon address translation
pipeline depends on later.
Prepare hygon_dehash_address() to be called by hygon/core.c later.
- Add hygon/dehash.c for Hygon-specific implementation similar to dehash.c
for AMD.
- Add hygon_dehash_address() to do address dehashing where the DRAM map
uses hashed interleave modes for Hygon DF1 including two-channel hash
interleave mode and DDR5 case.
- Build hygon/dehash.c from the Makefile as an amd_atl object.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/Makefile | 1 +
drivers/ras/amd/atl/hygon/dehash.c | 77 ++++++++++++++++++++++++++++++
drivers/ras/amd/atl/internal.h | 1 +
3 files changed, 79 insertions(+)
create mode 100644 drivers/ras/amd/atl/hygon/dehash.c
diff --git a/drivers/ras/amd/atl/Makefile b/drivers/ras/amd/atl/Makefile
index c4e01f3ecc7b..fc12013de21f 100644
--- a/drivers/ras/amd/atl/Makefile
+++ b/drivers/ras/amd/atl/Makefile
@@ -15,6 +15,7 @@ amd_atl-y += map.o
amd_atl-y += system.o
amd_atl-y += umc.o
+amd_atl-y += hygon/dehash.o
amd_atl-y += hygon/denormalize.o
amd_atl-y += hygon/map.o
amd_atl-y += hygon/system.o
diff --git a/drivers/ras/amd/atl/hygon/dehash.c b/drivers/ras/amd/atl/hygon/dehash.c
new file mode 100644
index 000000000000..43f1bdea1eeb
--- /dev/null
+++ b/drivers/ras/amd/atl/hygon/dehash.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Address Translation Library
+ *
+ * dehash.c : Functions to account for hashing bits for Hygon
+ *
+ * Author: AichunShi <shiaichun@open-hieco.net>
+ */
+
+#include "../internal.h"
+
+static int hygon_df1_ddr5_dehash_addr(struct addr_ctx *ctx)
+{
+ u8 chan_hash_enable = ctx->chan_intlv_hygon.chan_hash_enable;
+ u8 sub_channel = ctx->chan_intlv_hygon.sub_channel;
+ u8 start_bit = ctx->chan_intlv_hygon.start_bit;
+ u8 hashed_bit;
+
+ if (chan_hash_enable) {
+ hashed_bit = (ctx->ret_addr >> 12) ^
+ (ctx->ret_addr >> 21) ^
+ (ctx->ret_addr >> 30) ^
+ sub_channel;
+ hashed_bit &= BIT(0);
+ ctx->ret_addr |= hashed_bit << start_bit;
+ } else {
+ ctx->ret_addr |= sub_channel << start_bit;
+ }
+
+ return 0;
+}
+
+static int hygon_df_2chan_dehash_addr(struct addr_ctx *ctx)
+{
+ u8 hashed_bit;
+ u8 intlv_bit_pos = ctx->map.intlv_bit_pos;
+
+ hashed_bit = (ctx->ret_addr >> 12) ^
+ (ctx->ret_addr >> 18) ^
+ (ctx->ret_addr >> 21) ^
+ (ctx->ret_addr >> 30) ^
+ ctx->coh_st_fabric_id;
+
+ hashed_bit &= BIT(0);
+ if (hashed_bit != ((ctx->ret_addr >> intlv_bit_pos) & BIT(0)))
+ ctx->ret_addr ^= BIT(intlv_bit_pos);
+
+ return 0;
+}
+
+int hygon_dehash_address(struct addr_ctx *ctx)
+{
+ switch (ctx->map.intlv_mode) {
+ /* No hashing cases. */
+ case NONE:
+ case NOHASH_2CHAN:
+ case HYGON_DF1_3CHAN:
+ case NOHASH_4CHAN:
+ case NOHASH_8CHAN:
+ case NOHASH_16CHAN:
+ case NOHASH_32CHAN:
+ break;
+
+ case DF2_2CHAN_HASH:
+ hygon_df_2chan_dehash_addr(ctx);
+ break;
+
+ default:
+ atl_debug_on_bad_intlv_mode(ctx);
+ return -EINVAL;
+ }
+
+ if (df_cfg.rev == HYGON_DF1 && ctx->chan_intlv_hygon.ddr5_enable)
+ hygon_df1_ddr5_dehash_addr(ctx);
+
+ return 0;
+}
diff --git a/drivers/ras/amd/atl/internal.h b/drivers/ras/amd/atl/internal.h
index 8f9fe6710c1e..a5624599c91e 100644
--- a/drivers/ras/amd/atl/internal.h
+++ b/drivers/ras/amd/atl/internal.h
@@ -311,6 +311,7 @@ u64 insert_coh_st_id_at_intlv_bit(struct addr_ctx *ctx, u64 denorm_addr, u16 coh
int hygon_get_df_system_info(void);
int hygon_get_address_map(struct addr_ctx *ctx);
int hygon_denormalize_address(struct addr_ctx *ctx);
+int hygon_dehash_address(struct addr_ctx *ctx);
/* GUIDs for PRM handlers */
extern const guid_t norm_to_sys_guid;
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 05/10] ras/amd/atl: Add Hygon DF1 normalized-to-system address translation
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
` (3 preceding siblings ...)
2026-04-03 10:57 ` [RFC PATCH 04/10] ras/amd/atl: Add Hygon DF1 address dehash helper Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 06/10] ras/amd/atl: Add Hygon UMC MCA to system address conversion support Aichun Shi
` (5 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Translate a normalized UMC MCA address into a system physical address for
Hygon DF1 with the following pipeline.
Pipeline:
- Initialize address context with socket, die, coherent-station instance,
sub-channel, and normalized address.
- Enforce legacy hole requirements when the map enables the hole without
a configured hole base.
- Run Hygon DRAM map decoding, denormalization, DRAM base plus
legacy hole, dehash, and DRAM limit checks.
Prepare hygon_norm_to_sys_addr() to be called by hygon/umc.c later.
- Add hygon/core.c for Hygon-specific implementation similar to core.c for
AMD.
- Call hygon_get_address_map(), hygon_denormalize_address(),
hygon_dehash_address() prepared previously in hygon/map.c,
hygon/denormalize.c and hygon/dehash.c.
- Use legacy hole_en() and addr_over_limit() shared from core.c and
exported in internal.h to avoid duplicate code.
- Build hygon/core.c from the Makefile as an amd_atl object.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/Makefile | 1 +
drivers/ras/amd/atl/core.c | 4 +--
drivers/ras/amd/atl/hygon/core.c | 52 ++++++++++++++++++++++++++++++++
drivers/ras/amd/atl/internal.h | 6 ++++
4 files changed, 61 insertions(+), 2 deletions(-)
create mode 100644 drivers/ras/amd/atl/hygon/core.c
diff --git a/drivers/ras/amd/atl/Makefile b/drivers/ras/amd/atl/Makefile
index fc12013de21f..0a42f2cbabc4 100644
--- a/drivers/ras/amd/atl/Makefile
+++ b/drivers/ras/amd/atl/Makefile
@@ -15,6 +15,7 @@ amd_atl-y += map.o
amd_atl-y += system.o
amd_atl-y += umc.o
+amd_atl-y += hygon/core.o
amd_atl-y += hygon/dehash.o
amd_atl-y += hygon/denormalize.o
amd_atl-y += hygon/map.o
diff --git a/drivers/ras/amd/atl/core.c b/drivers/ras/amd/atl/core.c
index 0f7cd6dab0b0..b7083588b46e 100644
--- a/drivers/ras/amd/atl/core.c
+++ b/drivers/ras/amd/atl/core.c
@@ -17,7 +17,7 @@
struct df_config df_cfg __read_mostly;
-static int addr_over_limit(struct addr_ctx *ctx)
+int addr_over_limit(struct addr_ctx *ctx)
{
u64 dram_limit_addr;
@@ -39,7 +39,7 @@ static int addr_over_limit(struct addr_ctx *ctx)
return 0;
}
-static bool legacy_hole_en(struct addr_ctx *ctx)
+bool legacy_hole_en(struct addr_ctx *ctx)
{
u32 reg = ctx->map.base;
diff --git a/drivers/ras/amd/atl/hygon/core.c b/drivers/ras/amd/atl/hygon/core.c
new file mode 100644
index 000000000000..ef064e61805b
--- /dev/null
+++ b/drivers/ras/amd/atl/hygon/core.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Address Translation Library
+ *
+ * core.c : Base translation functions for Hygon
+ *
+ * Author: AichunShi <shiaichun@open-hieco.net>
+ */
+
+#include "../internal.h"
+
+unsigned long hygon_norm_to_sys_addr(u8 socket_id, u8 die_id, u8 coh_st_inst_id,
+ u8 sub_channel, unsigned long addr)
+{
+ struct addr_ctx ctx;
+
+ if (df_cfg.rev == UNKNOWN)
+ return -EINVAL;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ /* Start from the normalized address */
+ ctx.ret_addr = addr;
+ ctx.node_id = die_id;
+ ctx.inst_id = coh_st_inst_id;
+
+ ctx.inputs.norm_addr = addr;
+ ctx.inputs.socket_id = socket_id;
+ ctx.inputs.die_id = die_id;
+ ctx.inputs.coh_st_inst_id = coh_st_inst_id;
+
+ ctx.chan_intlv_hygon.sub_channel = sub_channel;
+
+ if (legacy_hole_en(&ctx) && !df_cfg.dram_hole_base)
+ return -EINVAL;
+
+ if (hygon_get_address_map(&ctx))
+ return -EINVAL;
+
+ if (hygon_denormalize_address(&ctx))
+ return -EINVAL;
+
+ ctx.ret_addr = add_base_and_hole(&ctx, ctx.ret_addr);
+
+ if (hygon_dehash_address(&ctx))
+ return -EINVAL;
+
+ if (addr_over_limit(&ctx))
+ return -EINVAL;
+
+ return ctx.ret_addr;
+}
diff --git a/drivers/ras/amd/atl/internal.h b/drivers/ras/amd/atl/internal.h
index a5624599c91e..f943cc33f82e 100644
--- a/drivers/ras/amd/atl/internal.h
+++ b/drivers/ras/amd/atl/internal.h
@@ -297,6 +297,8 @@ unsigned long convert_umc_mca_addr_to_sys_addr(struct atl_err *err);
u64 add_base_and_hole(struct addr_ctx *ctx, u64 addr);
u64 remove_base_and_hole(struct addr_ctx *ctx, u64 addr);
+bool legacy_hole_en(struct addr_ctx *ctx);
+int addr_over_limit(struct addr_ctx *ctx);
int get_dram_hole_base(void);
void dump_df_cfg(void);
@@ -313,6 +315,10 @@ int hygon_get_address_map(struct addr_ctx *ctx);
int hygon_denormalize_address(struct addr_ctx *ctx);
int hygon_dehash_address(struct addr_ctx *ctx);
+unsigned long hygon_norm_to_sys_addr(u8 socket_id, u8 die_id, u8 coh_st_inst_id,
+ u8 sub_channel, unsigned long addr);
+unsigned long hygon_convert_umc_mca_addr_to_sys_addr(struct atl_err *err);
+
/* GUIDs for PRM handlers */
extern const guid_t norm_to_sys_guid;
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 06/10] ras/amd/atl: Add Hygon UMC MCA to system address conversion support
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
` (4 preceding siblings ...)
2026-04-03 10:57 ` [RFC PATCH 05/10] ras/amd/atl: Add Hygon DF1 normalized-to-system address translation Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 07/10] ras/amd/atl: Add Hygon DF discovery and MCA decode at initialization Aichun Shi
` (4 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Prepare hygon_convert_umc_mca_addr_to_sys_addr() to be registered by
amd_atl_register_decoder() in amd_atl_init() later.
- Add hygon/umc.c for Hygon-specific implementation similar to umc.c for
AMD.
- Add hygon_convert_umc_mca_addr_to_sys_addr() to call
hygon_norm_to_sys_addr() to do the address translation process.
- Build hygon/umc.c from the Makefile as an amd_atl object.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/Makefile | 1 +
drivers/ras/amd/atl/hygon/umc.c | 46 +++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
create mode 100644 drivers/ras/amd/atl/hygon/umc.c
diff --git a/drivers/ras/amd/atl/Makefile b/drivers/ras/amd/atl/Makefile
index 0a42f2cbabc4..9d65e215b87a 100644
--- a/drivers/ras/amd/atl/Makefile
+++ b/drivers/ras/amd/atl/Makefile
@@ -20,6 +20,7 @@ amd_atl-y += hygon/dehash.o
amd_atl-y += hygon/denormalize.o
amd_atl-y += hygon/map.o
amd_atl-y += hygon/system.o
+amd_atl-y += hygon/umc.o
amd_atl-$(CONFIG_AMD_ATL_PRM) += prm.o
diff --git a/drivers/ras/amd/atl/hygon/umc.c b/drivers/ras/amd/atl/hygon/umc.c
new file mode 100644
index 000000000000..e2339db3f575
--- /dev/null
+++ b/drivers/ras/amd/atl/hygon/umc.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD Address Translation Library
+ *
+ * umc.c : Unified Memory Controller (UMC) topology helpers for Hygon
+ *
+ * Author: AichunShi <shiaichun@open-hieco.net>
+ */
+
+#include "../internal.h"
+
+static u8 hygon_get_die_id(struct atl_err *err)
+{
+ /*
+ * For Hygon CPUs, amd_node_id can be non-contiguous, so get the Die
+ * ID from the logical Die ID.
+ */
+ return topology_logical_die_id(err->cpu);
+}
+
+#define HYGON_IPID_SUB_CHANNEL_MASK BIT(13)
+static u8 hygon_get_ipid_sub_channel(struct atl_err *err)
+{
+ return FIELD_GET(HYGON_IPID_SUB_CHANNEL_MASK, err->ipid);
+}
+
+#define HYGON_UMC_CHANNEL_NUM GENMASK(23, 20)
+static u8 hygon_get_coh_st_inst_id(struct atl_err *err)
+{
+ return FIELD_GET(HYGON_UMC_CHANNEL_NUM, err->ipid);
+}
+
+unsigned long hygon_convert_umc_mca_addr_to_sys_addr(struct atl_err *err)
+{
+ u8 socket_id = topology_physical_package_id(err->cpu);
+ u8 coh_st_inst_id = hygon_get_coh_st_inst_id(err);
+ u8 die_id = hygon_get_die_id(err);
+
+ pr_debug("socket_id=0x%x die_id=0x%x coh_st_inst_id=0x%x addr=0x%016llx",
+ socket_id, die_id, coh_st_inst_id, err->addr);
+
+ u8 sub_channel = hygon_get_ipid_sub_channel(err);
+
+ return hygon_norm_to_sys_addr(socket_id, die_id, coh_st_inst_id,
+ sub_channel, err->addr);
+}
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 07/10] ras/amd/atl: Add Hygon DF discovery and MCA decode at initialization
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
` (5 preceding siblings ...)
2026-04-03 10:57 ` [RFC PATCH 06/10] ras/amd/atl: Add Hygon UMC MCA to system address conversion support Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 08/10] ras/amd/atl: Add Hygon DF2 address translation support Aichun Shi
` (3 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Add node number bounds, Data Fabric discovery and UMC MCA to system
address decoder for Hygon while keeping the AMD path unchanged.
Address translation function is exported by
amd_convert_umc_mca_addr_to_sys_addr() and used by users such as EDAC.
- Add Hygon node number support in amd_atl_init(),
amd_atl_exit() and __df_indirect_read().
- Call hygon_get_df_system_info() to get Hygon-specific Data Fabric
system information.
- Register hygon_convert_umc_mca_addr_to_sys_addr() as the address
translation decoder for Hygon.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/access.c | 9 ++++++++-
drivers/ras/amd/atl/core.c | 21 ++++++++++++++++++---
2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/drivers/ras/amd/atl/access.c b/drivers/ras/amd/atl/access.c
index c2334f8f9add..6d75bcb930d4 100644
--- a/drivers/ras/amd/atl/access.c
+++ b/drivers/ras/amd/atl/access.c
@@ -68,9 +68,16 @@ static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *l
struct pci_dev *F4;
int err = -ENODEV;
u32 ficaa = 0;
+ u16 num_nodes;
+ bool is_hygon = boot_cpu_data.x86_vendor == X86_VENDOR_HYGON;
node = get_accessible_node(node);
- if (node >= amd_nb_num()) {
+ if (is_hygon)
+ num_nodes = amd_num_nodes();
+ else
+ num_nodes = amd_nb_num();
+
+ if (node >= num_nodes) {
pr_debug("Node %u is out of bounds\n", node);
goto out;
}
diff --git a/drivers/ras/amd/atl/core.c b/drivers/ras/amd/atl/core.c
index b7083588b46e..bbbbff4d43a3 100644
--- a/drivers/ras/amd/atl/core.c
+++ b/drivers/ras/amd/atl/core.c
@@ -195,22 +195,37 @@ MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
static int __init amd_atl_init(void)
{
int ret;
+ u16 num_nodes;
+ bool is_hygon = boot_cpu_data.x86_vendor == X86_VENDOR_HYGON;
if (!x86_match_cpu(amd_atl_cpuids))
return -ENODEV;
- if (!amd_nb_num())
+ if (is_hygon)
+ num_nodes = amd_num_nodes();
+ else
+ num_nodes = amd_nb_num();
+
+ if (!num_nodes)
return -ENODEV;
check_for_legacy_df_access();
- ret = get_df_system_info();
+ if (is_hygon)
+ ret = hygon_get_df_system_info();
+ else
+ ret = get_df_system_info();
+
if (ret)
return ret;
/* Increment this module's recount so that it can't be easily unloaded. */
__module_get(THIS_MODULE);
- amd_atl_register_decoder(convert_umc_mca_addr_to_sys_addr);
+
+ if (is_hygon)
+ amd_atl_register_decoder(hygon_convert_umc_mca_addr_to_sys_addr);
+ else
+ amd_atl_register_decoder(convert_umc_mca_addr_to_sys_addr);
pr_info("AMD Address Translation Library initialized\n");
return 0;
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 08/10] ras/amd/atl: Add Hygon DF2 address translation support
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
` (6 preceding siblings ...)
2026-04-03 10:57 ` [RFC PATCH 07/10] ras/amd/atl: Add Hygon DF discovery and MCA decode at initialization Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 09/10] ras/amd/atl: Add Hygon DF3 " Aichun Shi
` (2 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Extend address translation support for Hygon Family 0x18 model 0x6/0x8
(Hygon DF2).
System detection:
- Add register field definitions for Hygon DF2 in hygon/reg_fields.h.
- Add HYGON_DF2 to enum df_revisions and interleave mode
HYGON_DF2_4CHAN_HASH for four-channel hash for Hygon DF2 in internal.h.
- Discover Hygon DF2 socket/die mask widths and shifts; detect model
0x6/0x8 as Hygon DF2.
DRAM map:
- Add hygon_df2_get_dram_addr_map(): use shared AMD DF2 DRAM base/limit
via df2_get_dram_addr_map(), and read Hygon-specific interleave fields.
- Map Hygon-specific HYGON_DF2_4CHAN_HASH interleave mode instead of
the AMD DF2 two-channel hash mode.
- Get die/socket interleave count for Hygon DF2.
Denormalize/dehash:
- Get coherent-station fabric ID for Hygon DF2 including
HYGON_DF2_4CHAN_HASH case.
- Add hygon_df2_4chan_dehash_addr() to support dehash function for
HYGON_DF2_4CHAN_HASH interleave mode.
UMC:
- Form the coherent-station instance ID from the channel field and
sub-channel bit from MCA IPID to match Hygon DF2 UMC addressing.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/hygon/dehash.c | 23 +++++++++++++++++
drivers/ras/amd/atl/hygon/denormalize.c | 3 +++
drivers/ras/amd/atl/hygon/map.c | 33 +++++++++++++++++++++++--
drivers/ras/amd/atl/hygon/reg_fields.h | 18 ++++++++++++++
drivers/ras/amd/atl/hygon/system.c | 9 +++++++
drivers/ras/amd/atl/hygon/umc.c | 8 +++++-
drivers/ras/amd/atl/internal.h | 2 ++
7 files changed, 93 insertions(+), 3 deletions(-)
diff --git a/drivers/ras/amd/atl/hygon/dehash.c b/drivers/ras/amd/atl/hygon/dehash.c
index 43f1bdea1eeb..e0546ea70900 100644
--- a/drivers/ras/amd/atl/hygon/dehash.c
+++ b/drivers/ras/amd/atl/hygon/dehash.c
@@ -48,6 +48,25 @@ static int hygon_df_2chan_dehash_addr(struct addr_ctx *ctx)
return 0;
}
+static int hygon_df2_4chan_dehash_addr(struct addr_ctx *ctx)
+{
+ u8 hashed_bit;
+ u8 intlv_bit_pos = ctx->map.intlv_bit_pos;
+
+ hashed_bit = (ctx->ret_addr >> 12) ^
+ (ctx->ret_addr >> 18) ^
+ (ctx->ret_addr >> 21) ^
+ (ctx->ret_addr >> 30) ^
+ ctx->coh_st_fabric_id;
+
+ hashed_bit &= 0x3;
+ if (hashed_bit != ((ctx->ret_addr >> intlv_bit_pos) & 0x3))
+ ctx->ret_addr = (ctx->ret_addr & ~((u64)3 << intlv_bit_pos)) |
+ (hashed_bit << intlv_bit_pos);
+
+ return 0;
+}
+
int hygon_dehash_address(struct addr_ctx *ctx)
{
switch (ctx->map.intlv_mode) {
@@ -65,6 +84,10 @@ int hygon_dehash_address(struct addr_ctx *ctx)
hygon_df_2chan_dehash_addr(ctx);
break;
+ case HYGON_DF2_4CHAN_HASH:
+ hygon_df2_4chan_dehash_addr(ctx);
+ break;
+
default:
atl_debug_on_bad_intlv_mode(ctx);
return -EINVAL;
diff --git a/drivers/ras/amd/atl/hygon/denormalize.c b/drivers/ras/amd/atl/hygon/denormalize.c
index d189799bd183..82c5982893ba 100644
--- a/drivers/ras/amd/atl/hygon/denormalize.c
+++ b/drivers/ras/amd/atl/hygon/denormalize.c
@@ -14,6 +14,8 @@ static u16 hygon_get_dst_fabric_id(struct addr_ctx *ctx)
switch (df_cfg.rev) {
case HYGON_DF1:
return FIELD_GET(HYGON_DF1_DST_FABRIC_ID, ctx->map.limit);
+ case HYGON_DF2:
+ return FIELD_GET(HYGON_DF2_DST_FABRIC_ID, ctx->map.limit);
default:
atl_debug_on_bad_df_rev();
return 0;
@@ -117,6 +119,7 @@ static u16 hygon_calculate_coh_st_id(struct addr_ctx *ctx)
case NOHASH_16CHAN:
case NOHASH_32CHAN:
case DF2_2CHAN_HASH:
+ case HYGON_DF2_4CHAN_HASH:
return hygon_get_coh_st_id_df(ctx);
default:
diff --git a/drivers/ras/amd/atl/hygon/map.c b/drivers/ras/amd/atl/hygon/map.c
index 2194c10ff587..b0c05532b744 100644
--- a/drivers/ras/amd/atl/hygon/map.c
+++ b/drivers/ras/amd/atl/hygon/map.c
@@ -16,8 +16,12 @@ static int hygon_df_get_intlv_mode(struct addr_ctx *ctx)
if (df_cfg.rev == HYGON_DF1 && ctx->map.intlv_mode == 2)
ctx->map.intlv_mode = HYGON_DF1_3CHAN;
- if (ctx->map.intlv_mode == 8)
- ctx->map.intlv_mode = DF2_2CHAN_HASH;
+ if (ctx->map.intlv_mode == 8) {
+ if (df_cfg.rev == HYGON_DF2)
+ ctx->map.intlv_mode = HYGON_DF2_4CHAN_HASH;
+ else
+ ctx->map.intlv_mode = DF2_2CHAN_HASH;
+ }
if (ctx->map.intlv_mode != NONE &&
ctx->map.intlv_mode != NOHASH_2CHAN &&
@@ -25,6 +29,7 @@ static int hygon_df_get_intlv_mode(struct addr_ctx *ctx)
ctx->map.intlv_mode != NOHASH_4CHAN &&
ctx->map.intlv_mode != NOHASH_8CHAN &&
ctx->map.intlv_mode != NOHASH_16CHAN &&
+ ctx->map.intlv_mode != HYGON_DF2_4CHAN_HASH &&
ctx->map.intlv_mode != DF2_2CHAN_HASH)
return -EINVAL;
@@ -38,6 +43,7 @@ static u64 hygon_get_hi_addr_offset(u32 reg_dram_offset)
switch (df_cfg.rev) {
case HYGON_DF1:
+ case HYGON_DF2:
hi_addr_offset = FIELD_GET(HYGON_DF1_HI_ADDR_OFFSET, reg_dram_offset);
break;
default:
@@ -120,10 +126,24 @@ static int hygon_df1_get_dram_addr_map(struct addr_ctx *ctx)
return 0;
}
+static int hygon_df2_get_dram_addr_map(struct addr_ctx *ctx)
+{
+ if (df2_get_dram_addr_map(ctx))
+ return -EINVAL;
+
+ /* Read D18F0x60 */
+ if (df_indirect_read_instance(ctx->node_id, 0, 0x60,
+ ctx->inst_id, &ctx->map.intlv))
+ return -EINVAL;
+
+ return 0;
+}
+
static int hygon_get_dram_addr_map(struct addr_ctx *ctx)
{
switch (df_cfg.rev) {
case HYGON_DF1: return hygon_df1_get_dram_addr_map(ctx);
+ case HYGON_DF2: return hygon_df2_get_dram_addr_map(ctx);
default:
atl_debug_on_bad_df_rev();
return -EINVAL;
@@ -142,6 +162,9 @@ static int hygon_get_coh_st_fabric_id(struct addr_ctx *ctx)
case HYGON_DF1:
ctx->coh_st_fabric_id = FIELD_GET(HYGON_DF1_COH_ST_FABRIC_ID, reg);
break;
+ case HYGON_DF2:
+ ctx->coh_st_fabric_id = FIELD_GET(HYGON_DF2_COH_ST_FABRIC_ID, reg);
+ break;
default:
atl_debug_on_bad_df_rev();
return -EINVAL;
@@ -226,6 +249,7 @@ static u8 hygon_get_num_intlv_chan(struct addr_ctx *ctx)
return 2;
case HYGON_DF1_3CHAN:
return 3;
+ case HYGON_DF2_4CHAN_HASH:
case NOHASH_4CHAN:
return 4;
case NOHASH_8CHAN:
@@ -261,6 +285,7 @@ static u8 hygon_get_intlv_bit_pos(struct addr_ctx *ctx)
switch (df_cfg.rev) {
case HYGON_DF1:
+ case HYGON_DF2:
addr_sel = FIELD_GET(HYGON_DF1_INTLV_ADDR_SEL, ctx->map.base);
break;
default:
@@ -280,6 +305,9 @@ static u8 hygon_get_num_intlv_dies(struct addr_ctx *ctx)
case HYGON_DF1:
dies = FIELD_GET(HYGON_DF1_INTLV_NUM_DIES, ctx->map.limit);
break;
+ case HYGON_DF2:
+ dies = FIELD_GET(HYGON_DF2_INTLV_NUM_DIES, ctx->map.intlv);
+ break;
default:
atl_debug_on_bad_df_rev();
break;
@@ -295,6 +323,7 @@ static u8 hygon_get_num_intlv_sockets(struct addr_ctx *ctx)
switch (df_cfg.rev) {
case HYGON_DF1:
+ case HYGON_DF2:
sockets = FIELD_GET(HYGON_DF1_INTLV_NUM_SOCKETS, ctx->map.base);
break;
default:
diff --git a/drivers/ras/amd/atl/hygon/reg_fields.h b/drivers/ras/amd/atl/hygon/reg_fields.h
index 69be2d5d2240..eaf1d628fd0e 100644
--- a/drivers/ras/amd/atl/hygon/reg_fields.h
+++ b/drivers/ras/amd/atl/hygon/reg_fields.h
@@ -17,8 +17,10 @@
*
* D18F0x50 [Fabric Block Instance Information 3]
* HYGON_DF1 BlockFabricId [17:8]
+ * HYGON_DF2 BlockFabricId [18:8]
*/
#define HYGON_DF1_COH_ST_FABRIC_ID GENMASK(17, 8)
+#define HYGON_DF2_COH_ST_FABRIC_ID GENMASK(18, 8)
/*
* Interleave Number of Sockets
@@ -30,6 +32,7 @@
*
* D18F0x110 [DRAM Base Address]
* HYGON_DF1 IntLvNumSockets [3:2]
+ * HYGON_DF2 IntLvNumSockets [3:2]
*/
#define HYGON_DF1_INTLV_NUM_SOCKETS GENMASK(3, 2)
@@ -43,6 +46,7 @@
*
* D18F0x110 [DRAM Base Address]
* HYGON_DF1 IntLvNumChan [7:4]
+ * HYGON_DF2 IntLvNumChan [7:4]
*/
#define HYGON_DF1_INTLV_NUM_CHAN GENMASK(7, 4)
@@ -56,6 +60,7 @@
*
* D18F0x110 [DRAM Base Address]
* HYGON_DF1 IntLvAddrSel [10:8]
+ * HYGON_DF2 IntLvAddrSel [10:8]
*/
#define HYGON_DF1_INTLV_ADDR_SEL GENMASK(10, 8)
@@ -69,8 +74,10 @@
*
* D18F0x114 [DRAM Limit Address]
* HYGON_DF1 DstFabricID [9:0]
+ * HYGON_DF2 DstFabricID [10:0]
*/
#define HYGON_DF1_DST_FABRIC_ID GENMASK(9, 0)
+#define HYGON_DF2_DST_FABRIC_ID GENMASK(10, 0)
/*
* Interleave Number of Dies
@@ -82,8 +89,12 @@
*
* D18F0x114 [DRAM Limit Address]
* HYGON_DF1 IntLvNumDies [11:10]
+ *
+ * D18F0x60
+ * HYGON_DF2 IntLvNumDies [1:0]
*/
#define HYGON_DF1_INTLV_NUM_DIES GENMASK(11, 10)
+#define HYGON_DF2_INTLV_NUM_DIES GENMASK(1, 0)
/*
* High Address Offset
@@ -95,6 +106,7 @@
*
* D18F0x214 [DRAM Offset]
* HYGON_DF1 HiAddrOffset [31:19]
+ * HYGON_DF2 HiAddrOffset [31:19]
*/
#define HYGON_DF1_HI_ADDR_OFFSET GENMASK(31, 19)
@@ -108,8 +120,10 @@
*
* D18F1x208 [System Fabric ID Mask]
* HYGON_DF1 DieIdMask [9:0]
+ * HYGON_DF2 DieIdMask [10:0]
*/
#define HYGON_DF1_DIE_ID_MASK GENMASK(9, 0)
+#define HYGON_DF2_DIE_ID_MASK GENMASK(10, 0)
/*
* Die ID Shift
@@ -121,6 +135,7 @@
*
* D18F1x208 [System Fabric ID Mask]
* HYGON_DF1 DieIdShift [15:12]
+ * HYGON_DF2 DieIdShift [15:12]
*/
#define HYGON_DF1_DIE_ID_SHIFT GENMASK(15, 12)
@@ -134,8 +149,10 @@
*
* D18F1x208 [System Fabric ID Mask]
* HYGON_DF1 SocketIdMask [25:16]
+ * HYGON_DF2 SocketIdMask [26:16]
*/
#define HYGON_DF1_SOCKET_ID_MASK GENMASK(25, 16)
+#define HYGON_DF2_SOCKET_ID_MASK GENMASK(26, 16)
/*
* Socket ID Shift
@@ -147,5 +164,6 @@
*
* D18F1x208 [System Fabric ID Mask]
* HYGON_DF1 SocketIdShift [31:28]
+ * HYGON_DF2 SocketIdShift [31:28]
*/
#define HYGON_DF1_SOCKET_ID_SHIFT GENMASK(31, 28)
diff --git a/drivers/ras/amd/atl/hygon/system.c b/drivers/ras/amd/atl/hygon/system.c
index 2a149cfc34c1..a06a39bc4527 100644
--- a/drivers/ras/amd/atl/hygon/system.c
+++ b/drivers/ras/amd/atl/hygon/system.c
@@ -18,6 +18,12 @@ static void hygon_df_get_masks_shifts(u32 mask0)
df_cfg.die_id_shift = FIELD_GET(HYGON_DF1_DIE_ID_SHIFT, mask0);
df_cfg.die_id_mask = FIELD_GET(HYGON_DF1_DIE_ID_MASK, mask0);
break;
+ case HYGON_DF2:
+ df_cfg.socket_id_shift = FIELD_GET(HYGON_DF1_SOCKET_ID_SHIFT, mask0);
+ df_cfg.socket_id_mask = FIELD_GET(HYGON_DF2_SOCKET_ID_MASK, mask0);
+ df_cfg.die_id_shift = FIELD_GET(HYGON_DF1_DIE_ID_SHIFT, mask0);
+ df_cfg.die_id_mask = FIELD_GET(HYGON_DF2_DIE_ID_MASK, mask0);
+ break;
default:
atl_debug_on_bad_df_rev();
return;
@@ -30,6 +36,8 @@ static int hygon_determine_df_rev(void)
if (boot_cpu_data.x86_model == 0x4 || boot_cpu_data.x86_model == 0x5)
df_cfg.rev = HYGON_DF1;
+ else if (boot_cpu_data.x86_model == 0x6 || boot_cpu_data.x86_model == 0x8)
+ df_cfg.rev = HYGON_DF2;
/* Read D18F1x208 (SystemFabricIdMask). */
if (df_indirect_read_broadcast(0, 1, 0x208, &fabric_id_mask0))
@@ -44,6 +52,7 @@ static void hygon_get_num_maps(void)
{
switch (df_cfg.rev) {
case HYGON_DF1:
+ case HYGON_DF2:
df_cfg.num_coh_st_maps = 2;
break;
default:
diff --git a/drivers/ras/amd/atl/hygon/umc.c b/drivers/ras/amd/atl/hygon/umc.c
index e2339db3f575..b912dbdebee4 100644
--- a/drivers/ras/amd/atl/hygon/umc.c
+++ b/drivers/ras/amd/atl/hygon/umc.c
@@ -27,7 +27,13 @@ static u8 hygon_get_ipid_sub_channel(struct atl_err *err)
#define HYGON_UMC_CHANNEL_NUM GENMASK(23, 20)
static u8 hygon_get_coh_st_inst_id(struct atl_err *err)
{
- return FIELD_GET(HYGON_UMC_CHANNEL_NUM, err->ipid);
+ u8 sub_channel = hygon_get_ipid_sub_channel(err);
+ u8 coh_st_inst_id = FIELD_GET(HYGON_UMC_CHANNEL_NUM, err->ipid);
+
+ if (df_cfg.rev == HYGON_DF2)
+ coh_st_inst_id = (coh_st_inst_id << 1) + sub_channel;
+
+ return coh_st_inst_id;
}
unsigned long hygon_convert_umc_mca_addr_to_sys_addr(struct atl_err *err)
diff --git a/drivers/ras/amd/atl/internal.h b/drivers/ras/amd/atl/internal.h
index f943cc33f82e..c368559ca2ae 100644
--- a/drivers/ras/amd/atl/internal.h
+++ b/drivers/ras/amd/atl/internal.h
@@ -44,6 +44,7 @@
enum df_revisions {
UNKNOWN,
HYGON_DF1,
+ HYGON_DF2,
DF2,
DF3,
DF3p5,
@@ -61,6 +62,7 @@ enum intlv_modes {
DF3_6CHAN = 0x06,
NOHASH_16CHAN = 0x07,
NOHASH_32CHAN = 0x08,
+ HYGON_DF2_4CHAN_HASH = 0x09,
DF3_COD4_2CHAN_HASH = 0x0C,
DF3_COD2_4CHAN_HASH = 0x0D,
DF3_COD1_8CHAN_HASH = 0x0E,
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 09/10] ras/amd/atl: Add Hygon DF3 address translation support
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
` (7 preceding siblings ...)
2026-04-03 10:57 ` [RFC PATCH 08/10] ras/amd/atl: Add Hygon DF2 address translation support Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 10/10] EDAC/amd64: Add Hygon Family 0x18 models 0x4-0x8 support Aichun Shi
2026-05-14 15:08 ` [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Borislav Petkov
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Extend address translation support for Hygon Family 0x18 model 0x7
(Hygon DF3).
System detection:
- Add register field definitions for Hygon DF3 in hygon/reg_fields.h.
- Add HYGON_DF3 to enum df_revisions in internal.h.
- Discover Hygon DF3 socket/die mask widths and shifts; detect model
0x7 as Hygon DF3.
DRAM map:
- Use shared AMD DF2 DRAM base/limit via df2_get_dram_addr_map().
- Map Hygon-specific HYGON_DF2_4CHAN_HASH interleave mode for Hygon DF3.
- Get die/socket interleave count for Hygon DF3.
- Get DRAM offset and high address offset for Hygon DF3.
Denormalize:
- Get destination fabric ID for Hygon DF3.
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/ras/amd/atl/hygon/denormalize.c | 2 ++
drivers/ras/amd/atl/hygon/map.c | 21 +++++++++++++++++++--
drivers/ras/amd/atl/hygon/reg_fields.h | 22 ++++++++++++++++++++++
drivers/ras/amd/atl/hygon/system.c | 9 +++++++++
drivers/ras/amd/atl/internal.h | 1 +
5 files changed, 53 insertions(+), 2 deletions(-)
diff --git a/drivers/ras/amd/atl/hygon/denormalize.c b/drivers/ras/amd/atl/hygon/denormalize.c
index 82c5982893ba..6e882a6b7b55 100644
--- a/drivers/ras/amd/atl/hygon/denormalize.c
+++ b/drivers/ras/amd/atl/hygon/denormalize.c
@@ -16,6 +16,8 @@ static u16 hygon_get_dst_fabric_id(struct addr_ctx *ctx)
return FIELD_GET(HYGON_DF1_DST_FABRIC_ID, ctx->map.limit);
case HYGON_DF2:
return FIELD_GET(HYGON_DF2_DST_FABRIC_ID, ctx->map.limit);
+ case HYGON_DF3:
+ return FIELD_GET(HYGON_DF3_DST_FABRIC_ID, ctx->map.limit);
default:
atl_debug_on_bad_df_rev();
return 0;
diff --git a/drivers/ras/amd/atl/hygon/map.c b/drivers/ras/amd/atl/hygon/map.c
index b0c05532b744..2a3d962be083 100644
--- a/drivers/ras/amd/atl/hygon/map.c
+++ b/drivers/ras/amd/atl/hygon/map.c
@@ -17,7 +17,7 @@ static int hygon_df_get_intlv_mode(struct addr_ctx *ctx)
ctx->map.intlv_mode = HYGON_DF1_3CHAN;
if (ctx->map.intlv_mode == 8) {
- if (df_cfg.rev == HYGON_DF2)
+ if (df_cfg.rev == HYGON_DF2 || df_cfg.rev == HYGON_DF3)
ctx->map.intlv_mode = HYGON_DF2_4CHAN_HASH;
else
ctx->map.intlv_mode = DF2_2CHAN_HASH;
@@ -46,6 +46,9 @@ static u64 hygon_get_hi_addr_offset(u32 reg_dram_offset)
case HYGON_DF2:
hi_addr_offset = FIELD_GET(HYGON_DF1_HI_ADDR_OFFSET, reg_dram_offset);
break;
+ case HYGON_DF3:
+ hi_addr_offset = FIELD_GET(HYGON_DF3_HI_ADDR_OFFSET, reg_dram_offset);
+ break;
default:
hi_addr_offset = 0;
atl_debug_on_bad_df_rev();
@@ -77,7 +80,12 @@ static int hygon_get_dram_offset(struct addr_ctx *ctx, u64 *norm_offset)
*/
map_num = ctx->map.num - 1;
- if (df_cfg.rev >= HYGON_DF1) {
+ if (df_cfg.rev >= HYGON_DF3) {
+ /* Read D18F0x1B4 (DramOffset) */
+ if (df_indirect_read_instance(ctx->node_id, 0, 0x1B4 + (4 * map_num),
+ ctx->inst_id, ®_dram_offset))
+ return -EINVAL;
+ } else if (df_cfg.rev >= HYGON_DF1) {
/* Read D18F0x214 (DramOffset) */
if (df_indirect_read_instance(ctx->node_id, 0, 0x214 + (4 * map_num),
ctx->inst_id, ®_dram_offset))
@@ -144,6 +152,7 @@ static int hygon_get_dram_addr_map(struct addr_ctx *ctx)
switch (df_cfg.rev) {
case HYGON_DF1: return hygon_df1_get_dram_addr_map(ctx);
case HYGON_DF2: return hygon_df2_get_dram_addr_map(ctx);
+ case HYGON_DF3: return df2_get_dram_addr_map(ctx);
default:
atl_debug_on_bad_df_rev();
return -EINVAL;
@@ -165,6 +174,9 @@ static int hygon_get_coh_st_fabric_id(struct addr_ctx *ctx)
case HYGON_DF2:
ctx->coh_st_fabric_id = FIELD_GET(HYGON_DF2_COH_ST_FABRIC_ID, reg);
break;
+ case HYGON_DF3:
+ ctx->coh_st_fabric_id = FIELD_GET(HYGON_DF3_COH_ST_FABRIC_ID, reg);
+ break;
default:
atl_debug_on_bad_df_rev();
return -EINVAL;
@@ -286,6 +298,7 @@ static u8 hygon_get_intlv_bit_pos(struct addr_ctx *ctx)
switch (df_cfg.rev) {
case HYGON_DF1:
case HYGON_DF2:
+ case HYGON_DF3:
addr_sel = FIELD_GET(HYGON_DF1_INTLV_ADDR_SEL, ctx->map.base);
break;
default:
@@ -303,6 +316,7 @@ static u8 hygon_get_num_intlv_dies(struct addr_ctx *ctx)
switch (df_cfg.rev) {
case HYGON_DF1:
+ case HYGON_DF3:
dies = FIELD_GET(HYGON_DF1_INTLV_NUM_DIES, ctx->map.limit);
break;
case HYGON_DF2:
@@ -326,6 +340,9 @@ static u8 hygon_get_num_intlv_sockets(struct addr_ctx *ctx)
case HYGON_DF2:
sockets = FIELD_GET(HYGON_DF1_INTLV_NUM_SOCKETS, ctx->map.base);
break;
+ case HYGON_DF3:
+ sockets = FIELD_GET(HYGON_DF3_INTLV_NUM_SOCKETS, ctx->map.limit);
+ break;
default:
atl_debug_on_bad_df_rev();
break;
diff --git a/drivers/ras/amd/atl/hygon/reg_fields.h b/drivers/ras/amd/atl/hygon/reg_fields.h
index eaf1d628fd0e..1ee9980d7834 100644
--- a/drivers/ras/amd/atl/hygon/reg_fields.h
+++ b/drivers/ras/amd/atl/hygon/reg_fields.h
@@ -18,9 +18,11 @@
* D18F0x50 [Fabric Block Instance Information 3]
* HYGON_DF1 BlockFabricId [17:8]
* HYGON_DF2 BlockFabricId [18:8]
+ * HYGON_DF3 BlockFabricId [15:8]
*/
#define HYGON_DF1_COH_ST_FABRIC_ID GENMASK(17, 8)
#define HYGON_DF2_COH_ST_FABRIC_ID GENMASK(18, 8)
+#define HYGON_DF3_COH_ST_FABRIC_ID GENMASK(15, 8)
/*
* Interleave Number of Sockets
@@ -33,8 +35,12 @@
* D18F0x110 [DRAM Base Address]
* HYGON_DF1 IntLvNumSockets [3:2]
* HYGON_DF2 IntLvNumSockets [3:2]
+ *
+ * D18F0x114 [DRAM Limit Address]
+ * HYGON_DF3 IntLvNumSockets [8]
*/
#define HYGON_DF1_INTLV_NUM_SOCKETS GENMASK(3, 2)
+#define HYGON_DF3_INTLV_NUM_SOCKETS BIT(8)
/*
* Interleave Number of Channels
@@ -47,6 +53,7 @@
* D18F0x110 [DRAM Base Address]
* HYGON_DF1 IntLvNumChan [7:4]
* HYGON_DF2 IntLvNumChan [7:4]
+ * HYGON_DF3 IntLvNumChan [7:4]
*/
#define HYGON_DF1_INTLV_NUM_CHAN GENMASK(7, 4)
@@ -61,6 +68,7 @@
* D18F0x110 [DRAM Base Address]
* HYGON_DF1 IntLvAddrSel [10:8]
* HYGON_DF2 IntLvAddrSel [10:8]
+ * HYGON_DF3 IntLvAddrSel [10:8]
*/
#define HYGON_DF1_INTLV_ADDR_SEL GENMASK(10, 8)
@@ -75,9 +83,11 @@
* D18F0x114 [DRAM Limit Address]
* HYGON_DF1 DstFabricID [9:0]
* HYGON_DF2 DstFabricID [10:0]
+ * HYGON_DF3 DstFabricID [7:0]
*/
#define HYGON_DF1_DST_FABRIC_ID GENMASK(9, 0)
#define HYGON_DF2_DST_FABRIC_ID GENMASK(10, 0)
+#define HYGON_DF3_DST_FABRIC_ID GENMASK(7, 0)
/*
* Interleave Number of Dies
@@ -89,6 +99,7 @@
*
* D18F0x114 [DRAM Limit Address]
* HYGON_DF1 IntLvNumDies [11:10]
+ * HYGON_DF3 IntLvNumDies [11:10]
*
* D18F0x60
* HYGON_DF2 IntLvNumDies [1:0]
@@ -107,8 +118,12 @@
* D18F0x214 [DRAM Offset]
* HYGON_DF1 HiAddrOffset [31:19]
* HYGON_DF2 HiAddrOffset [31:19]
+ *
+ * D18F0x1B4 [DRAM Offset]
+ * HYGON_DF3 HiAddrOffset [31:20]
*/
#define HYGON_DF1_HI_ADDR_OFFSET GENMASK(31, 19)
+#define HYGON_DF3_HI_ADDR_OFFSET GENMASK(31, 20)
/*
* Die ID Mask
@@ -121,9 +136,11 @@
* D18F1x208 [System Fabric ID Mask]
* HYGON_DF1 DieIdMask [9:0]
* HYGON_DF2 DieIdMask [10:0]
+ * HYGON_DF3 DieIdMask [15:8]
*/
#define HYGON_DF1_DIE_ID_MASK GENMASK(9, 0)
#define HYGON_DF2_DIE_ID_MASK GENMASK(10, 0)
+#define HYGON_DF3_DIE_ID_MASK GENMASK(15, 8)
/*
* Die ID Shift
@@ -136,8 +153,10 @@
* D18F1x208 [System Fabric ID Mask]
* HYGON_DF1 DieIdShift [15:12]
* HYGON_DF2 DieIdShift [15:12]
+ * HYGON_DF3 DieIdShift [27:24]
*/
#define HYGON_DF1_DIE_ID_SHIFT GENMASK(15, 12)
+#define HYGON_DF3_DIE_ID_SHIFT GENMASK(27, 24)
/*
* Socket ID Mask
@@ -150,9 +169,11 @@
* D18F1x208 [System Fabric ID Mask]
* HYGON_DF1 SocketIdMask [25:16]
* HYGON_DF2 SocketIdMask [26:16]
+ * HYGON_DF3 SocketIdMask [23:16]
*/
#define HYGON_DF1_SOCKET_ID_MASK GENMASK(25, 16)
#define HYGON_DF2_SOCKET_ID_MASK GENMASK(26, 16)
+#define HYGON_DF3_SOCKET_ID_MASK GENMASK(23, 16)
/*
* Socket ID Shift
@@ -165,5 +186,6 @@
* D18F1x208 [System Fabric ID Mask]
* HYGON_DF1 SocketIdShift [31:28]
* HYGON_DF2 SocketIdShift [31:28]
+ * HYGON_DF3 SocketIdShift [31:28]
*/
#define HYGON_DF1_SOCKET_ID_SHIFT GENMASK(31, 28)
diff --git a/drivers/ras/amd/atl/hygon/system.c b/drivers/ras/amd/atl/hygon/system.c
index a06a39bc4527..f3ca6f58d00f 100644
--- a/drivers/ras/amd/atl/hygon/system.c
+++ b/drivers/ras/amd/atl/hygon/system.c
@@ -24,6 +24,12 @@ static void hygon_df_get_masks_shifts(u32 mask0)
df_cfg.die_id_shift = FIELD_GET(HYGON_DF1_DIE_ID_SHIFT, mask0);
df_cfg.die_id_mask = FIELD_GET(HYGON_DF2_DIE_ID_MASK, mask0);
break;
+ case HYGON_DF3:
+ df_cfg.socket_id_shift = FIELD_GET(HYGON_DF1_SOCKET_ID_SHIFT, mask0);
+ df_cfg.socket_id_mask = FIELD_GET(HYGON_DF3_SOCKET_ID_MASK, mask0);
+ df_cfg.die_id_shift = FIELD_GET(HYGON_DF3_DIE_ID_SHIFT, mask0);
+ df_cfg.die_id_mask = FIELD_GET(HYGON_DF3_DIE_ID_MASK, mask0);
+ break;
default:
atl_debug_on_bad_df_rev();
return;
@@ -38,6 +44,8 @@ static int hygon_determine_df_rev(void)
df_cfg.rev = HYGON_DF1;
else if (boot_cpu_data.x86_model == 0x6 || boot_cpu_data.x86_model == 0x8)
df_cfg.rev = HYGON_DF2;
+ else if (boot_cpu_data.x86_model == 0x7)
+ df_cfg.rev = HYGON_DF3;
/* Read D18F1x208 (SystemFabricIdMask). */
if (df_indirect_read_broadcast(0, 1, 0x208, &fabric_id_mask0))
@@ -53,6 +61,7 @@ static void hygon_get_num_maps(void)
switch (df_cfg.rev) {
case HYGON_DF1:
case HYGON_DF2:
+ case HYGON_DF3:
df_cfg.num_coh_st_maps = 2;
break;
default:
diff --git a/drivers/ras/amd/atl/internal.h b/drivers/ras/amd/atl/internal.h
index c368559ca2ae..5e95d598c89e 100644
--- a/drivers/ras/amd/atl/internal.h
+++ b/drivers/ras/amd/atl/internal.h
@@ -45,6 +45,7 @@ enum df_revisions {
UNKNOWN,
HYGON_DF1,
HYGON_DF2,
+ HYGON_DF3,
DF2,
DF3,
DF3p5,
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH 10/10] EDAC/amd64: Add Hygon Family 0x18 models 0x4-0x8 support
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
` (8 preceding siblings ...)
2026-04-03 10:57 ` [RFC PATCH 09/10] ras/amd/atl: Add Hygon DF3 " Aichun Shi
@ 2026-04-03 10:57 ` Aichun Shi
2026-05-14 15:08 ` [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Borislav Petkov
10 siblings, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-04-03 10:57 UTC (permalink / raw)
To: yazen.ghannam; +Cc: tglx, linux-edac, linux-kernel, shiaichun
Add UMC base support for Hygon Family 0x18 models 0x4-0x8.
Add error channel fields from MCA IPID for Hygon.
Add distinct memory-controller counts for Hygon models 0x4/0x5.
Add Hygon node number support in amd64_edac_init() and amd64_edac_exit().
Signed-off-by: Aichun Shi <shiaichun@open-hieco.net>
---
drivers/edac/amd64_edac.c | 68 ++++++++++++++++++++++++++++++++-------
1 file changed, 56 insertions(+), 12 deletions(-)
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 8908ab881c85..4a24cf28e543 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -4,6 +4,7 @@
#include "amd64_edac.h"
#include <asm/amd/nb.h>
#include <asm/amd/node.h>
+#include <asm/hygon/node.h>
static struct edac_pci_ctl_info *pci_ctl;
@@ -98,6 +99,29 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
return pcibios_err_to_errno(err);
}
+static u32 get_umc_base_addr(struct amd64_pvt *pvt, u8 channel)
+{
+ if (hygon_f18h_m4h()) {
+ struct pci_dev *dev = node_to_amd_nb(pvt->mc_node_id)->misc;
+ u8 df_id;
+
+ hygon_get_dfid(dev, &df_id);
+
+ return get_umc_base(channel) + (0x80000000 + (0x10000000 * (df_id - 4)));
+ }
+
+ return get_umc_base(channel);
+}
+
+static u16 get_num_nodes(void)
+{
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON &&
+ boot_cpu_data.x86 == 0x18)
+ return amd_num_nodes();
+
+ return amd_nb_num();
+}
+
/*
* Select DCT to which PCI cfg accesses are routed
*/
@@ -1452,12 +1476,15 @@ static void umc_read_base_mask(struct amd64_pvt *pvt)
u32 mask_reg, mask_reg_sec;
u32 *base, *base_sec;
u32 *mask, *mask_sec;
+ u32 umc_base;
int cs, umc;
u32 tmp;
for_each_umc(umc) {
- umc_base_reg = get_umc_base(umc) + UMCCH_BASE_ADDR;
- umc_base_reg_sec = get_umc_base(umc) + UMCCH_BASE_ADDR_SEC;
+ umc_base = get_umc_base_addr(pvt, umc);
+
+ umc_base_reg = umc_base + UMCCH_BASE_ADDR;
+ umc_base_reg_sec = umc_base + UMCCH_BASE_ADDR_SEC;
for_each_chip_select(cs, umc, pvt) {
base = &pvt->csels[umc].csbases[cs];
@@ -1479,8 +1506,8 @@ static void umc_read_base_mask(struct amd64_pvt *pvt)
}
}
- umc_mask_reg = get_umc_base(umc) + UMCCH_ADDR_MASK;
- umc_mask_reg_sec = get_umc_base(umc) + get_umc_reg(pvt, UMCCH_ADDR_MASK_SEC);
+ umc_mask_reg = umc_base + UMCCH_ADDR_MASK;
+ umc_mask_reg_sec = umc_base + get_umc_reg(pvt, UMCCH_ADDR_MASK_SEC);
for_each_chip_select_mask(cs, umc, pvt) {
mask = &pvt->csels[umc].csmasks[cs];
@@ -1567,7 +1594,8 @@ static void umc_determine_memory_type(struct amd64_pvt *pvt)
* Check if the system supports the "DDR Type" field in UMC Config
* and has DDR5 DIMMs in use.
*/
- if (pvt->flags.zn_regs_v2 && ((umc->umc_cfg & GENMASK(2, 0)) == 0x1)) {
+ if ((pvt->flags.zn_regs_v2 || hygon_f18h_m4h()) &&
+ ((umc->umc_cfg & GENMASK(2, 0)) == 0x1)) {
if (umc->dimm_cfg & BIT(5))
umc->dram_type = MEM_LRDDR5;
else if (umc->dimm_cfg & BIT(4))
@@ -2801,7 +2829,11 @@ static inline void decode_bus_error(int node_id, struct mce *m)
*/
static void umc_get_err_info(struct mce *m, struct err_info *err)
{
- err->channel = (m->ipid & GENMASK(31, 0)) >> 20;
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON &&
+ boot_cpu_data.x86 == 0x18)
+ err->channel = FIELD_GET(GENMASK(23, 20), m->ipid);
+ else
+ err->channel = FIELD_GET(GENMASK(31, 20), m->ipid);
err->csrow = m->synd & 0x7;
}
@@ -2922,8 +2954,7 @@ static void umc_read_mc_regs(struct amd64_pvt *pvt)
/* Read registers from each UMC */
for_each_umc(i) {
-
- umc_base = get_umc_base(i);
+ umc_base = get_umc_base_addr(pvt, i);
umc = &pvt->umc[i];
if (!amd_smn_read(nid, umc_base + get_umc_reg(pvt, UMCCH_DIMM_CFG), &tmp))
@@ -3841,6 +3872,16 @@ static int per_family_init(struct amd64_pvt *pvt)
break;
case 0x18:
+ switch (pvt->model) {
+ case 0x4:
+ pvt->max_mcs = 3;
+ break;
+ case 0x5:
+ pvt->max_mcs = 1;
+ break;
+ default:
+ break;
+ }
break;
case 0x19:
@@ -4128,6 +4169,7 @@ static int __init amd64_edac_init(void)
{
const char *owner;
int err = -ENODEV;
+ u16 node_num;
int i;
if (ghes_get_devices())
@@ -4140,13 +4182,15 @@ static int __init amd64_edac_init(void)
if (!x86_match_cpu(amd64_cpuids))
return -ENODEV;
- if (!amd_nb_num())
+ node_num = get_num_nodes();
+
+ if (!node_num)
return -ENODEV;
opstate_init();
err = -ENOMEM;
- ecc_stngs = kzalloc_objs(ecc_stngs[0], amd_nb_num());
+ ecc_stngs = kzalloc_objs(ecc_stngs[0], node_num);
if (!ecc_stngs)
goto err_free;
@@ -4154,7 +4198,7 @@ static int __init amd64_edac_init(void)
if (!msrs)
goto err_free;
- for (i = 0; i < amd_nb_num(); i++) {
+ for (i = 0; i < node_num; i++) {
err = probe_one_instance(i);
if (err) {
/* unwind properly */
@@ -4210,7 +4254,7 @@ static void __exit amd64_edac_exit(void)
else
amd_unregister_ecc_decoder(decode_bus_error);
- for (i = 0; i < amd_nb_num(); i++)
+ for (i = 0; i < get_num_nodes(); i++)
remove_one_instance(i);
kfree(ecc_stngs);
--
2.47.3
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
` (9 preceding siblings ...)
2026-04-03 10:57 ` [RFC PATCH 10/10] EDAC/amd64: Add Hygon Family 0x18 models 0x4-0x8 support Aichun Shi
@ 2026-05-14 15:08 ` Borislav Petkov
2026-05-16 5:42 ` Aichun Shi
2026-05-21 1:46 ` Aichun Shi
10 siblings, 2 replies; 14+ messages in thread
From: Borislav Petkov @ 2026-05-14 15:08 UTC (permalink / raw)
To: Aichun Shi; +Cc: yazen.ghannam, tglx, linux-edac, linux-kernel
On Fri, Apr 03, 2026 at 06:57:24PM +0800, Aichun Shi wrote:
> Hi all,
>
> This is an RFC (Request For Comments) for the patch series that proposes
> Hygon Family 0x18 (models 0x4-0x8) support for UMC ECC error address
> translation, aligned with the existing AMD EDAC + AMD ATL layering in
> mainline.
Please go over all review comments and address:
https://sashiko.dev/#/patchset/cover.1775213147.git.shiaichun%40open-hieco.net
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation
2026-05-14 15:08 ` [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Borislav Petkov
@ 2026-05-16 5:42 ` Aichun Shi
2026-05-21 1:46 ` Aichun Shi
1 sibling, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-05-16 5:42 UTC (permalink / raw)
To: Borislav Petkov; +Cc: yazen.ghannam, tglx, linux-edac, linux-kernel
On 5/14/2026 11:08 PM, Borislav Petkov wrote:
> On Fri, Apr 03, 2026 at 06:57:24PM +0800, Aichun Shi wrote:
>> Hi all,
>>
>> This is an RFC (Request For Comments) for the patch series that proposes
>> Hygon Family 0x18 (models 0x4-0x8) support for UMC ECC error address
>> translation, aligned with the existing AMD EDAC + AMD ATL layering in
>> mainline.
>
> Please go over all review comments and address:
>
> https://sashiko.dev/#/patchset/cover.1775213147.git.shiaichun%40open-hieco.net
>
Hi Boris,
Thanks for the review. I will go over all of them and address them soon.
Best Regards,
Aichun
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation
2026-05-14 15:08 ` [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Borislav Petkov
2026-05-16 5:42 ` Aichun Shi
@ 2026-05-21 1:46 ` Aichun Shi
1 sibling, 0 replies; 14+ messages in thread
From: Aichun Shi @ 2026-05-21 1:46 UTC (permalink / raw)
To: Borislav Petkov; +Cc: yazen.ghannam, tglx, linux-edac, linux-kernel
On 5/14/2026 11:08 PM, Borislav Petkov wrote:
> On Fri, Apr 03, 2026 at 06:57:24PM +0800, Aichun Shi wrote:
>> Hi all,
>>
>> This is an RFC (Request For Comments) for the patch series that proposes
>> Hygon Family 0x18 (models 0x4-0x8) support for UMC ECC error address
>> translation, aligned with the existing AMD EDAC + AMD ATL layering in
>> mainline.
>
> Please go over all review comments and address:
>
> https://sashiko.dev/#/patchset/cover.1775213147.git.shiaichun%40open-hieco.net
>
Hi Boris,
Please see my comments below, and I will send RFC V2 soon.
> Patch 2: [RFC PATCH 02/10] ras/amd/atl: Add Hygon DF1 DRAM address map decoding helper
> Result:
> Critical: 0 · High: 1 · Medium: 0 · Low: 0
>> +static u8 hygon_get_num_intlv_chan(struct addr_ctx *ctx)
>> +{
>> + switch (ctx->map.intlv_mode) {
>> + case NONE:
>> + return 1;
>> + case NOHASH_2CHAN:
>> + return 2;
>> + case HYGON_DF1_3CHAN:
>> + return 3;
>> + case NOHASH_4CHAN:
>> + return 4;
>> + case NOHASH_8CHAN:
>> + return 8;
>> + case NOHASH_16CHAN:
>> + return 16;
>> + case NOHASH_32CHAN:
>> + return 32;
>> + default:
>> + atl_debug_on_bad_intlv_mode(ctx);
>> + return 0;
>> + }
>> +}
> In hygon_df_get_intlv_mode(), an initial value of 8 is explicitly mapped to
> DF2_2CHAN_HASH. However, the switch statement in hygon_get_num_intlv_chan()
> checks for NOHASH_32CHAN (which is enum value 0x08) but does not have a case
> for DF2_2CHAN_HASH.
> Does this code fail to handle the DF2_2CHAN_HASH interleave mode?
> If DF2_2CHAN_HASH is used, the switch will fall through to the default case
> and return 0 channels. This 0 propagates into ctx->map.total_intlv_chan
> and ctx->map.total_intlv_bits.
> Later in the address translation pipeline, calculating the coherent station
> ID uses order_base_2(ctx->map.num_intlv_chan). Because order_base_2(0)
> returns 0, this causes GENMASK(num_intlv_bits - 1, 0) to evaluate to
> GENMASK(-1, 0), producing ~0UL.
> Can this corrupt the translated system physical address by mangling the
> subsequent die/socket bit shifts and ORing the invalid station ID directly
> over the address bits?
> Should DF2_2CHAN_HASH be included in this switch block?
Will fix in RFC V2.
> Patch 3: [RFC PATCH 03/10] ras/amd/atl: Add Hygon DF1 normalized address denormalization helper
> Result:
> Critical: 0 · High: 3 · Medium: 0 · Low: 0
>
>> +static u16 hygon_get_coh_st_id_df(struct addr_ctx *ctx)
>> +{
>> + u8 num_socket_intlv_bits = ilog2(ctx->map.num_intlv_sockets);
>> + u8 num_die_intlv_bits = ilog2(ctx->map.num_intlv_dies);
>> + u16 coh_st_id = ctx->coh_st_fabric_id;
>> + u16 mask;
>> + u8 intlv_bit_pos = ctx->map.intlv_bit_pos;
>> + u8 num_intlv_bits = order_base_2(ctx->map.num_intlv_chan);
>> +
>> + if (ctx->map.intlv_mode == HYGON_DF1_3CHAN) {
>> + u8 cs_offset;
>> + u16 dst_fabric_id = hygon_get_dst_fabric_id(ctx);
>> + u64 temp_addr_x;
>> +
>> + cs_offset = (ctx->coh_st_fabric_id & 0x3) - (dst_fabric_id & 0x3);
> If the base and target IDs cross a multiple-of-4 boundary (for example, if
> the base ID is 2 and the target ID is 4), does this subtraction underflow?
> Since cs_offset is a u8, (4 & 3) - (2 & 3) would evaluate to 0 - 2, wrapping
> around to 254.
>> + if (cs_offset > 3) {
> And cause this check to falsely evaluate to true, rejecting a valid topology?
As the valid values of ctx->coh_st_fabric_id and dst_fabric_id are
between 0 and 3, "cs_offset > 3" check should be enough for invalid
values of ctx->coh_st_fabric_id and dst_fabric_id.
>> + atl_debug(ctx,
>> + "Invalid cs_offset: 0x%x coh_st_fabric_id: 0x%x dst_fabric_id: 0x%x.\n",
>> + cs_offset, ctx->coh_st_fabric_id, dst_fabric_id);
>> + return ~0;
>> + }
>> +
>> + temp_addr_x = (ctx->ret_addr & GENMASK_ULL(63, intlv_bit_pos)) >>
>> + intlv_bit_pos;
>> + temp_addr_x = temp_addr_x * 3 + cs_offset;
>> + ctx->ret_addr = temp_addr_x;
>> +
>> + coh_st_id = temp_addr_x & GENMASK_ULL(num_intlv_bits - 1, 0);
>> + } else {
>> + mask = GENMASK(num_intlv_bits - 1, 0);
>> + coh_st_id &= mask;
>> + }
>> +
>> + /* Die interleave bits */
>> + if (num_die_intlv_bits) {
>> + u16 die_bits;
>> +
>> + mask = GENMASK(num_die_intlv_bits - 1, 0);
>> + die_bits = ctx->coh_st_fabric_id & df_cfg.die_id_mask;
>> + die_bits >>= df_cfg.die_id_shift;
>> + die_bits -= 4;
>> +
>> + coh_st_id |= (die_bits & mask) << num_intlv_bits;
>> + num_intlv_bits += num_die_intlv_bits;
>> + }
>> +
>> + /* Socket interleave bits */
>> + if (num_socket_intlv_bits) {
>> + u16 socket_bits;
>> +
>> + mask = GENMASK(num_socket_intlv_bits - 1, 0);
>> + socket_bits = ctx->coh_st_fabric_id & df_cfg.socket_id_mask;
>> + socket_bits >>= df_cfg.socket_id_shift;
>> +
>> + coh_st_id |= (socket_bits & mask) << num_intlv_bits;
>> + }
> If die or socket interleaving is enabled alongside HYGON_DF1_3CHAN mode, does
> coh_st_id bitwise overlap with the uncleared physical address? The upper bits
> of coh_st_id will be set here, but the physical address was multiplied by 3
> and never shifted left to make space for these additional bits.
> [ ... ]
>> +static u64 hygon_insert_coh_st_id_df1_3chan(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id)
>> +{
>> + u8 num_intlv_bits_chan = order_base_2(ctx->map.num_intlv_chan);
>> + u64 temp_addr = ((ctx->ret_addr >> num_intlv_bits_chan) << num_intlv_bits_chan) | coh_st_id;
> Since num_intlv_bits_chan is 2 for 3-channel mode, are we missing the space
> needed for the die and socket bits when we apply | coh_st_id here?
As HYGON_DF1_3CHAN mode will not co-exist with die interleave or socket
interleave mode, the above comments should not be an issue.
>> +static int hygon_denorm_addr_common(struct addr_ctx *ctx)
>> +{
>> + u64 denorm_addr;
>> + u16 coh_st_id;
>> +
>> + denorm_addr = hygon_make_space_for_coh_st_id(ctx);
>> + coh_st_id = hygon_calculate_coh_st_id(ctx);
>> + ctx->ret_addr = hygon_insert_coh_st_id(ctx, denorm_addr, coh_st_id);
>> + return 0;
>> +}
> If hygon_calculate_coh_st_id() encounters an error and returns ~0, is it safe
> to pass this directly into hygon_insert_coh_st_id() and then return 0 for
> success?
> It looks like the ~0 will be truncated to 0xFFFF as a u16, bitwise OR'd
> directly into the physical address, and then the caller will trust the
> success return code, leading to incorrect page offlining operations.
Will fix in RFC V2.
> Patch 4: [RFC PATCH 04/10] ras/amd/atl: Add Hygon DF1 address dehash helper
> Result:
> Critical: 0 · High: 0 · Medium: 0 · Low: 1
>> +static int hygon_df_2chan_dehash_addr(struct addr_ctx *ctx)
>> +{
>> + u8 hashed_bit;
>> + u8 intlv_bit_pos = ctx->map.intlv_bit_pos;
>> +
>> + hashed_bit = (ctx->ret_addr >> 12) ^
>> + (ctx->ret_addr >> 18) ^
>> + (ctx->ret_addr >> 21) ^
>> + (ctx->ret_addr >> 30) ^
>> + ctx->coh_st_fabric_id;
>> +
>> + hashed_bit &= BIT(0);
>> + if (hashed_bit != ((ctx->ret_addr >> intlv_bit_pos) & BIT(0)))
>> + ctx->ret_addr ^= BIT(intlv_bit_pos);
> Should this code use BIT_ULL() instead of BIT()?
> While BIT() expands to an unsigned long (which happens to be 64-bit on x86_64),
> ctx->ret_addr is explicitly defined as a u64 variable. Using BIT_ULL() here
> would ensure the mask is always explicitly 64-bit, which safely guards against
> future changes and maintains consistency with the rest of the AMD ATL
> subsystem.
Will fix in RFC V2.
> Patch 5: [RFC PATCH 05/10] ras/amd/atl: Add Hygon DF1 normalized-to-system address translation
> Result:
> Critical: 0 · High: 1 · Medium: 0 · Low: 0
>> +unsigned long hygon_norm_to_sys_addr(u8 socket_id, u8 die_id, u8 coh_st_inst_id,
>> + u8 sub_channel, unsigned long addr)
>> +{
>> + struct addr_ctx ctx;
>> +
>> + if (df_cfg.rev == UNKNOWN)
>> + return -EINVAL;
>> +
>> + memset(&ctx, 0, sizeof(ctx));
>> +
>> + /* Start from the normalized address */
>> + ctx.ret_addr = addr;
>> + ctx.node_id = die_id;
>> + ctx.inst_id = coh_st_inst_id;
>> +
>> + ctx.inputs.norm_addr = addr;
>> + ctx.inputs.socket_id = socket_id;
>> + ctx.inputs.die_id = die_id;
>> + ctx.inputs.coh_st_inst_id = coh_st_inst_id;
>> +
>> + ctx.chan_intlv_hygon.sub_channel = sub_channel;
>> +
>> + if (legacy_hole_en(&ctx) && !df_cfg.dram_hole_base)
>> + return -EINVAL;
> Since ctx was just initialized with memset(), ctx.map hasn't been populated
> yet. Will legacy_hole_en() always read 0 for ctx.map.base and return false?
> If so, does this bypass the misconfiguration check?
>> +
>> + if (hygon_get_address_map(&ctx))
>> + return -EINVAL;
>> +
>> + if (hygon_denormalize_address(&ctx))
>> + return -EINVAL;
>> +
>> + ctx.ret_addr = add_base_and_hole(&ctx, ctx.ret_addr);
> Later, when ctx.map is populated, if the legacy hole is actually enabled in
> hardware but left unconfigured (df_cfg.dram_hole_base is 0), could
> add_base_and_hole() incorrectly shift the physical address by 4GB?
> If that happens, can this lead to a corrupted system physical address being
> returned, causing the machine check handler to offline a healthy memory page
> and leave the faulty page active?
> Would it be better to move the legacy hole validation check after
> hygon_get_address_map() has populated ctx.map?
Will fix in RFC V2.
> Patch 7: [RFC PATCH 07/10] ras/amd/atl: Add Hygon DF discovery and MCA decode at initialization
> Result:
> Critical: 0 · High: 1 · Medium: 1 · Low: 0
> commit 4b3cc464e5cc696659d9b1d2ba8977f4028f70ca
> Author: Aichun Shi <shiaichun@open-hieco.net>
> ras/amd/atl: Add Hygon DF discovery and MCA decode at initialization
> This commit adds node number bounds, Data Fabric discovery, and UMC MCA address
> translation support for Hygon systems in the AMD Address Translation Library.
>> - Add Hygon node number support in amd_atl_init(),
>> amd_atl_exit() and __df_indirect_read().
> The commit message mentions adding support in amd_atl_exit(), but there do not
> appear to be any modifications to that function in this patch. Is this bullet
> point out of date?
Will fix in RFC V2.
>> struct pci_dev *F4;
>> int err = -ENODEV;
>> u32 ficaa = 0;
>> + u16 num_nodes;
>> + bool is_hygon = boot_cpu_data.x86_vendor == X86_VENDOR_HYGON;
>>
>> node = get_accessible_node(node);
>> - if (node >= amd_nb_num()) {
>> + if (is_hygon)
>> + num_nodes = amd_num_nodes();
>> + else
>> + num_nodes = amd_nb_num();
>> +
>> + if (node >= num_nodes) {
>> pr_debug("Node %u is out of bounds\n", node);
>> goto out;
>> }
> Can this result in a NULL pointer dereference just after this check?
> The code immediately follows this bounds check with:
> F4 = node_to_amd_nb(node)->link;
> If northbridge initialization partially or fully fails, amd_num_nodes() can
> return a value greater than amd_nb_num(). If node is greater than or equal to
> amd_nb_num(), node_to_amd_nb(node) will return NULL, causing the ->link access
> to crash.
> [ ... ]
>> diff --git a/drivers/ras/amd/atl/core.c b/drivers/ras/amd/atl/core.c
>> index b7083588b46ea..bbbbff4d43a3a 100644
>> --- a/drivers/ras/amd/atl/core.c
>> +++ b/drivers/ras/amd/atl/core.c
>> @@ -195,22 +195,37 @@ MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
>> static int __init amd_atl_init(void)
>> {
>> int ret;
>> + u16 num_nodes;
>> + bool is_hygon = boot_cpu_data.x86_vendor == X86_VENDOR_HYGON;
>>
>> if (!x86_match_cpu(amd_atl_cpuids))
>> return -ENODEV;
>>
>> - if (!amd_nb_num())
>> + if (is_hygon)
>> + num_nodes = amd_num_nodes();
>> + else
>> + num_nodes = amd_nb_num();
>> +
>> + if (!num_nodes)
>> return -ENODEV;
> Does this allow the module to incorrectly initialize when no northbridges are
> found?
> Previously, if amd_nb_num() was 0, it would safely return -ENODEV. By checking
> amd_num_nodes() instead for Hygon systems, the initialization could proceed
> even if northbridge initialization failed, potentially leading to similar
> NULL pointer dereferences when node_to_amd_nb() is called later in the code.
Will fix it in RFC V2 with hygon_cdd_num() instead of amd_num_nodes()
exported from Lin's Hygon Node RFC V2:
https://lore.kernel.org/lkml/20260423060420.1785357-3-wanglin@open-hieco.net/
> Patch 8: [RFC PATCH 08/10] ras/amd/atl: Add Hygon DF2 address translation support
> Result:
> Critical: 0 · High: 2 · Medium: 0 · Low: 1
>> @@ -117,6 +119,7 @@ static u16 hygon_calculate_coh_st_id(struct addr_ctx *ctx)
>> case NOHASH_16CHAN:
>> case NOHASH_32CHAN:
>> case DF2_2CHAN_HASH:
>> + case HYGON_DF2_4CHAN_HASH:
>> return hygon_get_coh_st_id_df(ctx);
>>
>> default:
> If HYGON_DF2_4CHAN_HASH is added here, does it also need to be handled in
> hygon_make_space_for_coh_st_id() and hygon_insert_coh_st_id() which also use
> a switch on ctx->map.intlv_mode?
> Without adding it to those functions, the switch will fall through to the
> default case, logging an "Unrecognized interleave mode" debug message and
> returning ~0ULL. This propagates into ctx->ret_addr causing subsequent bounds
> checks like addr_over_limit() to fail, which prevents translation of the MCA
> error address.
Will fix in RFC V2.
>
>> diff --git a/drivers/ras/amd/atl/hygon/map.c b/drivers/ras/amd/atl/hygon/map.c
>> index 2194c10ff5877..b0c05532b7449 100644
>> --- a/drivers/ras/amd/atl/hygon/map.c
>> +++ b/drivers/ras/amd/atl/hygon/map.c
>> @@ -16,8 +16,12 @@ static int hygon_df_get_intlv_mode(struct addr_ctx *ctx)
>> if (df_cfg.rev == HYGON_DF1 && ctx->map.intlv_mode == 2)
>> ctx->map.intlv_mode = HYGON_DF1_3CHAN;
>>
>> - if (ctx->map.intlv_mode == 8)
>> - ctx->map.intlv_mode = DF2_2CHAN_HASH;
>> + if (ctx->map.intlv_mode == 8) {
>> + if (df_cfg.rev == HYGON_DF2)
>> + ctx->map.intlv_mode = HYGON_DF2_4CHAN_HASH;
>> + else
>> + ctx->map.intlv_mode = DF2_2CHAN_HASH;
>> + }
> This isn't a bug introduced by this patch, but does the pre-existing
> DF2_2CHAN_HASH mode map to 0 channels in hygon_get_num_intlv_chan() due to
> being missing from its switch statement?
> If it falls through to the default case and returns 0, order_base_2(0) will
> evaluate to 0 in hygon_get_coh_st_id_df(), resulting in GENMASK(-1, 0).
> This can trigger undefined behavior and break Coherent Station ID
> calculations entirely for these systems.
Will fix in Patch 2 in RFC V2.
>> if (ctx->map.intlv_mode != NONE &&
>> ctx->map.intlv_mode != NOHASH_2CHAN &&
>> @@ -25,6 +29,7 @@ static int hygon_df_get_intlv_mode(struct addr_ctx *ctx)
>> ctx->map.intlv_mode != NOHASH_4CHAN &&
>> ctx->map.intlv_mode != NOHASH_8CHAN &&
>> ctx->map.intlv_mode != NOHASH_16CHAN &&
>> + ctx->map.intlv_mode != HYGON_DF2_4CHAN_HASH &&
>> ctx->map.intlv_mode != DF2_2CHAN_HASH)
>> return -EINVAL;
> If the hardware register reads a raw value of 9, could it bypass this
> validation?
> Since HYGON_DF1_INTLV_NUM_CHAN is a 4-bit hardware field, a value of 9 would
> skip the remapping block above. But because HYGON_DF2_4CHAN_HASH is defined
> as 0x09 in the intlv_modes enum, the validation check here will evaluate to
> false, mistakenly accepting an invalid hardware state as 4-channel hashing.
>> diff --git a/drivers/ras/amd/atl/internal.h b/drivers/ras/amd/atl/internal.h
>> index f943cc33f82e0..c368559ca2ae5 100644
>> --- a/drivers/ras/amd/atl/internal.h
>> +++ b/drivers/ras/amd/atl/internal.h
> [ ... ]
>> @@ -61,6 +62,7 @@ enum intlv_modes {
>> DF3_6CHAN = 0x06,
>> NOHASH_16CHAN = 0x07,
>> NOHASH_32CHAN = 0x08,
>> + HYGON_DF2_4CHAN_HASH = 0x09,
>> DF3_COD4_2CHAN_HASH = 0x0C,
> Could this enum value be moved to a value like those > 0x20 to prevent the
> aliasing with raw hardware field values described above?
Will fix in RFC V2.
> Patch 9: [RFC PATCH 09/10] ras/amd/atl: Add Hygon DF3 address translation support
> Result:
> Critical: 0 · High: 1 · Medium: 1 · Low: 0
>> diff --git a/drivers/ras/amd/atl/hygon/map.c b/drivers/ras/amd/atl/hygon/map.c
>> index b0c05532b7449..2a3d962be083e 100644
>> --- a/drivers/ras/amd/atl/hygon/map.c
>> +++ b/drivers/ras/amd/atl/hygon/map.c
>> @@ -17,7 +17,7 @@ static int hygon_df_get_intlv_mode(struct addr_ctx *ctx)
>> ctx->map.intlv_mode = HYGON_DF1_3CHAN;
>>
>> if (ctx->map.intlv_mode == 8) {
>> - if (df_cfg.rev == HYGON_DF2)
>> + if (df_cfg.rev == HYGON_DF2 || df_cfg.rev == HYGON_DF3)
>> ctx->map.intlv_mode = HYGON_DF2_4CHAN_HASH;
> Since HYGON_DF3 explicitly maps to the HYGON_DF2_4CHAN_HASH interleave
> mode here, does it also share the 4-channel hashing and sub-channel
> architecture of DF2?
> If so, does hygon_get_coh_st_inst_id() in drivers/ras/amd/atl/hygon/umc.c
> need to be updated?
> Currently, it only checks df_cfg.rev == HYGON_DF2 to shift the
> coh_st_inst_id and add the sub_channel. Without an update there, could
> MCA physical address translation fail or return an incorrect address for
> HYGON_DF3?
Will fix in RFC V2.
>> diff --git a/drivers/ras/amd/atl/hygon/system.c b/drivers/ras/amd/atl/hygon/system.c
>> index a06a39bc45276..f3ca6f58d00f5 100644
>> --- a/drivers/ras/amd/atl/hygon/system.c
>> +++ b/drivers/ras/amd/atl/hygon/system.c
> [ ... ]
>> @@ -38,6 +44,8 @@ static int hygon_determine_df_rev(void)
>> df_cfg.rev = HYGON_DF1;
>> else if (boot_cpu_data.x86_model == 0x6 || boot_cpu_data.x86_model == 0x8)
>> df_cfg.rev = HYGON_DF2;
>> + else if (boot_cpu_data.x86_model == 0x7)
>> + df_cfg.rev = HYGON_DF3;
> Does this logic need to verify the CPU family (boot_cpu_data.x86 == 0x18)
> alongside the model number?
> While Hygon might only have Family 0x18 right now, if a future CPU
> family introduces a model 0x7, could it falsely be detected as HYGON_DF3
> since model numbers can overlap across different families?
Will fix in RFC V2.
> Patch 10: [RFC PATCH 10/10] EDAC/amd64: Add Hygon Family 0x18 models 0x4-0x8 support
> Result:
> Critical: 1 · High: 2 · Medium: 1 · Low: 1
>> @@ -98,6 +99,29 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset,
>> return pcibios_err_to_errno(err);
>> }
>>
>> +static u32 get_umc_base_addr(struct amd64_pvt *pvt, u8 channel)
>> +{
>> + if (hygon_f18h_m4h()) {
>> + struct pci_dev *dev = node_to_amd_nb(pvt->mc_node_id)->misc;
> Could this use the already stored misc device pointer pvt->F3 instead of
> looking it up globally again?
Will fix in RFC V2.
>> + u8 df_id;
>> +
>> + hygon_get_dfid(dev, &df_id);
> If hygon_get_dfid() encounters an error and returns without populating the
> out-parameter, does this leave df_id uninitialized, leading to a regression
> during the umc base calculation?
Will fix in RFC V2.
>> +
>> + return get_umc_base(channel) + (0x80000000 + (0x10000000 * (df_id - 4)));
> Could this cause a signed integer overflow regression if df_id is 12 or
> greater?
> The literal 0x10000000 is treated as a signed integer. Multiplying it by 8
> or more evaluates to 2,147,483,648, which exceeds the maximum signed 32-bit
> integer limit.
> Would adding an unsigned suffix like 0x10000000U prevent this?
Will fix in RFC V2.
>> @@ -4143,13 +4185,15 @@ static int __init amd64_edac_init(void)
>> if (!x86_match_cpu(amd64_cpuids))
>> return -ENODEV;
>>
>> - if (!amd_nb_num())
>> + node_num = get_num_nodes();
>> +
>> + if (!node_num)
>> return -ENODEV;
> If northbridge caching fails during boot, amd_northbridges.num is 0, and
> amd_nb_num() correctly returns 0 to abort the driver load. By returning
> amd_num_nodes() for Hygon systems here, does this bypass the failure state
> check and force execution into the probe_one_instance() loop?
> If execution proceeds when initialization has failed, node_to_amd_nb(nid)
> will evaluate to null since nid >= amd_northbridges.num. Can this cause a
> null pointer dereference regression when the driver subsequently accesses
> the misc field?
>> opstate_init();
>>
>> err = -ENOMEM;
>> - ecc_stngs = kzalloc_objs(ecc_stngs[0], amd_nb_num());
>> + ecc_stngs = kzalloc_objs(ecc_stngs[0], node_num);
>> if (!ecc_stngs)
>> goto err_free;
Will fix it in RFC V2 with hygon_cdd_num() instead of amd_num_nodes()
exported from Lin's Hygon Node RFC V2:
https://lore.kernel.org/lkml/20260423060420.1785357-3-wanglin@open-hieco.net/
>> @@ -4213,7 +4257,7 @@ static void __exit amd64_edac_exit(void)
>> else
>> amd_unregister_ecc_decoder(decode_bus_error);
>>
>> - for (i = 0; i < amd_nb_num(); i++)
>> + for (i = 0; i < get_num_nodes(); i++)
>> remove_one_instance(i);
> This isn't a bug introduced by this commit, but is there a race condition
> during module unload?
> When amd_unregister_ecc_decoder() clears the callback pointer, could
> concurrently executing exception handlers still be running? If so,
> remove_one_instance() might free the memory controller instances while
> the handler is actively reading from them, potentially resulting in a
> use-after-free regression.
May consider it in RFC V2.
Best Regards,
Aichun
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-05-21 1:46 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-03 10:57 [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 01/10] ras/amd/atl: Add Hygon DF1 Data Fabric system information helper Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 02/10] ras/amd/atl: Add Hygon DF1 DRAM address map decoding helper Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 03/10] ras/amd/atl: Add Hygon DF1 normalized address denormalization helper Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 04/10] ras/amd/atl: Add Hygon DF1 address dehash helper Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 05/10] ras/amd/atl: Add Hygon DF1 normalized-to-system address translation Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 06/10] ras/amd/atl: Add Hygon UMC MCA to system address conversion support Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 07/10] ras/amd/atl: Add Hygon DF discovery and MCA decode at initialization Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 08/10] ras/amd/atl: Add Hygon DF2 address translation support Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 09/10] ras/amd/atl: Add Hygon DF3 " Aichun Shi
2026-04-03 10:57 ` [RFC PATCH 10/10] EDAC/amd64: Add Hygon Family 0x18 models 0x4-0x8 support Aichun Shi
2026-05-14 15:08 ` [RFC PATCH 00/10] EDAC/RAS: Hygon Family 0x18 UMC ECC address translation Borislav Petkov
2026-05-16 5:42 ` Aichun Shi
2026-05-21 1:46 ` Aichun Shi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®