Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions drivers/acpi/prmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ static struct prm_handler_info *find_prm_handler(const guid_t *guid)
return (struct prm_handler_info *) find_guid_info(guid, GET_HANDLER);
}

bool acpi_prm_handler_available(const guid_t *guid)
{
return find_prm_handler(guid) && find_prm_module(guid);
}
EXPORT_SYMBOL_GPL(acpi_prm_handler_available);

/* In-coming PRM commands */

#define PRM_CMD_RUN_SERVICE 0
Expand All @@ -224,6 +230,30 @@ static struct prm_handler_info *find_prm_handler(const guid_t *guid)
#define UPDATE_LOCK_ALREADY_HELD 4
#define UPDATE_UNLOCK_WITHOUT_LOCK 5

int acpi_call_prm_handler(guid_t handler_guid, void *param_buffer)
{
struct prm_handler_info *handler = find_prm_handler(&handler_guid);
struct prm_module_info *module = find_prm_module(&handler_guid);
struct prm_context_buffer context;
efi_status_t status;

if (!module || !handler)
return -ENODEV;

memset(&context, 0, sizeof(context));
ACPI_COPY_NAMESEG(context.signature, "PRMC");
context.identifier = handler->guid;
context.static_data_buffer = handler->static_data_buffer_addr;
context.mmio_ranges = module->mmio_info;

status = efi_call_acpi_prm_handler(handler->handler_addr,
(u64)param_buffer,
&context);

return efi_status_to_err(status);
}
EXPORT_SYMBOL_GPL(acpi_call_prm_handler);

/*
* This is the PlatformRtMechanism opregion space handler.
* @function: indicates the read/write. In fact as the PlatformRtMechanism
Expand Down
4 changes: 4 additions & 0 deletions drivers/ras/amd/atl/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@ config AMD_ATL

Enable this option if using DRAM ECC on Zen-based systems
and OS-based error handling.

config AMD_ATL_PRM
depends on AMD_ATL && ACPI_PRMT
def_bool y
2 changes: 2 additions & 0 deletions drivers/ras/amd/atl/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ amd_atl-y += map.o
amd_atl-y += system.o
amd_atl-y += umc.o

amd_atl-$(CONFIG_AMD_ATL_PRM) += prm.o

obj-$(CONFIG_AMD_ATL) += amd_atl.o
27 changes: 27 additions & 0 deletions drivers/ras/amd/atl/access.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,40 @@ static DEFINE_MUTEX(df_indirect_mutex);

#define DF_FICAA_REG_NUM_LEGACY GENMASK(10, 2)

static u16 get_accessible_node(u16 node)
{
/*
* On heterogeneous systems, not all AMD Nodes are accessible
* through software-visible registers. The Node ID needs to be
* adjusted for register accesses. But its value should not be
* changed for the translation methods.
*/
if (df_cfg.flags.heterogeneous) {
/* Only Node 0 is accessible on DF3.5 systems. */
if (df_cfg.rev == DF3p5)
node = 0;

/*
* Only the first Node in each Socket is accessible on
* DF4.5 systems, and this is visible to software as one
* Fabric per Socket. The Socket ID can be derived from
* the Node ID and global shift values.
*/
if (df_cfg.rev == DF4p5)
node >>= df_cfg.socket_id_shift - df_cfg.node_id_shift;
}

return node;
}

static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *lo)
{
u32 ficaa_addr = 0x8C, ficad_addr = 0xB8;
struct pci_dev *F4;
int err = -ENODEV;
u32 ficaa = 0;

node = get_accessible_node(node);
if (node >= amd_nb_num())
goto out;

Expand Down
7 changes: 5 additions & 2 deletions drivers/ras/amd/atl/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);

static int __init amd_atl_init(void)
{
int ret;

if (!x86_match_cpu(amd_atl_cpuids))
return -ENODEV;

Expand All @@ -199,8 +201,9 @@ static int __init amd_atl_init(void)

check_for_legacy_df_access();

if (get_df_system_info())
return -ENODEV;
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);
Expand Down
95 changes: 94 additions & 1 deletion drivers/ras/amd/atl/dehash.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ static int df4p5_dehash_addr(struct addr_ctx *ctx)
hash_ctl_64k = FIELD_GET(DF4_HASH_CTL_64K, ctx->map.ctl);
hash_ctl_2M = FIELD_GET(DF4_HASH_CTL_2M, ctx->map.ctl);
hash_ctl_1G = FIELD_GET(DF4_HASH_CTL_1G, ctx->map.ctl);
hash_ctl_1T = FIELD_GET(DF4_HASH_CTL_1T, ctx->map.ctl);
hash_ctl_1T = FIELD_GET(DF4p5_HASH_CTL_1T, ctx->map.ctl);

/*
* Generate a unique address to determine which bits
Expand Down Expand Up @@ -343,6 +343,94 @@ static int df4p5_dehash_addr(struct addr_ctx *ctx)
return 0;
}

/*
* MI300 hash bits
* 4K 64K 2M 1G 1T 1T
* COH_ST_Select[0] = XOR of addr{8, 12, 15, 22, 29, 36, 43}
* COH_ST_Select[1] = XOR of addr{9, 13, 16, 23, 30, 37, 44}
* COH_ST_Select[2] = XOR of addr{10, 14, 17, 24, 31, 38, 45}
* COH_ST_Select[3] = XOR of addr{11, 18, 25, 32, 39, 46}
* COH_ST_Select[4] = XOR of addr{14, 19, 26, 33, 40, 47} aka Stack
* DieID[0] = XOR of addr{12, 20, 27, 34, 41 }
* DieID[1] = XOR of addr{13, 21, 28, 35, 42 }
*/
static int mi300_dehash_addr(struct addr_ctx *ctx)
{
bool hash_ctl_4k, hash_ctl_64k, hash_ctl_2M, hash_ctl_1G, hash_ctl_1T;
bool hashed_bit, intlv_bit, test_bit;
u8 num_intlv_bits, base_bit, i;

if (!map_bits_valid(ctx, 8, 8, 4, 1))
return -EINVAL;

hash_ctl_4k = FIELD_GET(DF4p5_HASH_CTL_4K, ctx->map.ctl);
hash_ctl_64k = FIELD_GET(DF4_HASH_CTL_64K, ctx->map.ctl);
hash_ctl_2M = FIELD_GET(DF4_HASH_CTL_2M, ctx->map.ctl);
hash_ctl_1G = FIELD_GET(DF4_HASH_CTL_1G, ctx->map.ctl);
hash_ctl_1T = FIELD_GET(DF4p5_HASH_CTL_1T, ctx->map.ctl);

/* Channel bits */
num_intlv_bits = ilog2(ctx->map.num_intlv_chan);

for (i = 0; i < num_intlv_bits; i++) {
base_bit = 8 + i;

/* COH_ST_Select[4] jumps to a base bit of 14. */
if (i == 4)
base_bit = 14;

intlv_bit = BIT_ULL(base_bit) & ctx->ret_addr;

hashed_bit = intlv_bit;

/* 4k hash bit only applies to the first 3 bits. */
if (i <= 2) {
test_bit = BIT_ULL(12 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_4k;
}

/* Use temporary 'test_bit' value to avoid Sparse warnings. */
test_bit = BIT_ULL(15 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_64k;
test_bit = BIT_ULL(22 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_2M;
test_bit = BIT_ULL(29 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_1G;
test_bit = BIT_ULL(36 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_1T;
test_bit = BIT_ULL(43 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_1T;

if (hashed_bit != intlv_bit)
ctx->ret_addr ^= BIT_ULL(base_bit);
}

/* Die bits */
num_intlv_bits = ilog2(ctx->map.num_intlv_dies);

for (i = 0; i < num_intlv_bits; i++) {
base_bit = 12 + i;

intlv_bit = BIT_ULL(base_bit) & ctx->ret_addr;

hashed_bit = intlv_bit;

test_bit = BIT_ULL(20 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_64k;
test_bit = BIT_ULL(27 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_2M;
test_bit = BIT_ULL(34 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_1G;
test_bit = BIT_ULL(41 + i) & ctx->ret_addr;
hashed_bit ^= test_bit & hash_ctl_1T;

if (hashed_bit != intlv_bit)
ctx->ret_addr ^= BIT_ULL(base_bit);
}

return 0;
}

int dehash_address(struct addr_ctx *ctx)
{
switch (ctx->map.intlv_mode) {
Expand Down Expand Up @@ -400,6 +488,11 @@ int dehash_address(struct addr_ctx *ctx)
case DF4p5_NPS1_16CHAN_2K_HASH:
return df4p5_dehash_addr(ctx);

case MI3_HASH_8CHAN:
case MI3_HASH_16CHAN:
case MI3_HASH_32CHAN:
return mi300_dehash_addr(ctx);

default:
atl_debug_on_bad_intlv_mode(ctx);
return -EINVAL;
Expand Down
102 changes: 102 additions & 0 deletions drivers/ras/amd/atl/denormalize.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,40 @@ static u64 make_space_for_coh_st_id_split_2_1(struct addr_ctx *ctx)
return expand_bits(12, ctx->map.total_intlv_bits - 1, denorm_addr);
}

/*
* Make space for CS ID at bits [14:8] as follows:
*
* 8 channels -> bits [10:8]
* 16 channels -> bits [11:8]
* 32 channels -> bits [14,11:8]
*
* 1 die -> N/A
* 2 dies -> bit [12]
* 4 dies -> bits [13:12]
*/
static u64 make_space_for_coh_st_id_mi300(struct addr_ctx *ctx)
{
u8 num_intlv_bits = ilog2(ctx->map.num_intlv_chan);
u64 denorm_addr;

if (ctx->map.intlv_bit_pos != 8) {
pr_debug("Invalid interleave bit: %u", ctx->map.intlv_bit_pos);
return ~0ULL;
}

/* Channel bits. Covers up to 4 bits at [11:8]. */
denorm_addr = expand_bits(8, min(num_intlv_bits, 4), ctx->ret_addr);

/* Die bits. Always starts at [12]. */
denorm_addr = expand_bits(12, ilog2(ctx->map.num_intlv_dies), denorm_addr);

/* Additional channel bit at [14]. */
if (num_intlv_bits > 4)
denorm_addr = expand_bits(14, 1, denorm_addr);

return denorm_addr;
}

/*
* Take the current calculated address and shift enough bits in the middle
* to make a gap where the interleave bits will be inserted.
Expand Down Expand Up @@ -107,6 +141,12 @@ static u64 make_space_for_coh_st_id(struct addr_ctx *ctx)
case DF4p5_NPS1_8CHAN_2K_HASH:
case DF4p5_NPS1_16CHAN_2K_HASH:
return make_space_for_coh_st_id_split_2_1(ctx);

case MI3_HASH_8CHAN:
case MI3_HASH_16CHAN:
case MI3_HASH_32CHAN:
return make_space_for_coh_st_id_mi300(ctx);

default:
atl_debug_on_bad_intlv_mode(ctx);
return ~0ULL;
Expand Down Expand Up @@ -204,6 +244,32 @@ static u16 get_coh_st_id_df4(struct addr_ctx *ctx)
return coh_st_id;
}

/*
* MI300 hash has:
* (C)hannel[3:0] = coh_st_id[3:0]
* (S)tack[0] = coh_st_id[4]
* (D)ie[1:0] = coh_st_id[6:5]
*
* Hashed coh_st_id is swizzled so that Stack bit is at the end.
* coh_st_id = SDDCCCC
*/
static u16 get_coh_st_id_mi300(struct addr_ctx *ctx)
{
u8 channel_bits, die_bits, stack_bit;
u16 die_id;

/* Subtract the "base" Destination Fabric ID. */
ctx->coh_st_fabric_id -= get_dst_fabric_id(ctx);

die_id = (ctx->coh_st_fabric_id & df_cfg.die_id_mask) >> df_cfg.die_id_shift;

channel_bits = FIELD_GET(GENMASK(3, 0), ctx->coh_st_fabric_id);
stack_bit = FIELD_GET(BIT(4), ctx->coh_st_fabric_id) << 6;
die_bits = die_id << 4;

return stack_bit | die_bits | channel_bits;
}

/*
* Derive the correct Coherent Station ID that represents the interleave bits
* used within the system physical address. This accounts for the
Expand Down Expand Up @@ -237,6 +303,11 @@ static u16 calculate_coh_st_id(struct addr_ctx *ctx)
case DF4p5_NPS1_16CHAN_2K_HASH:
return get_coh_st_id_df4(ctx);

case MI3_HASH_8CHAN:
case MI3_HASH_16CHAN:
case MI3_HASH_32CHAN:
return get_coh_st_id_mi300(ctx);

/* COH_ST ID is simply the COH_ST Fabric ID adjusted by the Destination Fabric ID. */
case DF4p5_NPS2_4CHAN_1K_HASH:
case DF4p5_NPS1_8CHAN_1K_HASH:
Expand Down Expand Up @@ -287,6 +358,9 @@ static u64 insert_coh_st_id(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id
case NOHASH_8CHAN:
case NOHASH_16CHAN:
case NOHASH_32CHAN:
case MI3_HASH_8CHAN:
case MI3_HASH_16CHAN:
case MI3_HASH_32CHAN:
case DF2_2CHAN_HASH:
return insert_coh_st_id_at_intlv_bit(ctx, denorm_addr, coh_st_id);

Expand Down Expand Up @@ -314,13 +388,41 @@ static u64 insert_coh_st_id(struct addr_ctx *ctx, u64 denorm_addr, u16 coh_st_id
}
}

/*
* MI300 systems have a fixed, hardware-defined physical-to-logical
* Coherent Station mapping. The Remap registers are not used.
*/
static const u16 phy_to_log_coh_st_map_mi300[] = {
12, 13, 14, 15,
8, 9, 10, 11,
4, 5, 6, 7,
0, 1, 2, 3,
28, 29, 30, 31,
24, 25, 26, 27,
20, 21, 22, 23,
16, 17, 18, 19,
};

static u16 get_logical_coh_st_fabric_id_mi300(struct addr_ctx *ctx)
{
if (ctx->inst_id >= sizeof(phy_to_log_coh_st_map_mi300)) {
atl_debug(ctx, "Instance ID out of range");
return ~0;
}

return phy_to_log_coh_st_map_mi300[ctx->inst_id] | (ctx->node_id << df_cfg.node_id_shift);
}

static u16 get_logical_coh_st_fabric_id(struct addr_ctx *ctx)
{
u16 component_id, log_fabric_id;

/* Start with the physical COH_ST Fabric ID. */
u16 phys_fabric_id = ctx->coh_st_fabric_id;

if (df_cfg.rev == DF4p5 && df_cfg.flags.heterogeneous)
return get_logical_coh_st_fabric_id_mi300(ctx);

/* Skip logical ID lookup if remapping is disabled. */
if (!FIELD_GET(DF4_REMAP_EN, ctx->map.ctl) &&
ctx->map.intlv_mode != DF3_6CHAN)
Expand Down
Loading