File Index
Files and directories
Sign in
FILE

core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/ras/amd/atl/core.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * AMD Address Translation Library
  4 *
  5 * core.c : Module init and base translation functions
  6 *
  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	else
 27		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	else
 81		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 legacy
159	 * 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 systems
181 * and the set of systems with the Scalable MCA feature at this time. However, these
182 * are technically independent things.
183 *
184 * It's possible to match on the PCI IDs of the Data Fabric devices, but this will be
185 * an ever expanding list. Instead, match on the SMCA and Zen features to cover all
186 * 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	int ret;
198
199	if (!x86_match_cpu(amd_atl_cpuids))
200		return -ENODEV;
201
202	if (!amd_nb_num())
203		return -ENODEV;
204
205	check_for_legacy_df_access();
206
207	ret = get_df_system_info();
208	if (ret)
209		return ret;
210
211	/* Increment this module's recount so that it can't be easily unloaded. */
212	__module_get(THIS_MODULE);
213	amd_atl_register_decoder(convert_umc_mca_addr_to_sys_addr);
214
215	pr_info("AMD Address Translation Library initialized\n");
216	return 0;
217}
218
219/*
220 * Exit function is only needed for testing and debug. Module unload must be
221 * forced to override refcount check.
222 */
223static void __exit amd_atl_exit(void)
224{
225	amd_atl_unregister_decoder();
226}
227
228module_init(amd_atl_init);
229module_exit(amd_atl_exit);
230
231MODULE_DESCRIPTION("AMD Address Translation Library");
232MODULE_LICENSE("GPL");
~/ Read-only file preview.