]> Git Repo - J-linux.git/blob - drivers/ras/amd/atl/core.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / ras / amd / atl / core.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 <[email protected]>
11  */
12
13 #include <linux/module.h>
14 #include <asm/cpu_device_id.h>
15
16 #include "internal.h"
17
18 struct df_config df_cfg __read_mostly;
19
20 static 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
42 static 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
52 static 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
63 static 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
74 static 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
86 u64 add_base_and_hole(struct addr_ctx *ctx, u64 addr)
87 {
88         return add_legacy_hole(ctx, addr + get_base_addr(ctx));
89 }
90
91 u64 remove_base_and_hole(struct addr_ctx *ctx, u64 addr)
92 {
93         return remove_legacy_hole(ctx, addr) - get_base_addr(ctx);
94 }
95
96 static 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
110 unsigned 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
155 static 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  */
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),
191         { }
192 };
193 MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
194
195 static 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 be
218  * forced to override refcount check.
219  */
220 static void __exit amd_atl_exit(void)
221 {
222         amd_atl_unregister_decoder();
223 }
224
225 module_init(amd_atl_init);
226 module_exit(amd_atl_exit);
227
228 MODULE_DESCRIPTION("AMD Address Translation Library");
229 MODULE_LICENSE("GPL");
This page took 0.060935 seconds and 4 git commands to generate.