// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AMD Address Translation Library
*
* umc . c : Unified Memory Controller ( UMC ) topology helpers
*
* Copyright ( c ) 2023 , Advanced Micro Devices , Inc .
* All Rights Reserved .
*
* Author : Yazen Ghannam < Yazen . Ghannam @ amd . com >
*/
#include "internal.h"
/*
* MI300 has a fixed , model - specific mapping between a UMC instance and
* its related Data Fabric Coherent Station instance .
*
* The MCA_IPID_UMC [ InstanceId ] field holds a unique identifier for the
* UMC instance within a Node . Use this to find the appropriate Coherent
* Station ID .
*
* Redundant bits were removed from the map below .
*/
static const u16 umc_coh_st_map[32 ] = {
0 x393, 0 x293, 0 x193, 0 x093,
0 x392, 0 x292, 0 x192, 0 x092,
0 x391, 0 x291, 0 x191, 0 x091,
0 x390, 0 x290, 0 x190, 0 x090,
0 x793, 0 x693, 0 x593, 0 x493,
0 x792, 0 x692, 0 x592, 0 x492,
0 x791, 0 x691, 0 x591, 0 x491,
0 x790, 0 x690, 0 x590, 0 x490,
};
#define UMC_ID_MI300 GENMASK(23 , 12 )
static u8 get_coh_st_inst_id_mi300(struct atl_err *err)
{
u16 umc_id = FIELD_GET(UMC_ID_MI300, err->ipid);
u8 i;
for (i = 0 ; i < ARRAY_SIZE(umc_coh_st_map); i++) {
if (umc_id == umc_coh_st_map[i])
break ;
}
WARN_ON_ONCE(i >= ARRAY_SIZE(umc_coh_st_map));
return i;
}
/* XOR the bits in @val. */
static u16 bitwise_xor_bits(u16 val)
{
u16 tmp = 0 ;
u8 i;
for (i = 0 ; i < 16 ; i++)
tmp ^= (val >> i) & 0 x1;
return tmp;
}
struct xor_bits {
bool xor_enable;
u16 col_xor;
u32 row_xor;
};
#define NUM_BANK_BITS 4
#define NUM_COL_BITS 5
#define NUM_SID_BITS 2
static struct {
/* UMC::CH::AddrHashBank */
struct xor_bits bank[NUM_BANK_BITS];
/* UMC::CH::AddrHashPC */
struct xor_bits pc;
/* UMC::CH::AddrHashPC2 */
u8 bank_xor;
} addr_hash;
static struct {
u8 bank[NUM_BANK_BITS];
u8 col[NUM_COL_BITS];
u8 sid[NUM_SID_BITS];
u8 num_row_lo;
u8 num_row_hi;
u8 row_lo;
u8 row_hi;
u8 pc;
} bit_shifts;
#define MI300_UMC_CH_BASE 0 x90000
#define MI300_ADDR_CFG (MI300_UMC_CH_BASE + 0 x30)
#define MI300_ADDR_SEL (MI300_UMC_CH_BASE + 0 x40)
#define MI300_COL_SEL_LO (MI300_UMC_CH_BASE + 0 x50)
#define MI300_ADDR_SEL_2 (MI300_UMC_CH_BASE + 0 xA4)
#define MI300_ADDR_HASH_BANK0 (MI300_UMC_CH_BASE + 0 xC8)
#define MI300_ADDR_HASH_PC (MI300_UMC_CH_BASE + 0 xE0)
#define MI300_ADDR_HASH_PC2 (MI300_UMC_CH_BASE + 0 xE4)
#define ADDR_HASH_XOR_EN BIT(0 )
#define ADDR_HASH_COL_XOR GENMASK(13 , 1 )
#define ADDR_HASH_ROW_XOR GENMASK(31 , 14 )
#define ADDR_HASH_BANK_XOR GENMASK(5 , 0 )
#define ADDR_CFG_NUM_ROW_LO GENMASK(11 , 8 )
#define ADDR_CFG_NUM_ROW_HI GENMASK(15 , 12 )
#define ADDR_SEL_BANK0 GENMASK(3 , 0 )
#define ADDR_SEL_BANK1 GENMASK(7 , 4 )
#define ADDR_SEL_BANK2 GENMASK(11 , 8 )
#define ADDR_SEL_BANK3 GENMASK(15 , 12 )
#define ADDR_SEL_BANK4 GENMASK(20 , 16 )
#define ADDR_SEL_ROW_LO GENMASK(27 , 24 )
#define ADDR_SEL_ROW_HI GENMASK(31 , 28 )
#define COL_SEL_LO_COL0 GENMASK(3 , 0 )
#define COL_SEL_LO_COL1 GENMASK(7 , 4 )
#define COL_SEL_LO_COL2 GENMASK(11 , 8 )
#define COL_SEL_LO_COL3 GENMASK(15 , 12 )
#define COL_SEL_LO_COL4 GENMASK(19 , 16 )
#define ADDR_SEL_2_BANK5 GENMASK(4 , 0 )
#define ADDR_SEL_2_CHAN GENMASK(15 , 12 )
/*
* Read UMC : : CH : : AddrHash { Bank , PC , PC2 } registers to get XOR bits used
* for hashing .
*
* Also , read UMC : : CH : : Addr { Cfg , Sel , Sel2 } and UMC : : CH : ColSelLo registers to
* get the values needed to reconstruct the normalized address . Apply additional
* offsets to the raw register values , as needed .
*
* Do this during module init , since the values will not change during run time .
*
* These registers are instantiated for each UMC across each AMD Node .
* However , they should be identically programmed due to the fixed hardware
* design of MI300 systems . So read the values from Node 0 UMC 0 and keep a
* single global structure for simplicity .
*/
int get_umc_info_mi300(void )
{
u32 temp;
int ret;
u8 i;
for (i = 0 ; i < NUM_BANK_BITS; i++) {
ret = amd_smn_read(0 , MI300_ADDR_HASH_BANK0 + (i * 4 ), &temp);
if (ret)
return ret;
addr_hash.bank[i].xor_enable = FIELD_GET(ADDR_HASH_XOR_EN, temp);
addr_hash.bank[i].col_xor = FIELD_GET(ADDR_HASH_COL_XOR, temp);
addr_hash.bank[i].row_xor = FIELD_GET(ADDR_HASH_ROW_XOR, temp);
}
ret = amd_smn_read(0 , MI300_ADDR_HASH_PC, &temp);
if (ret)
return ret;
addr_hash.pc.xor_enable = FIELD_GET(ADDR_HASH_XOR_EN, temp);
addr_hash.pc.col_xor = FIELD_GET(ADDR_HASH_COL_XOR, temp);
addr_hash.pc.row_xor = FIELD_GET(ADDR_HASH_ROW_XOR, temp);
ret = amd_smn_read(0 , MI300_ADDR_HASH_PC2, &temp);
if (ret)
return ret;
addr_hash.bank_xor = FIELD_GET(ADDR_HASH_BANK_XOR, temp);
ret = amd_smn_read(0 , MI300_ADDR_CFG, &temp);
if (ret)
return ret;
bit_shifts.num_row_hi = FIELD_GET(ADDR_CFG_NUM_ROW_HI, temp);
bit_shifts.num_row_lo = 10 + FIELD_GET(ADDR_CFG_NUM_ROW_LO, temp);
ret = amd_smn_read(0 , MI300_ADDR_SEL, &temp);
if (ret)
return ret;
bit_shifts.bank[0 ] = 5 + FIELD_GET(ADDR_SEL_BANK0, temp);
bit_shifts.bank[1 ] = 5 + FIELD_GET(ADDR_SEL_BANK1, temp);
bit_shifts.bank[2 ] = 5 + FIELD_GET(ADDR_SEL_BANK2, temp);
bit_shifts.bank[3 ] = 5 + FIELD_GET(ADDR_SEL_BANK3, temp);
/* Use BankBit4 for the SID0 position. */
bit_shifts.sid[0 ] = 5 + FIELD_GET(ADDR_SEL_BANK4, temp);
bit_shifts.row_lo = 12 + FIELD_GET(ADDR_SEL_ROW_LO, temp);
bit_shifts.row_hi = 24 + FIELD_GET(ADDR_SEL_ROW_HI, temp);
ret = amd_smn_read(0 , MI300_COL_SEL_LO, &temp);
if (ret)
return ret;
bit_shifts.col[0 ] = 2 + FIELD_GET(COL_SEL_LO_COL0, temp);
bit_shifts.col[1 ] = 2 + FIELD_GET(COL_SEL_LO_COL1, temp);
bit_shifts.col[2 ] = 2 + FIELD_GET(COL_SEL_LO_COL2, temp);
bit_shifts.col[3 ] = 2 + FIELD_GET(COL_SEL_LO_COL3, temp);
bit_shifts.col[4 ] = 2 + FIELD_GET(COL_SEL_LO_COL4, temp);
ret = amd_smn_read(0 , MI300_ADDR_SEL_2, &temp);
if (ret)
return ret;
/* Use BankBit5 for the SID1 position. */
bit_shifts.sid[1 ] = 5 + FIELD_GET(ADDR_SEL_2_BANK5, temp);
bit_shifts.pc = 5 + FIELD_GET(ADDR_SEL_2_CHAN, temp);
return 0 ;
}
/*
* MI300 systems report a DRAM address in MCA_ADDR for DRAM ECC errors . This must
* be converted to the intermediate normalized address ( NA ) before translating to a
* system physical address .
*
* The DRAM address includes bank , row , and column . Also included are bits for
* pseudochannel ( PC ) and stack ID ( SID ) .
*
* Abbreviations : ( S ) tack ID , ( P ) seudochannel , ( R ) ow , ( B ) ank , ( C ) olumn , ( Z ) ero
*
* The MCA address format is as follows :
* MCA_ADDR [ 27 : 0 ] = { S [ 1 : 0 ] , P [ 0 ] , R [ 14 : 0 ] , B [ 3 : 0 ] , C [ 4 : 0 ] , Z [ 0 ] }
*
* Additionally , the PC and Bank bits may be hashed . This must be accounted for before
* reconstructing the normalized address .
*/
#define MI300_UMC_MCA_BANK GENMASK(9 , 6 )
#define MI300_UMC_MCA_ROW GENMASK(24 , 10 )
#define MI300_UMC_MCA_PC BIT(25 )
#define MI300_UMC_MCA_SID GENMASK(27 , 26 )
static unsigned long convert_dram_to_norm_addr_mi300(unsigned long addr)
{
u16 i, col, row, bank, pc, sid;
u32 temp;
col = FIELD_GET(MI300_UMC_MCA_COL, addr);
bank = FIELD_GET(MI300_UMC_MCA_BANK, addr);
row = FIELD_GET(MI300_UMC_MCA_ROW, addr);
pc = FIELD_GET(MI300_UMC_MCA_PC, addr);
sid = FIELD_GET(MI300_UMC_MCA_SID, addr);
/* Calculate hash for each Bank bit. */
for (i = 0 ; i < NUM_BANK_BITS; i++) {
if (!addr_hash.bank[i].xor_enable)
continue ;
temp = bitwise_xor_bits(col & addr_hash.bank[i].col_xor);
temp ^= bitwise_xor_bits(row & addr_hash.bank[i].row_xor);
bank ^= temp << i;
}
/* Calculate hash for PC bit. */
if (addr_hash.pc.xor_enable) {
temp = bitwise_xor_bits(col & addr_hash.pc.col_xor);
temp ^= bitwise_xor_bits(row & addr_hash.pc.row_xor);
/* Bits SID[1:0] act as Bank[5:4] for PC hash, so apply them here. */
temp ^= bitwise_xor_bits((bank | sid << NUM_BANK_BITS) & addr_hash.bank_xor);
pc ^= temp;
}
/* Reconstruct the normalized address starting with NA[4:0] = 0 */
addr = 0 ;
/* Column bits */
for (i = 0 ; i < NUM_COL_BITS; i++) {
temp = (col >> i) & 0 x1;
addr |= temp << bit_shifts.col[i];
}
/* Bank bits */
for (i = 0 ; i < NUM_BANK_BITS; i++) {
temp = (bank >> i) & 0 x1;
addr |= temp << bit_shifts.bank[i];
}
/* Row lo bits */
for (i = 0 ; i < bit_shifts.num_row_lo; i++) {
temp = (row >> i) & 0 x1;
addr |= temp << (i + bit_shifts.row_lo);
}
/* Row hi bits */
for (i = 0 ; i < bit_shifts.num_row_hi; i++) {
temp = (row >> (i + bit_shifts.num_row_lo)) & 0 x1;
addr |= temp << (i + bit_shifts.row_hi);
}
/* PC bit */
addr |= pc << bit_shifts.pc;
/* SID bits */
for (i = 0 ; i < NUM_SID_BITS; i++) {
temp = (sid >> i) & 0 x1;
addr |= temp << bit_shifts.sid[i];
}
pr_debug("Addr=0x%016lx" , addr);
pr_debug("Bank=%u Row=%u Column=%u PC=%u SID=%u" , bank, row, col, pc, sid);
return addr;
}
/*
* When a DRAM ECC error occurs on MI300 systems , it is recommended to retire
* all memory within that DRAM row . This applies to the memory with a DRAM
* bank .
*
* To find the memory addresses , loop through permutations of the DRAM column
* bits and find the System Physical address of each . The column bits are used
* to calculate the intermediate Normalized address , so all permutations should
* be checked .
*
* See amd_atl : : convert_dram_to_norm_addr_mi300 ( ) for MI300 address formats .
*/
#define MI300_NUM_COL BIT(HWEIGHT(MI300_UMC_MCA_COL))
static void _retire_row_mi300(struct atl_err *a_err)
{
unsigned long addr;
struct page *p;
u8 col;
for (col = 0 ; col < MI300_NUM_COL; col++) {
a_err->addr &= ~MI300_UMC_MCA_COL;
a_err->addr |= FIELD_PREP(MI300_UMC_MCA_COL, col);
addr = amd_convert_umc_mca_addr_to_sys_addr(a_err);
if (IS_ERR_VALUE(addr))
continue ;
addr = PHYS_PFN(addr);
/*
* Skip invalid or already poisoned pages to avoid unnecessary
* error messages from memory_failure ( ) .
*/
p = pfn_to_online_page(addr);
if (!p)
continue ;
if (PageHWPoison(p))
continue ;
memory_failure(addr, 0 );
}
}
/*
* In addition to the column bits , the row [ 13 ] bit should also be included when
* calculating addresses affected by a physical row .
*
* Instead of running through another loop over a single bit , just run through
* the column bits twice and flip the row [ 13 ] bit in - between .
*
* See MI300_UMC_MCA_ROW for the row bits in MCA_ADDR_UMC value .
*/
static void retire_row_mi300(struct atl_err *a_err)
{
_retire_row_mi300(a_err);
a_err->addr ^= MI300_UMC_MCA_ROW13;
_retire_row_mi300(a_err);
}
void amd_retire_dram_row(struct atl_err *a_err)
{
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
return retire_row_mi300(a_err);
}
EXPORT_SYMBOL_GPL(amd_retire_dram_row);
static unsigned long get_addr(unsigned long addr)
{
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
return convert_dram_to_norm_addr_mi300(addr);
return addr;
}
#define MCA_IPID_INST_ID_HI GENMASK_ULL(47 , 44 )
static u8 get_die_id(struct atl_err *err)
{
/*
* AMD Node ID is provided in MCA_IPID [ InstanceIdHi ] , and this
* needs to be divided by 4 to get the internal Die ID .
*/
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous) {
u8 node_id = FIELD_GET(MCA_IPID_INST_ID_HI, err->ipid);
return node_id >> 2 ;
}
/*
* For CPUs , this is the AMD Node ID modulo the number
* of AMD Nodes per socket .
*/
return topology_amd_node_id(err->cpu) % topology_amd_nodes_per_pkg();
}
#define UMC_CHANNEL_NUM GENMASK(31 , 20 )
static u8 get_coh_st_inst_id(struct atl_err *err)
{
if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
return get_coh_st_inst_id_mi300(err);
return FIELD_GET(UMC_CHANNEL_NUM, err->ipid);
}
unsigned long 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 = get_coh_st_inst_id(err);
unsigned long addr = get_addr(err->addr);
u8 die_id = get_die_id(err);
unsigned long ret_addr;
pr_debug("socket_id=0x%x die_id=0x%x coh_st_inst_id=0x%x addr=0x%016lx" ,
socket_id, die_id, coh_st_inst_id, addr);
ret_addr = prm_umc_norm_to_sys_addr(socket_id, err->ipid, addr);
if (!IS_ERR_VALUE(ret_addr))
return ret_addr;
return norm_to_sys_addr(socket_id, die_id, coh_st_inst_id, addr);
}
Messung V0.5 in Prozent C=95 H=81 G=88
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-10-02)
¤
*© Formatika GbR, Deutschland