1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * AMD Address Translation Library
5 * core.c : Module init and base translation functions
7 * Copyright (c) 2023, Advanced Micro Devices, Inc.
10 * Author: Yazen Ghannam <Yazen.Ghannam@amd.com>
13 #include <linux/module.h>
14 #include <asm/cpu_device_id.h>
18 struct df_config df_cfg __read_mostly
;
20 static int addr_over_limit(struct addr_ctx
*ctx
)
24 if (df_cfg
.rev
>= DF4
)
25 dram_limit_addr
= FIELD_GET(DF4_DRAM_LIMIT_ADDR
, ctx
->map
.limit
);
27 dram_limit_addr
= FIELD_GET(DF2_DRAM_LIMIT_ADDR
, ctx
->map
.limit
);
29 dram_limit_addr
<<= DF_DRAM_BASE_LIMIT_LSB
;
30 dram_limit_addr
|= GENMASK(DF_DRAM_BASE_LIMIT_LSB
- 1, 0);
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
);
42 static bool legacy_hole_en(struct addr_ctx
*ctx
)
44 u32 reg
= ctx
->map
.base
;
46 if (df_cfg
.rev
>= DF4
)
49 return FIELD_GET(DF_LEGACY_MMIO_HOLE_EN
, reg
);
52 static u64
add_legacy_hole(struct addr_ctx
*ctx
, u64 addr
)
54 if (!legacy_hole_en(ctx
))
57 if (addr
>= df_cfg
.dram_hole_base
)
58 addr
+= (BIT_ULL(32) - df_cfg
.dram_hole_base
);
63 static u64
remove_legacy_hole(struct addr_ctx
*ctx
, u64 addr
)
65 if (!legacy_hole_en(ctx
))
68 if (addr
>= df_cfg
.dram_hole_base
)
69 addr
-= (BIT_ULL(32) - df_cfg
.dram_hole_base
);
74 static u64
get_base_addr(struct addr_ctx
*ctx
)
78 if (df_cfg
.rev
>= DF4
)
79 base_addr
= FIELD_GET(DF4_BASE_ADDR
, ctx
->map
.base
);
81 base_addr
= FIELD_GET(DF2_BASE_ADDR
, ctx
->map
.base
);
83 return base_addr
<< DF_DRAM_BASE_LIMIT_LSB
;
86 u64
add_base_and_hole(struct addr_ctx
*ctx
, u64 addr
)
88 return add_legacy_hole(ctx
, addr
+ get_base_addr(ctx
));
91 u64
remove_base_and_hole(struct addr_ctx
*ctx
, u64 addr
)
93 return remove_legacy_hole(ctx
, addr
) - get_base_addr(ctx
);
96 static bool late_hole_remove(struct addr_ctx
*ctx
)
98 if (df_cfg
.rev
== DF3p5
)
101 if (df_cfg
.rev
== DF4
)
104 if (ctx
->map
.intlv_mode
== DF3_6CHAN
)
110 unsigned long norm_to_sys_addr(u8 socket_id
, u8 die_id
, u8 coh_st_inst_id
, unsigned long addr
)
114 if (df_cfg
.rev
== UNKNOWN
)
117 memset(&ctx
, 0, sizeof(ctx
));
119 /* Start from the normalized address */
121 ctx
.inst_id
= coh_st_inst_id
;
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
;
128 if (legacy_hole_en(&ctx
) && !df_cfg
.dram_hole_base
)
131 if (determine_node_id(&ctx
, socket_id
, die_id
))
134 if (get_address_map(&ctx
))
137 if (denormalize_address(&ctx
))
140 if (!late_hole_remove(&ctx
))
141 ctx
.ret_addr
= add_base_and_hole(&ctx
, ctx
.ret_addr
);
143 if (dehash_address(&ctx
))
146 if (late_hole_remove(&ctx
))
147 ctx
.ret_addr
= add_base_and_hole(&ctx
, ctx
.ret_addr
);
149 if (addr_over_limit(&ctx
))
155 static void check_for_legacy_df_access(void)
158 * All Zen-based systems before Family 19h use the legacy
159 * DF Indirect Access (FICAA/FICAD) offsets.
161 if (boot_cpu_data
.x86
< 0x19) {
162 df_cfg
.flags
.legacy_ficaa
= true;
166 /* All systems after Family 19h use the current offsets. */
167 if (boot_cpu_data
.x86
> 0x19)
170 /* Some Family 19h systems use the legacy offsets. */
171 switch (boot_cpu_data
.x86_model
) {
174 df_cfg
.flags
.legacy_ficaa
= true;
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.
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
188 static const struct x86_cpu_id amd_atl_cpuids
[] = {
189 X86_MATCH_FEATURE(X86_FEATURE_SMCA
, NULL
),
190 X86_MATCH_FEATURE(X86_FEATURE_ZEN
, NULL
),
193 MODULE_DEVICE_TABLE(x86cpu
, amd_atl_cpuids
);
195 static int __init
amd_atl_init(void)
197 if (!x86_match_cpu(amd_atl_cpuids
))
203 check_for_legacy_df_access();
205 if (get_df_system_info())
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
);
212 pr_info("AMD Address Translation Library initialized\n");
217 * Exit function is only needed for testing and debug. Module unload must be
218 * forced to override refcount check.
220 static void __exit
amd_atl_exit(void)
222 amd_atl_unregister_decoder();
225 module_init(amd_atl_init
);
226 module_exit(amd_atl_exit
);
228 MODULE_DESCRIPTION("AMD Address Translation Library");
229 MODULE_LICENSE("GPL");