[PATCH 25/25] x86/MCE/AMD: Add support for address translation on DF3 systems

From: Yazen Ghannam
Date: Fri May 07 2021 - 15:04:06 EST


From: Yazen Ghannam <yazen.ghannam@xxxxxxx>

DF3-based systems (Rome and later) support new interleaving modes and a
number of bit fields have changed or moved entirely. Add support for
these new modes and fields.

Refactoring should be minimal due to earlier changes, and most updates
will be additions.

Signed-off-by: Yazen Ghannam <yazen.ghannam@xxxxxxx>
---
arch/x86/kernel/cpu/mce/amd.c | 183 +++++++++++++++++++++++++++++++---
1 file changed, 171 insertions(+), 12 deletions(-)

diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c
index b3bfdc42dcaa..a165706c04f1 100644
--- a/arch/x86/kernel/cpu/mce/amd.c
+++ b/arch/x86/kernel/cpu/mce/amd.c
@@ -684,9 +684,11 @@ enum df_reg_names {
DRAM_BASE_ADDR_1,
DRAM_LIMIT_ADDR_1,
DRAM_OFFSET,
+ DF_GLOBAL_CTL,

/* Function 1 */
SYS_FAB_ID_MASK,
+ SYS_FAB_ID_MASK_1,
};

static struct df_reg df_regs[] = {
@@ -702,18 +704,28 @@ static struct df_reg df_regs[] = {
[DRAM_LIMIT_ADDR_1] = {0, 0x11C},
/* D18F0x1B4 (DramOffset) */
[DRAM_OFFSET] = {0, 0x1B4},
+ /* D18F0x3F8 (DfGlobalCtrl) */
+ [DF_GLOBAL_CTL] = {0, 0x3F8},
/* D18F1x208 (SystemFabricIdMask) */
[SYS_FAB_ID_MASK] = {1, 0x208},
+ /* D18F1x20C (SystemFabricIdMask1) */
+ [SYS_FAB_ID_MASK_1] = {1, 0x20C},
};

enum df_types {
DF2,
+ DF3,
};

/* These are mapped 1:1 to the hardware values. Special cases are set at > 0x20. */
enum intlv_modes {
NONE = 0x00,
NOHASH_2CH = 0x01,
+ NOHASH_4CH = 0x03,
+ NOHASH_8CH = 0x05,
+ HASH_COD4_2CH = 0x0C,
+ HASH_COD2_4CH = 0x0D,
+ HASH_COD1_8CH = 0x0E,
DF2_HASH_2CH = 0x21,
};

@@ -726,6 +738,7 @@ struct addr_ctx {
u32 reg_base_addr;
u32 reg_limit_addr;
u32 reg_fab_id_mask0;
+ u32 reg_fab_id_mask1;
u16 nid;
u8 umc;
u8 map_num;
@@ -737,11 +750,15 @@ struct addr_ctx {
u8 cs_fabric_id;
u8 die_id_mask;
u8 socket_id_mask;
+ u8 node_id_shift;
bool hash_enabled;
};

static enum df_types get_df_type(struct addr_ctx *ctx)
{
+ if ((ctx->reg_fab_id_mask0 & 0xFF) != 0)
+ return DF3;
+
return DF2;
}

@@ -762,8 +779,23 @@ static int get_intlv_mode_df2(struct addr_ctx *ctx)
return 0;
}

+static int get_intlv_mode_df3(struct addr_ctx *ctx)
+{
+ ctx->intlv_mode = (ctx->reg_base_addr >> 2) & 0xF;
+
+ if (ctx->intlv_mode == HASH_COD4_2CH ||
+ ctx->intlv_mode == HASH_COD2_4CH ||
+ ctx->intlv_mode == HASH_COD1_8CH)
+ ctx->hash_enabled = true;
+
+ return 0;
+}
+
static int get_intlv_mode(struct addr_ctx *ctx)
{
+ if (ctx->df_type == DF3)
+ return get_intlv_mode_df3(ctx);
+
return get_intlv_mode_df2(ctx);
}

@@ -777,6 +809,9 @@ static int get_dram_offset_reg(struct addr_ctx *ctx)

static u64 get_hi_addr_offset(struct addr_ctx *ctx)
{
+ if (ctx->df_type == DF3)
+ return (ctx->reg_dram_offset & GENMASK_ULL(31, 12)) << 16;
+
return (ctx->reg_dram_offset & GENMASK_ULL(31, 20)) << 8;
}

@@ -823,10 +858,15 @@ static int get_dram_addr_map(struct addr_ctx *ctx)

static int get_intlv_addr_bit(struct addr_ctx *ctx)
{
- u8 intlv_addr_sel = (ctx->reg_base_addr >> 8) & 0x7;
+ u8 intlv_addr_sel;
+
+ if (ctx->df_type == DF3)
+ intlv_addr_sel = (ctx->reg_base_addr >> 9) & 0x7;
+ else
+ intlv_addr_sel = (ctx->reg_base_addr >> 8) & 0x7;

- /* {0, 1, 2, 3} map to address bits {8, 9, 10, 11} respectively */
- if (intlv_addr_sel > 3) {
+ /* {0, 1, 2, 3, 4} map to address bits {8, 9, 10, 11, 12} respectively */
+ if (intlv_addr_sel > 4) {
pr_err("%s: Invalid interleave address select %d.\n",
__func__, intlv_addr_sel);
return -EINVAL;
@@ -845,9 +885,18 @@ static void get_intlv_num_chan(struct addr_ctx *ctx)
ctx->intlv_num_chan = 0;
break;
case NOHASH_2CH:
+ case HASH_COD4_2CH:
case DF2_HASH_2CH:
ctx->intlv_num_chan = 1;
break;
+ case NOHASH_4CH:
+ case HASH_COD2_4CH:
+ ctx->intlv_num_chan = 2;
+ break;
+ case NOHASH_8CH:
+ case HASH_COD1_8CH:
+ ctx->intlv_num_chan = 3;
+ break;
default:
/* Valid interleaving modes where checked earlier. */
break;
@@ -856,12 +905,18 @@ static void get_intlv_num_chan(struct addr_ctx *ctx)

static void get_intlv_num_dies(struct addr_ctx *ctx)
{
- ctx->intlv_num_dies = (ctx->reg_limit_addr >> 10) & 0x3;
+ if (ctx->df_type == DF3)
+ ctx->intlv_num_dies = (ctx->reg_base_addr >> 6) & 0x3;
+ else
+ ctx->intlv_num_dies = (ctx->reg_limit_addr >> 10) & 0x3;
}

static void get_intlv_num_sockets(struct addr_ctx *ctx)
{
- ctx->intlv_num_sockets = (ctx->reg_limit_addr >> 8) & 0x1;
+ if (ctx->df_type == DF3)
+ ctx->intlv_num_sockets = (ctx->reg_base_addr >> 8) & 0x1;
+ else
+ ctx->intlv_num_sockets = (ctx->reg_limit_addr >> 8) & 0x1;
}

static void expand_bits(u8 start_bit, u8 num_bits, u64 *value)
@@ -884,12 +939,22 @@ static void make_space_for_cs_id(struct addr_ctx *ctx)

switch (ctx->intlv_mode) {
case NOHASH_2CH:
+ case NOHASH_4CH:
+ case NOHASH_8CH:
case DF2_HASH_2CH:
num_intlv_bits = ctx->intlv_num_chan;
num_intlv_bits += ctx->intlv_num_dies;
num_intlv_bits += ctx->intlv_num_sockets;
expand_bits(ctx->intlv_addr_bit, num_intlv_bits, &ctx->ret_addr);
break;
+ case HASH_COD4_2CH:
+ case HASH_COD2_4CH:
+ case HASH_COD1_8CH:
+ num_intlv_bits = ctx->intlv_num_chan;
+ num_intlv_bits += ctx->intlv_num_sockets;
+ expand_bits(ctx->intlv_addr_bit, 1, &ctx->ret_addr);
+ if (num_intlv_bits > 1)
+ expand_bits(12, num_intlv_bits - 1, &ctx->ret_addr);
default:
/* Valid interleaving modes where checked earlier. */
break;
@@ -908,29 +973,56 @@ static int get_cs_fabric_id(struct addr_ctx *ctx)
return 0;
}

-static void get_masks(struct addr_ctx *ctx)
-{
- ctx->die_id_mask = (ctx->reg_fab_id_mask0 >> 8) & 0xFF;
- ctx->socket_id_mask = (ctx->reg_fab_id_mask0 >> 16) & 0xFF;
-}
-
static u8 get_die_id_shift(struct addr_ctx *ctx)
{
+ if (ctx->df_type == DF3)
+ return ctx->node_id_shift;
+
return (ctx->reg_fab_id_mask0 >> 24) & 0xF;
}

static u8 get_socket_id_shift(struct addr_ctx *ctx)
{
+ if (ctx->df_type == DF3)
+ return ((ctx->reg_fab_id_mask1 >> 8) & 0x3) + ctx->node_id_shift;
+
return (ctx->reg_fab_id_mask0 >> 28) & 0xF;
}

+static void get_node_id_shift(struct addr_ctx *ctx)
+{
+ ctx->node_id_shift = ctx->reg_fab_id_mask1 & 0xF;
+}
+
+static void get_masks(struct addr_ctx *ctx)
+{
+ if (ctx->df_type == DF3) {
+ get_node_id_shift(ctx);
+
+ ctx->die_id_mask = (ctx->reg_fab_id_mask1 >> 16) & 0x7;
+ ctx->die_id_mask <<= ctx->node_id_shift;
+
+ ctx->socket_id_mask = (ctx->reg_fab_id_mask1 >> 24) & 0x7;
+ ctx->socket_id_mask <<= ctx->node_id_shift;
+ } else {
+ ctx->die_id_mask = (ctx->reg_fab_id_mask0 >> 8) & 0xFF;
+ ctx->socket_id_mask = (ctx->reg_fab_id_mask0 >> 16) & 0xFF;
+ }
+}
+
static u8 get_dst_fabric_id(struct addr_ctx *ctx)
{
+ if (ctx->df_type == DF3)
+ return ctx->reg_limit_addr & 0x1FF;
+
return ctx->reg_limit_addr & 0xFF;
}

static u8 get_component_id_mask(struct addr_ctx *ctx)
{
+ if (ctx->df_type == DF3)
+ return ctx->reg_fab_id_mask0 & 0x1FF;
+
return (~(ctx->socket_id_mask | ctx->die_id_mask)) & 0xFF;
}

@@ -982,7 +1074,14 @@ static int calculate_cs_id(struct addr_ctx *ctx)

static void insert_cs_id(struct addr_ctx *ctx)
{
- ctx->ret_addr |= (ctx->cs_id << ctx->intlv_addr_bit);
+ if (ctx->intlv_mode == HASH_COD4_2CH ||
+ ctx->intlv_mode == HASH_COD2_4CH ||
+ ctx->intlv_mode == HASH_COD1_8CH) {
+ ctx->ret_addr |= ((ctx->cs_id & 0x1) << ctx->intlv_addr_bit);
+ ctx->ret_addr |= ((ctx->cs_id & 0xE) << 11);
+ } else {
+ ctx->ret_addr |= (ctx->cs_id << ctx->intlv_addr_bit);
+ }
}

static int denormalize_addr(struct addr_ctx *ctx)
@@ -1047,11 +1146,67 @@ static int dehash_addr_df2(struct addr_ctx *ctx)
return 0;
}

+static int dehash_addr_df3(struct addr_ctx *ctx)
+{
+ u8 hashed_bit, intlv_ctl_64k, intlv_ctl_2M, intlv_ctl_1G;
+ u32 tmp;
+
+ if (amd_df_indirect_read(ctx->nid, df_regs[DF_GLOBAL_CTL], ctx->umc, &tmp))
+ return -EINVAL;
+
+ intlv_ctl_64k = !!((tmp >> 20) & 0x1);
+ intlv_ctl_2M = !!((tmp >> 21) & 0x1);
+ intlv_ctl_1G = !!((tmp >> 22) & 0x1);
+
+ hashed_bit = (ctx->ret_addr >> 14) ^
+ ((ctx->ret_addr >> 18) & intlv_ctl_64k) ^
+ ((ctx->ret_addr >> 23) & intlv_ctl_2M) ^
+ ((ctx->ret_addr >> 32) & intlv_ctl_1G) ^
+ (ctx->ret_addr >> ctx->intlv_addr_bit);
+
+ hashed_bit &= BIT(0);
+
+ if (hashed_bit != ((ctx->ret_addr >> ctx->intlv_addr_bit) & BIT(0)))
+ ctx->ret_addr ^= BIT(ctx->intlv_addr_bit);
+
+ if (ctx->intlv_mode != HASH_COD2_4CH &&
+ ctx->intlv_mode != HASH_COD1_8CH)
+ return 0;
+
+ hashed_bit = (ctx->ret_addr >> 12) ^
+ ((ctx->ret_addr >> 16) & intlv_ctl_64k) ^
+ ((ctx->ret_addr >> 21) & intlv_ctl_2M) ^
+ ((ctx->ret_addr >> 30) & intlv_ctl_1G);
+
+ hashed_bit &= BIT(0);
+
+ if (hashed_bit != ((ctx->ret_addr >> 12) & BIT(0)))
+ ctx->ret_addr ^= BIT(12);
+
+ if (ctx->intlv_mode != HASH_COD1_8CH)
+ return 0;
+
+ hashed_bit = (ctx->ret_addr >> 13) ^
+ ((ctx->ret_addr >> 17) & intlv_ctl_64k) ^
+ ((ctx->ret_addr >> 22) & intlv_ctl_2M) ^
+ ((ctx->ret_addr >> 31) & intlv_ctl_1G);
+
+ hashed_bit &= BIT(0);
+
+ if (hashed_bit != ((ctx->ret_addr >> 13) & BIT(0)))
+ ctx->ret_addr ^= BIT(13);
+
+ return 0;
+}
+
static int dehash_addr(struct addr_ctx *ctx)
{
if (!ctx->hash_enabled)
return 0;

+ if (ctx->df_type == DF3)
+ return dehash_addr_df3(ctx);
+
return dehash_addr_df2(ctx);
}

@@ -1073,6 +1228,10 @@ static int get_fabric_id_mask_reg(struct addr_ctx *ctx)
ctx->umc, &ctx->reg_fab_id_mask0))
return -EINVAL;

+ if (amd_df_indirect_read(ctx->nid, df_regs[SYS_FAB_ID_MASK_1],
+ ctx->umc, &ctx->reg_fab_id_mask1))
+ return -EINVAL;
+
return 0;
}

--
2.25.1