brintos

brintos / linux-shallow public Read only

0
0
Text · 5.1 KiB · 4197e10 Raw
230 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * AMD Address Translation Library4 *5 * core.c : Module init and base translation functions6 *7 * Copyright (c) 2023, Advanced Micro Devices, Inc.8 * All Rights Reserved.9 *10 * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>11 */12 13#include <linux/module.h>14#include <asm/cpu_device_id.h>15 16#include "internal.h"17 18struct df_config df_cfg __read_mostly;19 20static int addr_over_limit(struct addr_ctx *ctx)21{22	u64 dram_limit_addr;23 24	if (df_cfg.rev >= DF4)25		dram_limit_addr = FIELD_GET(DF4_DRAM_LIMIT_ADDR, ctx->map.limit);26	else27		dram_limit_addr = FIELD_GET(DF2_DRAM_LIMIT_ADDR, ctx->map.limit);28 29	dram_limit_addr <<= DF_DRAM_BASE_LIMIT_LSB;30	dram_limit_addr |= GENMASK(DF_DRAM_BASE_LIMIT_LSB - 1, 0);31 32	/* Is calculated system address above DRAM limit address? */33	if (ctx->ret_addr > dram_limit_addr) {34		atl_debug(ctx, "Calculated address (0x%016llx) > DRAM limit (0x%016llx)",35			  ctx->ret_addr, dram_limit_addr);36		return -EINVAL;37	}38 39	return 0;40}41 42static bool legacy_hole_en(struct addr_ctx *ctx)43{44	u32 reg = ctx->map.base;45 46	if (df_cfg.rev >= DF4)47		reg = ctx->map.ctl;48 49	return FIELD_GET(DF_LEGACY_MMIO_HOLE_EN, reg);50}51 52static u64 add_legacy_hole(struct addr_ctx *ctx, u64 addr)53{54	if (!legacy_hole_en(ctx))55		return addr;56 57	if (addr >= df_cfg.dram_hole_base)58		addr += (BIT_ULL(32) - df_cfg.dram_hole_base);59 60	return addr;61}62 63static u64 remove_legacy_hole(struct addr_ctx *ctx, u64 addr)64{65	if (!legacy_hole_en(ctx))66		return addr;67 68	if (addr >= df_cfg.dram_hole_base)69		addr -= (BIT_ULL(32) - df_cfg.dram_hole_base);70 71	return addr;72}73 74static u64 get_base_addr(struct addr_ctx *ctx)75{76	u64 base_addr;77 78	if (df_cfg.rev >= DF4)79		base_addr = FIELD_GET(DF4_BASE_ADDR, ctx->map.base);80	else81		base_addr = FIELD_GET(DF2_BASE_ADDR, ctx->map.base);82 83	return base_addr << DF_DRAM_BASE_LIMIT_LSB;84}85 86u64 add_base_and_hole(struct addr_ctx *ctx, u64 addr)87{88	return add_legacy_hole(ctx, addr + get_base_addr(ctx));89}90 91u64 remove_base_and_hole(struct addr_ctx *ctx, u64 addr)92{93	return remove_legacy_hole(ctx, addr) - get_base_addr(ctx);94}95 96static bool late_hole_remove(struct addr_ctx *ctx)97{98	if (df_cfg.rev == DF3p5)99		return true;100 101	if (df_cfg.rev == DF4)102		return true;103 104	if (ctx->map.intlv_mode == DF3_6CHAN)105		return true;106 107	return false;108}109 110unsigned long norm_to_sys_addr(u8 socket_id, u8 die_id, u8 coh_st_inst_id, unsigned long addr)111{112	struct addr_ctx ctx;113 114	if (df_cfg.rev == UNKNOWN)115		return -EINVAL;116 117	memset(&ctx, 0, sizeof(ctx));118 119	/* Start from the normalized address */120	ctx.ret_addr = addr;121	ctx.inst_id = coh_st_inst_id;122 123	ctx.inputs.norm_addr = addr;124	ctx.inputs.socket_id = socket_id;125	ctx.inputs.die_id = die_id;126	ctx.inputs.coh_st_inst_id = coh_st_inst_id;127 128	if (legacy_hole_en(&ctx) && !df_cfg.dram_hole_base)129		return -EINVAL;130 131	if (determine_node_id(&ctx, socket_id, die_id))132		return -EINVAL;133 134	if (get_address_map(&ctx))135		return -EINVAL;136 137	if (denormalize_address(&ctx))138		return -EINVAL;139 140	if (!late_hole_remove(&ctx))141		ctx.ret_addr = add_base_and_hole(&ctx, ctx.ret_addr);142 143	if (dehash_address(&ctx))144		return -EINVAL;145 146	if (late_hole_remove(&ctx))147		ctx.ret_addr = add_base_and_hole(&ctx, ctx.ret_addr);148 149	if (addr_over_limit(&ctx))150		return -EINVAL;151 152	return ctx.ret_addr;153}154 155static void check_for_legacy_df_access(void)156{157	/*158	 * All Zen-based systems before Family 19h use the legacy159	 * DF Indirect Access (FICAA/FICAD) offsets.160	 */161	if (boot_cpu_data.x86 < 0x19) {162		df_cfg.flags.legacy_ficaa = true;163		return;164	}165 166	/* All systems after Family 19h use the current offsets. */167	if (boot_cpu_data.x86 > 0x19)168		return;169 170	/* Some Family 19h systems use the legacy offsets. */171	switch (boot_cpu_data.x86_model) {172	case 0x00 ... 0x0f:173	case 0x20 ... 0x5f:174	       df_cfg.flags.legacy_ficaa = true;175	}176}177 178/*179 * This library provides functionality for AMD-based systems with a Data Fabric.180 * The set of systems with a Data Fabric is equivalent to the set of Zen-based systems181 * and the set of systems with the Scalable MCA feature at this time. However, these182 * are technically independent things.183 *184 * It's possible to match on the PCI IDs of the Data Fabric devices, but this will be185 * an ever expanding list. Instead, match on the SMCA and Zen features to cover all186 * relevant systems.187 */188static const struct x86_cpu_id amd_atl_cpuids[] = {189	X86_MATCH_FEATURE(X86_FEATURE_SMCA, NULL),190	X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL),191	{ }192};193MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);194 195static int __init amd_atl_init(void)196{197	if (!x86_match_cpu(amd_atl_cpuids))198		return -ENODEV;199 200	if (!amd_nb_num())201		return -ENODEV;202 203	check_for_legacy_df_access();204 205	if (get_df_system_info())206		return -ENODEV;207 208	/* Increment this module's recount so that it can't be easily unloaded. */209	__module_get(THIS_MODULE);210	amd_atl_register_decoder(convert_umc_mca_addr_to_sys_addr);211 212	pr_info("AMD Address Translation Library initialized\n");213	return 0;214}215 216/*217 * Exit function is only needed for testing and debug. Module unload must be218 * forced to override refcount check.219 */220static void __exit amd_atl_exit(void)221{222	amd_atl_unregister_decoder();223}224 225module_init(amd_atl_init);226module_exit(amd_atl_exit);227 228MODULE_DESCRIPTION("AMD Address Translation Library");229MODULE_LICENSE("GPL");230