diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0035-x86-MCE-AMD-EDAC-amd64-Move-address-translation-to-A.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0035-x86-MCE-AMD-EDAC-amd64-Move-address-translation-to-A.patch | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0035-x86-MCE-AMD-EDAC-amd64-Move-address-translation-to-A.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0035-x86-MCE-AMD-EDAC-amd64-Move-address-translation-to-A.patch new file mode 100644 index 00000000..5b2343e0 --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0035-x86-MCE-AMD-EDAC-amd64-Move-address-translation-to-A.patch @@ -0,0 +1,466 @@ +From 8fe584b1af57f36e362a89c66d73f5273b8cd732 Mon Sep 17 00:00:00 2001 +From: Yazen Ghannam <yazen.ghannam@amd.com> +Date: Thu, 28 Oct 2021 17:56:56 +0000 +Subject: [PATCH 35/86] x86/MCE/AMD, EDAC/amd64: Move address translation to + AMD64 EDAC + +commit 0b746e8c1e1e3fcc9e4036efe8d3ea3fd3e5d4c3 upstream + +The address translation code used for current AMD systems is +non-architectural. So move it to EDAC. + +Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com> +Signed-off-by: Borislav Petkov <bp@suse.de> +Link: https://lkml.kernel.org/r/20211028175728.121452-2-yazen.ghannam@amd.com +Signed-off-by: Zhaolong Zhang <zhaolong.zhang@windriver.com> +--- + arch/x86/include/asm/mce.h | 3 - + arch/x86/kernel/cpu/mce/amd.c | 200 ---------------------------------- + drivers/edac/amd64_edac.c | 199 +++++++++++++++++++++++++++++++++ + 3 files changed, 199 insertions(+), 203 deletions(-) + +diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h +index d69f716d8bc5..4857d962f0fb 100644 +--- a/arch/x86/include/asm/mce.h ++++ b/arch/x86/include/asm/mce.h +@@ -357,7 +357,6 @@ extern int mce_threshold_create_device(unsigned int cpu); + extern int mce_threshold_remove_device(unsigned int cpu); + + void mce_amd_feature_init(struct cpuinfo_x86 *c); +-int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr); + enum smca_bank_types smca_get_bank_type(unsigned int bank); + #else + +@@ -365,8 +364,6 @@ static inline int mce_threshold_create_device(unsigned int cpu) { return 0; }; + static inline int mce_threshold_remove_device(unsigned int cpu) { return 0; }; + static inline bool amd_mce_is_memory_error(struct mce *m) { return false; }; + static inline void mce_amd_feature_init(struct cpuinfo_x86 *c) { } +-static inline int +-umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) { return -EINVAL; }; + #endif + + static inline void mce_hygon_feature_init(struct cpuinfo_x86 *c) { return mce_amd_feature_init(c); } +diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c +index 9f517009fd19..c0156c547e46 100644 +--- a/arch/x86/kernel/cpu/mce/amd.c ++++ b/arch/x86/kernel/cpu/mce/amd.c +@@ -689,206 +689,6 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c) + deferred_error_interrupt_enable(c); + } + +-int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) +-{ +- u64 dram_base_addr, dram_limit_addr, dram_hole_base; +- /* We start from the normalized address */ +- u64 ret_addr = norm_addr; +- +- u32 tmp; +- +- 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; +- u8 lgcy_mmio_hole_en, base = 0; +- u8 cs_mask, cs_id = 0; +- bool hash_enabled = false; +- +- /* 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: */ +- if (tmp & BIT(0)) { +- u64 hi_addr_offset = (tmp & GENMASK_ULL(31, 20)) << 8; +- +- if (norm_addr >= hi_addr_offset) { +- ret_addr -= hi_addr_offset; +- base = 1; +- } +- } +- +- /* Read D18F0x110 (DramBaseAddress). */ +- if (amd_df_indirect_read(nid, 0, 0x110 + (8 * base), umc, &tmp)) +- goto out_err; +- +- /* Check if address range is valid. */ +- if (!(tmp & BIT(0))) { +- pr_err("%s: Invalid DramBaseAddress range: 0x%x.\n", +- __func__, tmp); +- goto out_err; +- } +- +- lgcy_mmio_hole_en = tmp & BIT(1); +- intlv_num_chan = (tmp >> 4) & 0xF; +- intlv_addr_sel = (tmp >> 8) & 0x7; +- dram_base_addr = (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, &tmp)) +- goto out_err; +- +- 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; +- +- /* 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: +- 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 <= 4 */ +- if (num_intlv_bits > 4) { +- 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, 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, &tmp)) +- goto out_err; +- +- cs_fabric_id = (tmp >> 8) & 0xFF; +- 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, &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 = (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; +- socket_id_mask = (tmp >> 16) & 0xFF; +- +- 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 = ret_addr & GENMASK_ULL(intlv_addr_bit-1, 0); +- temp_addr_i = (cs_id << intlv_addr_bit); +- temp_addr_x = (ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits; +- ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; +- } +- +- /* Add dram base address */ +- ret_addr += dram_base_addr; +- +- /* If legacy MMIO hole enabled */ +- if (lgcy_mmio_hole_en) { +- if (amd_df_indirect_read(nid, 0, 0x104, umc, &tmp)) +- goto out_err; +- +- dram_hole_base = tmp & GENMASK(31, 24); +- if (ret_addr >= dram_hole_base) +- ret_addr += (BIT_ULL(32) - dram_hole_base); +- } +- +- if (hash_enabled) { +- /* Save some parentheses and grab ls-bit at the end. */ +- hashed_bit = (ret_addr >> 12) ^ +- (ret_addr >> 18) ^ +- (ret_addr >> 21) ^ +- (ret_addr >> 30) ^ +- cs_id; +- +- hashed_bit &= BIT(0); +- +- if (hashed_bit != ((ret_addr >> intlv_addr_bit) & BIT(0))) +- ret_addr ^= BIT(intlv_addr_bit); +- } +- +- /* Is calculated system address is above DRAM limit address? */ +- if (ret_addr > dram_limit_addr) +- goto out_err; +- +- *sys_addr = ret_addr; +- return 0; +- +-out_err: +- return -EINVAL; +-} +-EXPORT_SYMBOL_GPL(umc_normaddr_to_sysaddr); +- + bool amd_mce_is_memory_error(struct mce *m) + { + /* ErrCodeExt[20:16] */ +diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c +index 4fce75013674..d2ad9f06abb7 100644 +--- a/drivers/edac/amd64_edac.c ++++ b/drivers/edac/amd64_edac.c +@@ -988,6 +988,205 @@ static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr) + return csrow; + } + ++static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) ++{ ++ u64 dram_base_addr, dram_limit_addr, dram_hole_base; ++ /* We start from the normalized address */ ++ u64 ret_addr = norm_addr; ++ ++ u32 tmp; ++ ++ 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; ++ u8 lgcy_mmio_hole_en, base = 0; ++ u8 cs_mask, cs_id = 0; ++ bool hash_enabled = false; ++ ++ /* 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: */ ++ if (tmp & BIT(0)) { ++ u64 hi_addr_offset = (tmp & GENMASK_ULL(31, 20)) << 8; ++ ++ if (norm_addr >= hi_addr_offset) { ++ ret_addr -= hi_addr_offset; ++ base = 1; ++ } ++ } ++ ++ /* Read D18F0x110 (DramBaseAddress). */ ++ if (amd_df_indirect_read(nid, 0, 0x110 + (8 * base), umc, &tmp)) ++ goto out_err; ++ ++ /* Check if address range is valid. */ ++ if (!(tmp & BIT(0))) { ++ pr_err("%s: Invalid DramBaseAddress range: 0x%x.\n", ++ __func__, tmp); ++ goto out_err; ++ } ++ ++ lgcy_mmio_hole_en = tmp & BIT(1); ++ intlv_num_chan = (tmp >> 4) & 0xF; ++ intlv_addr_sel = (tmp >> 8) & 0x7; ++ dram_base_addr = (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, &tmp)) ++ goto out_err; ++ ++ 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; ++ ++ /* 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: ++ 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 <= 4 */ ++ if (num_intlv_bits > 4) { ++ 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, 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, &tmp)) ++ goto out_err; ++ ++ cs_fabric_id = (tmp >> 8) & 0xFF; ++ 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, &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 = (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; ++ socket_id_mask = (tmp >> 16) & 0xFF; ++ ++ 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 = ret_addr & GENMASK_ULL(intlv_addr_bit-1, 0); ++ temp_addr_i = (cs_id << intlv_addr_bit); ++ temp_addr_x = (ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits; ++ ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; ++ } ++ ++ /* Add dram base address */ ++ ret_addr += dram_base_addr; ++ ++ /* If legacy MMIO hole enabled */ ++ if (lgcy_mmio_hole_en) { ++ if (amd_df_indirect_read(nid, 0, 0x104, umc, &tmp)) ++ goto out_err; ++ ++ dram_hole_base = tmp & GENMASK(31, 24); ++ if (ret_addr >= dram_hole_base) ++ ret_addr += (BIT_ULL(32) - dram_hole_base); ++ } ++ ++ if (hash_enabled) { ++ /* Save some parentheses and grab ls-bit at the end. */ ++ hashed_bit = (ret_addr >> 12) ^ ++ (ret_addr >> 18) ^ ++ (ret_addr >> 21) ^ ++ (ret_addr >> 30) ^ ++ cs_id; ++ ++ hashed_bit &= BIT(0); ++ ++ if (hashed_bit != ((ret_addr >> intlv_addr_bit) & BIT(0))) ++ ret_addr ^= BIT(intlv_addr_bit); ++ } ++ ++ /* Is calculated system address is above DRAM limit address? */ ++ if (ret_addr > dram_limit_addr) ++ goto out_err; ++ ++ *sys_addr = ret_addr; ++ return 0; ++ ++out_err: ++ return -EINVAL; ++} ++ + static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16); + + /* +-- +2.37.3 + |