From 8574034c13ca8286d9b1a0d5d0570ce8691bfde9 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Mon, 26 May 2025 16:26:56 +0800 Subject: [PATCH 1/3] EDAC/amd64: Rename the address translation function for hygon family 18h model 4h~10h commit 2fd0f2d3d438474350d001a8e5ef4304b0bd1bfa anolis. ANBZ: #21447 Use the original function umc_normaddr_to_sysaddr() for AMD processors and use the function hygon_umc_normaddr_to_sysaddr() for Hygon processors. Hygon-SIG: commit 2fd0f2d3d438 anolis EDAC/amd64: Rename the address translation function for hygon family 18h model 4h Backport to fix EDAC issue for Hygon Processors. Signed-off-by: Wenhui Fan Reviewed-by: Ruidong Tian Reviewed-by: Guixin Liu Link: https://gitee.com/anolis/cloud-kernel/pulls/5346 [ Aichun Shi: amend commit log and resolve the conflict ] Signed-off-by: Aichun Shi --- arch/x86/kernel/cpu/mce/amd.c | 68 ++-------- drivers/edac/amd64_edac.c | 246 ++++++++++++++++++++++++++++++++-- 2 files changed, 251 insertions(+), 63 deletions(-) diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c index c681f9266777..bd425e81c13a 100644 --- a/arch/x86/kernel/cpu/mce/amd.c +++ b/arch/x86/kernel/cpu/mce/amd.c @@ -705,12 +705,7 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) u32 tmp; - u8 die_id_shift, socket_id_shift; -#ifdef CONFIG_CPU_SUP_HYGON - u16 die_id_mask, socket_id_mask; -#else - u8 die_id_mask, socket_id_mask; -#endif + u8 die_id_shift, die_id_mask, socket_id_shift, socket_id_mask; u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets; u8 intlv_addr_sel, intlv_addr_bit; u8 num_intlv_bits, hashed_bit; @@ -718,11 +713,8 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) u8 cs_mask, cs_id = 0; bool hash_enabled = false; - /* Read DramOffset, check if base 1 is used. */ - if ((hygon_f18h_m4h() || hygon_f18h_m10h()) && - amd_df_indirect_read(nid, 0, 0x214, umc, &tmp)) - goto out_err; - else if (amd_df_indirect_read(nid, 0, 0x1B4, umc, &tmp)) + /* Read D18F0x1B4 (DramOffset), check if base 1 is used. */ + if (amd_df_indirect_read(nid, 0, 0x1B4, umc, &tmp)) goto out_err; /* Remove HiAddrOffset from normalized address, if enabled: */ @@ -746,9 +738,6 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) goto out_err; } - intlv_num_sockets = 0; - if (hygon_f18h_m4h() || hygon_f18h_m10h()) - intlv_num_sockets = (tmp >> 2) & 0x3; lgcy_mmio_hole_en = tmp & BIT(1); intlv_num_chan = (tmp >> 4) & 0xF; intlv_addr_sel = (tmp >> 8) & 0x7; @@ -765,20 +754,12 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) if (amd_df_indirect_read(nid, 0, 0x114 + (8 * base), umc, &tmp)) goto out_err; - if (!hygon_f18h_m4h() && !hygon_f18h_m10h()) - intlv_num_sockets = (tmp >> 8) & 0x1; + intlv_num_sockets = (tmp >> 8) & 0x1; intlv_num_dies = (tmp >> 10) & 0x3; dram_limit_addr = ((tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0); intlv_addr_bit = intlv_addr_sel + 8; - if ((hygon_f18h_m4h() && boot_cpu_data.x86_model >= 0x6) || - hygon_f18h_m10h()) { - if (amd_df_indirect_read(nid, 0, 0x60, umc, &tmp)) - goto out_err; - intlv_num_dies = tmp & 0x3; - } - /* Re-use intlv_num_chan by setting it equal to log2(#channels) */ switch (intlv_num_chan) { case 0: intlv_num_chan = 0; break; @@ -791,10 +772,6 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) hash_enabled = true; break; default: - if (hygon_f18h_m4h() && boot_cpu_data.x86_model == 0x4 && - intlv_num_chan == 2) - break; - pr_err("%s: Invalid number of interleaved channels %d.\n", __func__, intlv_num_chan); goto out_err; @@ -813,9 +790,8 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) /* Add a bit if sockets are interleaved. */ num_intlv_bits += intlv_num_sockets; - /* Assert num_intlv_bits in the correct range. */ - if ((hygon_f18h_m4h() && num_intlv_bits > 7) || - (!hygon_f18h_m4h() && num_intlv_bits > 4)) { + /* Assert num_intlv_bits <= 4 */ + if (num_intlv_bits > 4) { pr_err("%s: Invalid interleave bits %d.\n", __func__, num_intlv_bits); goto out_err; @@ -823,12 +799,7 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) if (num_intlv_bits > 0) { u64 temp_addr_x, temp_addr_i, temp_addr_y; - u8 die_id_bit, sock_id_bit; -#ifdef CONFIG_CPU_SUP_HYGON - u16 cs_fabric_id; -#else - u8 cs_fabric_id; -#endif + u8 die_id_bit, sock_id_bit, cs_fabric_id; /* * Read FabricBlockInstanceInformation3_CS[BlockFabricID]. @@ -839,10 +810,7 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) if (amd_df_indirect_read(nid, 0, 0x50, umc, &tmp)) goto out_err; - if (hygon_f18h_m4h() || hygon_f18h_m10h()) - cs_fabric_id = (tmp >> 8) & 0x7FF; - else - cs_fabric_id = (tmp >> 8) & 0xFF; + cs_fabric_id = (tmp >> 8) & 0xFF; die_id_bit = 0; /* If interleaved over more than 1 channel: */ @@ -862,26 +830,16 @@ int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) /* If interleaved over more than 1 die. */ if (intlv_num_dies) { sock_id_bit = die_id_bit + intlv_num_dies; - if (hygon_f18h_m4h()) { - die_id_shift = (tmp >> 12) & 0xF; - die_id_mask = tmp & 0x7FF; - cs_id |= (((cs_fabric_id & die_id_mask) >> die_id_shift) - 4) << - die_id_bit; - } else { - die_id_shift = (tmp >> 24) & 0xF; - die_id_mask = (tmp >> 8) & 0xFF; - cs_id |= ((cs_fabric_id & die_id_mask) >> die_id_shift) << - die_id_bit; - } + die_id_shift = (tmp >> 24) & 0xF; + die_id_mask = (tmp >> 8) & 0xFF; + + cs_id |= ((cs_fabric_id & die_id_mask) >> die_id_shift) << die_id_bit; } /* If interleaved over more than 1 socket. */ if (intlv_num_sockets) { socket_id_shift = (tmp >> 28) & 0xF; - if (hygon_f18h_m4h()) - socket_id_mask = (tmp >> 16) & 0x7FF; - else - socket_id_mask = (tmp >> 16) & 0xFF; + socket_id_mask = (tmp >> 16) & 0xFF; cs_id |= ((cs_fabric_id & socket_id_mask) >> socket_id_shift) << sock_id_bit; } diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index cac8c8f74a3f..9454e530dbdf 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -708,6 +708,230 @@ static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) return csrow; } +struct addr_ctx { + u64 ret_addr; + u32 tmp; + u16 nid; + u8 inst_id; +}; + +static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) +{ + u64 dram_base_addr, dram_limit_addr, dram_hole_base; + + u16 die_id_mask, socket_id_mask, cs_id = 0; + u8 die_id_shift, socket_id_shift; + u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets; + u8 intlv_addr_sel, intlv_addr_bit; + u8 num_intlv_bits, hashed_bit; + u8 lgcy_mmio_hole_en, base = 0; + u8 cs_mask; + bool hash_enabled = false; + + struct addr_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + + /* Start from the normalized address */ + ctx.ret_addr = norm_addr; + + ctx.nid = nid; + ctx.inst_id = umc; + + /* Read DramOffset, check if base 1 is used. */ + if (amd_df_indirect_read(nid, 0, 0x214, umc, &ctx.tmp)) + goto out_err; + + /* Remove HiAddrOffset from normalized address, if enabled: */ + if (ctx.tmp & BIT(0)) { + u64 hi_addr_offset = (ctx.tmp & GENMASK_ULL(31, 20)) << 8; + + if (norm_addr >= hi_addr_offset) { + ctx.ret_addr -= hi_addr_offset; + base = 1; + } + } + + /* Read D18F0x110 (DramBaseAddress). */ + if (amd_df_indirect_read(nid, 0, 0x110 + (8 * base), umc, &ctx.tmp)) + goto out_err; + + /* Check if address range is valid. */ + if (!(ctx.tmp & BIT(0))) { + pr_err("%s: Invalid DramBaseAddress range: 0x%x.\n", + __func__, ctx.tmp); + goto out_err; + } + + intlv_num_sockets = (ctx.tmp >> 2) & 0x3; + lgcy_mmio_hole_en = ctx.tmp & BIT(1); + intlv_num_chan = (ctx.tmp >> 4) & 0xF; + intlv_addr_sel = (ctx.tmp >> 8) & 0x7; + dram_base_addr = (ctx.tmp & GENMASK_ULL(31, 12)) << 16; + + /* {0, 1, 2, 3} map to address bits {8, 9, 10, 11} respectively */ + if (intlv_addr_sel > 3) { + pr_err("%s: Invalid interleave address select %d.\n", + __func__, intlv_addr_sel); + goto out_err; + } + + /* Read D18F0x114 (DramLimitAddress). */ + if (amd_df_indirect_read(nid, 0, 0x114 + (8 * base), umc, &ctx.tmp)) + goto out_err; + + intlv_num_dies = (ctx.tmp >> 10) & 0x3; + dram_limit_addr = ((ctx.tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0); + + intlv_addr_bit = intlv_addr_sel + 8; + + if (boot_cpu_data.x86_model >= 0x6) { + if (amd_df_indirect_read(nid, 0, 0x60, umc, &ctx.tmp)) + goto out_err; + intlv_num_dies = ctx.tmp & 0x3; + } + + /* Re-use intlv_num_chan by setting it equal to log2(#channels) */ + switch (intlv_num_chan) { + case 0: intlv_num_chan = 0; break; + case 1: intlv_num_chan = 1; break; + case 3: intlv_num_chan = 2; break; + case 5: intlv_num_chan = 3; break; + case 7: intlv_num_chan = 4; break; + + case 8: intlv_num_chan = 1; + hash_enabled = true; + break; + default: + if (boot_cpu_data.x86_model == 0x4 && + intlv_num_chan == 2) + break; + pr_err("%s: Invalid number of interleaved channels %d.\n", + __func__, intlv_num_chan); + goto out_err; + } + + num_intlv_bits = intlv_num_chan; + + if (intlv_num_dies > 2) { + pr_err("%s: Invalid number of interleaved nodes/dies %d.\n", + __func__, intlv_num_dies); + goto out_err; + } + + num_intlv_bits += intlv_num_dies; + + /* Add a bit if sockets are interleaved. */ + num_intlv_bits += intlv_num_sockets; + + /* Assert num_intlv_bits in the correct range. */ + if (num_intlv_bits > 7) { + pr_err("%s: Invalid interleave bits %d.\n", + __func__, num_intlv_bits); + goto out_err; + } + + if (num_intlv_bits > 0) { + u64 temp_addr_x, temp_addr_i, temp_addr_y; + u8 die_id_bit, sock_id_bit; + u16 cs_fabric_id; + + /* + * Read FabricBlockInstanceInformation3_CS[BlockFabricID]. + * This is the fabric id for this coherent slave. Use + * umc/channel# as instance id of the coherent slave + * for FICAA. + */ + if (amd_df_indirect_read(nid, 0, 0x50, umc, &ctx.tmp)) + goto out_err; + + cs_fabric_id = (ctx.tmp >> 8) & 0x7FF; + die_id_bit = 0; + + /* If interleaved over more than 1 channel: */ + if (intlv_num_chan) { + die_id_bit = intlv_num_chan; + cs_mask = (1 << die_id_bit) - 1; + cs_id = cs_fabric_id & cs_mask; + } + + sock_id_bit = die_id_bit; + + /* Read D18F1x208 (SystemFabricIdMask). */ + if (intlv_num_dies || intlv_num_sockets) + if (amd_df_indirect_read(nid, 1, 0x208, umc, &ctx.tmp)) + goto out_err; + + /* If interleaved over more than 1 die. */ + if (intlv_num_dies) { + sock_id_bit = die_id_bit + intlv_num_dies; + die_id_shift = (ctx.tmp >> 12) & 0xF; + die_id_mask = ctx.tmp & 0x7FF; + + cs_id |= (((cs_fabric_id & die_id_mask) >> die_id_shift) - 4) << + die_id_bit; + } + + /* If interleaved over more than 1 socket. */ + if (intlv_num_sockets) { + socket_id_shift = (ctx.tmp >> 28) & 0xF; + socket_id_mask = (ctx.tmp >> 16) & 0x7FF; + + cs_id |= ((cs_fabric_id & socket_id_mask) >> socket_id_shift) << sock_id_bit; + } + + /* + * The pre-interleaved address consists of XXXXXXIIIYYYYY + * where III is the ID for this CS, and XXXXXXYYYYY are the + * address bits from the post-interleaved address. + * "num_intlv_bits" has been calculated to tell us how many "I" + * bits there are. "intlv_addr_bit" tells us how many "Y" bits + * there are (where "I" starts). + */ + temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); + temp_addr_i = (cs_id << intlv_addr_bit); + temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits; + ctx.ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; + } + + /* Add dram base address */ + ctx.ret_addr += dram_base_addr; + + /* If legacy MMIO hole enabled */ + if (lgcy_mmio_hole_en) { + if (amd_df_indirect_read(nid, 0, 0x104, umc, &ctx.tmp)) + goto out_err; + + dram_hole_base = ctx.tmp & GENMASK(31, 24); + if (ctx.ret_addr >= dram_hole_base) + ctx.ret_addr += (BIT_ULL(32) - dram_hole_base); + } + + if (hash_enabled) { + /* Save some parentheses and grab ls-bit at the end. */ + hashed_bit = (ctx.ret_addr >> 12) ^ + (ctx.ret_addr >> 18) ^ + (ctx.ret_addr >> 21) ^ + (ctx.ret_addr >> 30) ^ + cs_id; + + hashed_bit &= BIT(0); + + if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0))) + ctx.ret_addr ^= BIT(intlv_addr_bit); + } + + /* Is calculated system address is above DRAM limit address? */ + if (ctx.ret_addr > dram_limit_addr) + goto out_err; + + *sys_addr = ctx.ret_addr; + return 0; + +out_err: + return -EINVAL; +} + static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); /* @@ -2529,15 +2753,21 @@ static void decode_umc_error(int node_id, struct mce *m) err.csrow = m->synd & 0x7; - if ((hygon_f18h_m4h() && boot_cpu_data.x86_model >= 0x6) || - hygon_f18h_m10h()) - umc = (err.channel << 1) + ((m->ipid & BIT(13)) >> 13); - else - umc = err.channel; + if (hygon_f18h_m4h() || hygon_f18h_m10h()) { + if(boot_cpu_data.x86_model >= 0x6) + umc = (err.channel << 1) + ((m->ipid & BIT(13)) >> 13); + else + umc = err.channel; - if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, umc, &sys_addr)) { - err.err_code = ERR_NORM_ADDR; - goto log_error; + if (hygon_umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, umc, &sys_addr)) { + err.err_code = ERR_NORM_ADDR; + goto log_error; + } + } else { + if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, err.channel, &sys_addr)) { + err.err_code = ERR_NORM_ADDR; + goto log_error; + } } error_address_to_page_and_offset(sys_addr, &err); -- Gitee From 5ea95d5d66262774b5e79b8c6dddcd4a904ce1d9 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Mon, 26 May 2025 16:52:21 +0800 Subject: [PATCH 2/3] EDAC/amd64: Correct the address translation for hygon family 18h model 4h commit d15e452ea7ce67772d078158c676ddd5b9cd903d anolis. ANBZ: #21447 For Hygon family 18h model 4h processor, the normal address does not contain sub-channel bit, so it need to reserve the bit field for sub-channel and fill it in the address translating process. In the 3 channels interleaving scenario, the calculation of cs id is different from other scenarios. Hygon-SIG: commit d15e452ea7ce anolis EDAC/amd64: Correct the address translation for hygon family 18h model 4h Backport to fix EDAC issue for Hygon Processors. Fixes: 2c453f4b4ef5 ("EDAC/amd64: Adjust address translation for Hygon family 18h model 4h") Signed-off-by: Wenhui Fan Reviewed-by: Ruidong Tian Reviewed-by: Guixin Liu Link: https://gitee.com/anolis/cloud-kernel/pulls/5346 [ Aichun Shi: amend commit log and resolve the conflict ] Signed-off-by: Aichun Shi --- drivers/edac/amd64_edac.c | 100 +++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 13 deletions(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 9454e530dbdf..5feae05cbbb1 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -715,14 +715,16 @@ struct addr_ctx { u8 inst_id; }; -static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) +static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, + u8 sub_channel, u64 *sys_addr) { u64 dram_base_addr, dram_limit_addr, dram_hole_base; - u16 die_id_mask, socket_id_mask, cs_id = 0; + u16 die_id_mask, socket_id_mask, dst_fabric_id, cs_id = 0; u8 die_id_shift, socket_id_shift; u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets; u8 intlv_addr_sel, intlv_addr_bit; + u8 chan_addr_sel, chan_hash_enable, ddr5_enable, start_bit; u8 num_intlv_bits, hashed_bit; u8 lgcy_mmio_hole_en, base = 0; u8 cs_mask; @@ -782,6 +784,8 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy intlv_num_dies = (ctx.tmp >> 10) & 0x3; dram_limit_addr = ((ctx.tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0); + if (boot_cpu_data.x86_model == 0x4) + dst_fabric_id = ctx.tmp & GENMASK_ULL(9, 0); intlv_addr_bit = intlv_addr_sel + 8; @@ -791,6 +795,31 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy intlv_num_dies = ctx.tmp & 0x3; } + if (boot_cpu_data.x86_model == 0x4) { + if (amd_df_indirect_read(nid, 2, 0x48, umc, &ctx.tmp)) + goto out_err; + chan_addr_sel = (ctx.tmp >> 24) & 0x1; + chan_hash_enable = (ctx.tmp >> 23) & 0x1; + ddr5_enable = (ctx.tmp >> 19) & 0x1; + if (ddr5_enable) { + u64 low_addr, high_addr; + + if (chan_addr_sel) + start_bit = 8; + else + start_bit = 7; + + low_addr = ctx.ret_addr & GENMASK_ULL(start_bit - 1, 0); + /* + * Reserve the sub-channel bit filed(ret_addr[start_bit]), + * and fill the sub-channel bit in the later channel hashing + * process. + */ + high_addr = (ctx.ret_addr & GENMASK_ULL(63, start_bit)) << 1; + ctx.ret_addr = high_addr | low_addr; + } + } + /* Re-use intlv_num_chan by setting it equal to log2(#channels) */ switch (intlv_num_chan) { case 0: intlv_num_chan = 0; break; @@ -832,7 +861,7 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy } if (num_intlv_bits > 0) { - u64 temp_addr_x, temp_addr_i, temp_addr_y; + u64 temp_addr_x, temp_addr_i, temp_addr_y, addr_mul3; u8 die_id_bit, sock_id_bit; u16 cs_fabric_id; @@ -851,8 +880,30 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy /* If interleaved over more than 1 channel: */ if (intlv_num_chan) { die_id_bit = intlv_num_chan; - cs_mask = (1 << die_id_bit) - 1; - cs_id = cs_fabric_id & cs_mask; + + /* + * In the 3 channels interleaving scenario, the calculation + * of cs id is different from other scenarios. + */ + if (boot_cpu_data.x86_model == 0x4 && intlv_num_chan == 2) { + u8 cs_offset; + + cs_offset = (cs_fabric_id & 0x3) - (dst_fabric_id & 0x3); + if (cs_offset > 3) { + pr_err("%s: Invalid cs offset: 0x%x cs_fabric_id: 0x%x dst_fabric_id: 0x%x.\n", + __func__, cs_offset, cs_fabric_id, dst_fabric_id); + goto out_err; + } + temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) >> + intlv_addr_bit; + temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); + + addr_mul3 = temp_addr_x * 3 + cs_offset; + cs_id = addr_mul3 & GENMASK_ULL(intlv_num_chan - 1, 0); + } else { + cs_mask = (1 << die_id_bit) - 1; + cs_id = cs_fabric_id & cs_mask; + } } sock_id_bit = die_id_bit; @@ -888,10 +939,16 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy * bits there are. "intlv_addr_bit" tells us how many "Y" bits * there are (where "I" starts). */ - temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); - temp_addr_i = (cs_id << intlv_addr_bit); - temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits; - ctx.ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; + if (boot_cpu_data.x86_model == 0x4 && intlv_num_chan == 2) { + temp_addr_i = ((addr_mul3 >> intlv_num_chan) << intlv_num_chan) | cs_id; + ctx.ret_addr = temp_addr_y | (temp_addr_i << intlv_addr_bit); + } else { + temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); + temp_addr_i = (cs_id << intlv_addr_bit); + temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << + num_intlv_bits; + ctx.ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; + } } /* Add dram base address */ @@ -921,6 +978,21 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy ctx.ret_addr ^= BIT(intlv_addr_bit); } + /* The channel hashing process. */ + if (boot_cpu_data.x86_model == 0x4 && ddr5_enable) { + 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; + } + } + /* Is calculated system address is above DRAM limit address? */ if (ctx.ret_addr > dram_limit_addr) goto out_err; @@ -2722,7 +2794,7 @@ static void decode_umc_error(int node_id, struct mce *m) struct amd64_pvt *pvt; struct err_info err; u64 sys_addr; - u8 umc; + u8 umc, sub_channel = 0; mci = edac_mc_find(node_id); if (!mci) @@ -2754,12 +2826,14 @@ static void decode_umc_error(int node_id, struct mce *m) err.csrow = m->synd & 0x7; if (hygon_f18h_m4h() || hygon_f18h_m10h()) { - if(boot_cpu_data.x86_model >= 0x6) - umc = (err.channel << 1) + ((m->ipid & BIT(13)) >> 13); + sub_channel = (m->ipid & BIT(13)) >> 13; + if (boot_cpu_data.x86_model >= 0x6) + umc = (err.channel << 1) + sub_channel; else umc = err.channel; - if (hygon_umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, umc, &sys_addr)) { + if (hygon_umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, umc, + sub_channel, &sys_addr)) { err.err_code = ERR_NORM_ADDR; goto log_error; } -- Gitee From 5c173bfae1ffa9bac38333f0345e7a6b6d455ee2 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Tue, 28 Oct 2025 14:11:03 +0800 Subject: [PATCH 3/3] EDAC/amd64: The width of hash value is 2 bits for Hygon family 18h model 6h processors commit 95e0df888d5d977c100b27b627b800a2a09baba8 anolis. ANBZ: #26500 It has 2 bits hash value when hash enabled for hygon family 18h model 6h. Hygon-SIG: commit none hygon anolis: EDAC/amd64: The width of hash value is 2 bits for Hygon family 18h model 6h processors Hygon-SIG: commit 95e0df888d5d anolis EDAC/amd64: The width of hash value is 2 bits for Hygon family 18h model 6h processors Backport to fix EDAC issue for Hygon Processors. Signed-off-by: Wenhui Fan Cc: hygon-arch@list.openanolis.cn Reviewed-by: Guixin Liu Link: https://gitee.com/anolis/cloud-kernel/pulls/5912 [ Aichun Shi: amend commit log ] Signed-off-by: Aichun Shi --- drivers/edac/amd64_edac.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 5feae05cbbb1..423258a3f0df 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -828,7 +828,11 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, case 5: intlv_num_chan = 3; break; case 7: intlv_num_chan = 4; break; - case 8: intlv_num_chan = 1; + case 8: + if (boot_cpu_data.x86_model >= 0x6) + intlv_num_chan = 2; + else + intlv_num_chan = 1; hash_enabled = true; break; default: @@ -972,10 +976,16 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, (ctx.ret_addr >> 30) ^ cs_id; - hashed_bit &= BIT(0); - - if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0))) - ctx.ret_addr ^= BIT(intlv_addr_bit); + if (boot_cpu_data.x86_model >= 0x6) { + hashed_bit &= 0x3; + if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & 0x3)) + ctx.ret_addr = (ctx.ret_addr & ~((u64)3 << intlv_addr_bit)) | + (hashed_bit << intlv_addr_bit); + } else { + hashed_bit &= BIT(0); + if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0))) + ctx.ret_addr ^= BIT(intlv_addr_bit); + } } /* The channel hashing process. */ -- Gitee