Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | |
Kyösti Mälkki | 84935f7 | 2021-01-11 20:13:34 +0200 | [diff] [blame] | 3 | #include <acpi/acpi_gnvs.h> |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 4 | #include <cbmem.h> |
| 5 | #include <commonlib/helpers.h> |
| 6 | #include <commonlib/region.h> |
| 7 | #include <console/console.h> |
Arthur Heymans | a804f91 | 2022-05-30 14:39:45 +0200 | [diff] [blame] | 8 | #include <cpu/cpu.h> |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 9 | #include <cpu/x86/smm.h> |
Arthur Heymans | a804f91 | 2022-05-30 14:39:45 +0200 | [diff] [blame] | 10 | #include <device/device.h> |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 11 | #include <rmodule.h> |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 12 | #include <stdio.h> |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 13 | #include <string.h> |
Elyes Haouas | afc5f9b | 2022-10-02 10:26:55 +0200 | [diff] [blame] | 14 | #include <types.h> |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 15 | |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 16 | #define SMM_CODE_SEGMENT_SIZE 0x10000 |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 17 | |
| 18 | /* |
| 19 | * Components that make up the SMRAM: |
| 20 | * 1. Save state - the total save state memory used |
| 21 | * 2. Stack - stacks for the CPUs in the SMM handler |
| 22 | * 3. Stub - SMM stub code for calling into handler |
| 23 | * 4. Handler - C-based SMM handler. |
| 24 | * |
| 25 | * The components are assumed to consist of one consecutive region. |
| 26 | */ |
| 27 | |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 28 | /* |
| 29 | * The stub is the entry point that sets up protected mode and stacks for each |
| 30 | * CPU. It then calls into the SMM handler module. It is encoded as an rmodule. |
| 31 | */ |
| 32 | extern unsigned char _binary_smmstub_start[]; |
| 33 | |
| 34 | /* Per CPU minimum stack size. */ |
| 35 | #define SMM_MINIMUM_STACK_SIZE 32 |
| 36 | |
| 37 | struct cpu_smm_info { |
| 38 | uint8_t active; |
| 39 | uintptr_t smbase; |
Arthur Heymans | 0effeb5 | 2022-05-23 22:43:51 +0200 | [diff] [blame] | 40 | struct region ss; |
| 41 | struct region stub_code; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 42 | }; |
| 43 | struct cpu_smm_info cpus[CONFIG_MAX_CPUS] = { 0 }; |
| 44 | |
| 45 | /* |
| 46 | * This method creates a map of all the CPU entry points, save state locations |
| 47 | * and the beginning and end of code segments for each CPU. This map is used |
| 48 | * during relocation to properly align as many CPUs that can fit into the SMRAM |
| 49 | * region. For more information on how SMRAM works, refer to the latest Intel |
| 50 | * developer's manuals (volume 3, chapter 34). SMRAM is divided up into the |
| 51 | * following regions: |
| 52 | * +-----------------+ Top of SMRAM |
Arthur Heymans | 1efca4d | 2023-05-17 18:10:47 +0200 | [diff] [blame] | 53 | * | MSEG | |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 54 | * +-----------------+ |
| 55 | * | common | |
| 56 | * | smi handler | 64K |
| 57 | * | | |
| 58 | * +-----------------+ |
| 59 | * | CPU 0 code seg | |
| 60 | * +-----------------+ |
| 61 | * | CPU 1 code seg | |
| 62 | * +-----------------+ |
| 63 | * | CPU x code seg | |
| 64 | * +-----------------+ |
| 65 | * | | |
| 66 | * | | |
| 67 | * +-----------------+ |
| 68 | * | stacks | |
| 69 | * +-----------------+ <- START of SMRAM |
| 70 | * |
| 71 | * The code below checks when a code segment is full and begins placing the remainder |
| 72 | * CPUs in the lower segments. The entry point for each CPU is smbase + 0x8000 |
| 73 | * and save state is smbase + 0x8000 + (0x8000 - state save size). Save state |
| 74 | * area grows downward into the CPUs entry point. Therefore staggering too many |
| 75 | * CPUs in one 32K block will corrupt CPU0's entry code as the save states move |
| 76 | * downward. |
| 77 | * input : smbase of first CPU (all other CPUs |
| 78 | * will go below this address) |
| 79 | * input : num_cpus in the system. The map will |
| 80 | * be created from 0 to num_cpus. |
| 81 | */ |
Arthur Heymans | 0ab98d5 | 2022-04-07 19:14:48 +0200 | [diff] [blame] | 82 | static int smm_create_map(const uintptr_t smbase, const unsigned int num_cpus, |
| 83 | const struct smm_loader_params *params) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 84 | { |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 85 | struct rmodule smm_stub; |
Arthur Heymans | 0ab98d5 | 2022-04-07 19:14:48 +0200 | [diff] [blame] | 86 | |
| 87 | if (ARRAY_SIZE(cpus) < num_cpus) { |
| 88 | printk(BIOS_ERR, "%s: increase MAX_CPUS in Kconfig\n", __func__); |
| 89 | return 0; |
| 90 | } |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 91 | |
| 92 | if (rmodule_parse(&_binary_smmstub_start, &smm_stub)) { |
| 93 | printk(BIOS_ERR, "%s: unable to get SMM module size\n", __func__); |
| 94 | return 0; |
| 95 | } |
| 96 | |
Arthur Heymans | 0ab98d5 | 2022-04-07 19:14:48 +0200 | [diff] [blame] | 97 | /* |
| 98 | * How many CPUs can fit into one 64K segment? |
| 99 | * Make sure that the first stub does not overlap with the last save state of a segment. |
| 100 | */ |
| 101 | const size_t stub_size = rmodule_memory_size(&smm_stub); |
Arthur Heymans | 1684b0a | 2022-04-07 21:50:16 +0200 | [diff] [blame] | 102 | const size_t needed_ss_size = MAX(params->cpu_save_state_size, stub_size); |
Arthur Heymans | 0ab98d5 | 2022-04-07 19:14:48 +0200 | [diff] [blame] | 103 | const size_t cpus_per_segment = |
| 104 | (SMM_CODE_SEGMENT_SIZE - SMM_ENTRY_OFFSET - stub_size) / needed_ss_size; |
| 105 | |
| 106 | if (cpus_per_segment == 0) { |
| 107 | printk(BIOS_ERR, "%s: CPUs won't fit in segment. Broken stub or save state size\n", |
| 108 | __func__); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 109 | return 0; |
| 110 | } |
| 111 | |
Arthur Heymans | 0ab98d5 | 2022-04-07 19:14:48 +0200 | [diff] [blame] | 112 | for (unsigned int i = 0; i < num_cpus; i++) { |
Arthur Heymans | 0ab98d5 | 2022-04-07 19:14:48 +0200 | [diff] [blame] | 113 | const size_t segment_number = i / cpus_per_segment; |
| 114 | cpus[i].smbase = smbase - SMM_CODE_SEGMENT_SIZE * segment_number |
| 115 | - needed_ss_size * (i % cpus_per_segment); |
Arthur Heymans | 0effeb5 | 2022-05-23 22:43:51 +0200 | [diff] [blame] | 116 | cpus[i].stub_code.offset = cpus[i].smbase + SMM_ENTRY_OFFSET; |
| 117 | cpus[i].stub_code.size = stub_size; |
| 118 | cpus[i].ss.offset = cpus[i].smbase + SMM_CODE_SEGMENT_SIZE |
| 119 | - params->cpu_save_state_size; |
| 120 | cpus[i].ss.size = params->cpu_save_state_size; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 121 | cpus[i].active = 1; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 122 | } |
| 123 | |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 124 | return 1; |
| 125 | } |
| 126 | |
| 127 | /* |
| 128 | * This method expects the smm relocation map to be complete. |
| 129 | * This method does not read any HW registers, it simply uses a |
| 130 | * map that was created during SMM setup. |
| 131 | * input: cpu_num - cpu number which is used as an index into the |
| 132 | * map to return the smbase |
| 133 | */ |
| 134 | u32 smm_get_cpu_smbase(unsigned int cpu_num) |
| 135 | { |
| 136 | if (cpu_num < CONFIG_MAX_CPUS) { |
| 137 | if (cpus[cpu_num].active) |
| 138 | return cpus[cpu_num].smbase; |
| 139 | } |
| 140 | return 0; |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | * This method assumes that at least 1 CPU has been set up from |
| 145 | * which it will place other CPUs below its smbase ensuring that |
| 146 | * save state does not clobber the first CPUs init code segment. The init |
| 147 | * code which is the smm stub code is the same for all CPUs. They enter |
| 148 | * smm, setup stacks (based on their apic id), enter protected mode |
| 149 | * and then jump to the common smi handler. The stack is allocated |
| 150 | * at the beginning of smram (aka tseg base, not smbase). The stack |
| 151 | * pointer for each CPU is calculated by using its apic id |
| 152 | * (code is in smm_stub.s) |
| 153 | * Each entry point will now have the same stub code which, sets up the CPU |
| 154 | * stack, enters protected mode and then jumps to the smi handler. It is |
| 155 | * important to enter protected mode before the jump because the "jump to |
| 156 | * address" might be larger than the 20bit address supported by real mode. |
| 157 | * SMI entry right now is in real mode. |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 158 | * input: num_cpus - number of cpus that need relocation including |
| 159 | * the first CPU (though its code is already loaded) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 160 | */ |
| 161 | |
Arthur Heymans | 346db92 | 2022-04-07 21:52:59 +0200 | [diff] [blame] | 162 | static void smm_place_entry_code(const unsigned int num_cpus) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 163 | { |
| 164 | unsigned int i; |
Elyes Haouas | 1f07797 | 2022-06-15 15:36:03 +0200 | [diff] [blame] | 165 | size_t size; |
Arthur Heymans | dfff5c2 | 2021-02-15 23:39:01 +0100 | [diff] [blame] | 166 | |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 167 | /* start at 1, the first CPU stub code is already there */ |
Arthur Heymans | 0effeb5 | 2022-05-23 22:43:51 +0200 | [diff] [blame] | 168 | size = region_sz(&cpus[0].stub_code); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 169 | for (i = 1; i < num_cpus; i++) { |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 170 | printk(BIOS_DEBUG, |
Elyes Haouas | 1f07797 | 2022-06-15 15:36:03 +0200 | [diff] [blame] | 171 | "SMM Module: placing smm entry code at %zx, cpu # 0x%x\n", |
Arthur Heymans | 0effeb5 | 2022-05-23 22:43:51 +0200 | [diff] [blame] | 172 | region_offset(&cpus[i].stub_code), i); |
| 173 | memcpy((void *)region_offset(&cpus[i].stub_code), |
| 174 | (void *)region_offset(&cpus[0].stub_code), size); |
Elyes Haouas | 1f07797 | 2022-06-15 15:36:03 +0200 | [diff] [blame] | 175 | printk(BIOS_SPEW, "%s: copying from %zx to %zx 0x%zx bytes\n", |
Arthur Heymans | 0effeb5 | 2022-05-23 22:43:51 +0200 | [diff] [blame] | 176 | __func__, region_offset(&cpus[0].stub_code), |
| 177 | region_offset(&cpus[i].stub_code), size); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 178 | } |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 179 | } |
| 180 | |
Arthur Heymans | 96451a7 | 2021-10-28 15:14:18 +0200 | [diff] [blame] | 181 | static uintptr_t stack_top; |
| 182 | static size_t g_stack_size; |
| 183 | |
| 184 | int smm_setup_stack(const uintptr_t perm_smbase, const size_t perm_smram_size, |
| 185 | const unsigned int total_cpus, const size_t stack_size) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 186 | { |
Arthur Heymans | 96451a7 | 2021-10-28 15:14:18 +0200 | [diff] [blame] | 187 | /* Need a minimum stack size and alignment. */ |
| 188 | if (stack_size <= SMM_MINIMUM_STACK_SIZE || (stack_size & 3) != 0) { |
| 189 | printk(BIOS_ERR, "%s: need minimum stack size\n", __func__); |
| 190 | return -1; |
| 191 | } |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 192 | |
Arthur Heymans | 96451a7 | 2021-10-28 15:14:18 +0200 | [diff] [blame] | 193 | const size_t total_stack_size = total_cpus * stack_size; |
| 194 | if (total_stack_size >= perm_smram_size) { |
| 195 | printk(BIOS_ERR, "%s: Stack won't fit smram\n", __func__); |
| 196 | return -1; |
| 197 | } |
| 198 | stack_top = perm_smbase + total_stack_size; |
| 199 | g_stack_size = stack_size; |
| 200 | return 0; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 201 | } |
| 202 | |
| 203 | /* |
| 204 | * Place the staggered entry points for each CPU. The entry points are |
| 205 | * staggered by the per CPU SMM save state size extending down from |
| 206 | * SMM_ENTRY_OFFSET. |
| 207 | */ |
Arthur Heymans | 346db92 | 2022-04-07 21:52:59 +0200 | [diff] [blame] | 208 | static void smm_stub_place_staggered_entry_points(const struct smm_loader_params *params) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 209 | { |
Arthur Heymans | 346db92 | 2022-04-07 21:52:59 +0200 | [diff] [blame] | 210 | if (params->num_concurrent_save_states > 1) |
| 211 | smm_place_entry_code(params->num_concurrent_save_states); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | /* |
| 215 | * The stub setup code assumes it is completely contained within the |
| 216 | * default SMRAM size (0x10000) for the default SMI handler (entry at |
| 217 | * 0x30000), but no assumption should be made for the permanent SMI handler. |
| 218 | * The placement of CPU entry points for permanent handler are determined |
| 219 | * by the number of CPUs in the system and the amount of SMRAM. |
Arthur Heymans | e6c3523 | 2021-02-16 13:19:18 +0100 | [diff] [blame] | 220 | * There are potentially 2 regions to place |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 221 | * within the default SMRAM size: |
| 222 | * 1. Save state areas |
| 223 | * 2. Stub code |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 224 | * |
Arthur Heymans | e6c3523 | 2021-02-16 13:19:18 +0100 | [diff] [blame] | 225 | * The save state always lives at the top of the CPUS smbase (and the entry |
| 226 | * point is at offset 0x8000). This allows only a certain number of CPUs with |
| 227 | * staggered entry points until the save state area comes down far enough to |
| 228 | * overwrite/corrupt the entry code (stub code). Therefore, an SMM map is |
| 229 | * created to avoid this corruption, see smm_create_map() above. |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 230 | * This module setup code works for the default (0x30000) SMM handler setup and the |
| 231 | * permanent SMM handler. |
Arthur Heymans | e6c3523 | 2021-02-16 13:19:18 +0100 | [diff] [blame] | 232 | * The CPU stack is decided at runtime in the stub and is treaded as a continuous |
| 233 | * region. As this might not fit the default SMRAM region, the same region used |
Arthur Heymans | 96451a7 | 2021-10-28 15:14:18 +0200 | [diff] [blame] | 234 | * by the permanent handler can be used during relocation. |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 235 | */ |
Arthur Heymans | cfd3224 | 2021-10-28 13:59:54 +0200 | [diff] [blame] | 236 | static int smm_module_setup_stub(const uintptr_t smbase, const size_t smm_size, |
Arthur Heymans | 1efca4d | 2023-05-17 18:10:47 +0200 | [diff] [blame] | 237 | struct smm_loader_params *params) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 238 | { |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 239 | struct rmodule smm_stub; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 240 | if (rmodule_parse(&_binary_smmstub_start, &smm_stub)) { |
| 241 | printk(BIOS_ERR, "%s: unable to parse smm stub\n", __func__); |
| 242 | return -1; |
| 243 | } |
Arthur Heymans | d7c3716 | 2022-04-07 21:41:26 +0200 | [diff] [blame] | 244 | const size_t stub_size = rmodule_memory_size(&smm_stub); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 245 | |
Arthur Heymans | d7c3716 | 2022-04-07 21:41:26 +0200 | [diff] [blame] | 246 | /* Some sanity check */ |
| 247 | if (stub_size >= SMM_ENTRY_OFFSET) { |
| 248 | printk(BIOS_ERR, "%s: Stub too large\n", __func__); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 249 | return -1; |
| 250 | } |
| 251 | |
Arthur Heymans | d7c3716 | 2022-04-07 21:41:26 +0200 | [diff] [blame] | 252 | const uintptr_t smm_stub_loc = smbase + SMM_ENTRY_OFFSET; |
Arthur Heymans | cfd3224 | 2021-10-28 13:59:54 +0200 | [diff] [blame] | 253 | if (rmodule_load((void *)smm_stub_loc, &smm_stub)) { |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 254 | printk(BIOS_ERR, "%s: load module failed\n", __func__); |
| 255 | return -1; |
| 256 | } |
| 257 | |
Arthur Heymans | d7c3716 | 2022-04-07 21:41:26 +0200 | [diff] [blame] | 258 | struct smm_stub_params *stub_params = rmodule_parameters(&smm_stub); |
Arthur Heymans | 96451a7 | 2021-10-28 15:14:18 +0200 | [diff] [blame] | 259 | stub_params->stack_top = stack_top; |
| 260 | stub_params->stack_size = g_stack_size; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 261 | stub_params->c_handler = (uintptr_t)params->handler; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 262 | |
Arthur Heymans | a804f91 | 2022-05-30 14:39:45 +0200 | [diff] [blame] | 263 | /* This runs on the BSP. All the APs are its siblings */ |
| 264 | struct cpu_info *info = cpu_info(); |
| 265 | if (!info || !info->cpu) { |
| 266 | printk(BIOS_ERR, "%s: Failed to find BSP struct device\n", __func__); |
| 267 | return -1; |
| 268 | } |
| 269 | int i = 0; |
| 270 | for (struct device *dev = info->cpu; dev; dev = dev->sibling) |
| 271 | if (dev->enabled) |
| 272 | stub_params->apic_id_to_cpu[i++] = dev->path.apic.initial_lapicid; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 273 | |
Arthur Heymans | a804f91 | 2022-05-30 14:39:45 +0200 | [diff] [blame] | 274 | if (i != params->num_cpus) { |
| 275 | printk(BIOS_ERR, "%s: Failed to set up apic map correctly\n", __func__); |
| 276 | return -1; |
| 277 | } |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 278 | |
Arthur Heymans | d7c3716 | 2022-04-07 21:41:26 +0200 | [diff] [blame] | 279 | printk(BIOS_DEBUG, "%s: stack_top = 0x%x\n", __func__, stub_params->stack_top); |
| 280 | printk(BIOS_DEBUG, "%s: per cpu stack_size = 0x%x\n", __func__, |
| 281 | stub_params->stack_size); |
Arthur Heymans | d7c3716 | 2022-04-07 21:41:26 +0200 | [diff] [blame] | 282 | printk(BIOS_DEBUG, "%s: runtime.smm_size = 0x%zx\n", __func__, smm_size); |
| 283 | |
Arthur Heymans | 346db92 | 2022-04-07 21:52:59 +0200 | [diff] [blame] | 284 | smm_stub_place_staggered_entry_points(params); |
Arthur Heymans | d7c3716 | 2022-04-07 21:41:26 +0200 | [diff] [blame] | 285 | |
| 286 | printk(BIOS_DEBUG, "SMM Module: stub loaded at %lx. Will call %p\n", smm_stub_loc, |
| 287 | params->handler); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 288 | return 0; |
| 289 | } |
| 290 | |
| 291 | /* |
| 292 | * smm_setup_relocation_handler assumes the callback is already loaded in |
| 293 | * memory. i.e. Another SMM module isn't chained to the stub. The other |
| 294 | * assumption is that the stub will be entered from the default SMRAM |
| 295 | * location: 0x30000 -> 0x40000. |
| 296 | */ |
Arthur Heymans | 96451a7 | 2021-10-28 15:14:18 +0200 | [diff] [blame] | 297 | int smm_setup_relocation_handler(struct smm_loader_params *params) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 298 | { |
Arthur Heymans | cfd3224 | 2021-10-28 13:59:54 +0200 | [diff] [blame] | 299 | uintptr_t smram = SMM_DEFAULT_BASE; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 300 | printk(BIOS_SPEW, "%s: enter\n", __func__); |
| 301 | /* There can't be more than 1 concurrent save state for the relocation |
| 302 | * handler because all CPUs default to 0x30000 as SMBASE. */ |
| 303 | if (params->num_concurrent_save_states > 1) |
| 304 | return -1; |
| 305 | |
| 306 | /* A handler has to be defined to call for relocation. */ |
| 307 | if (params->handler == NULL) |
| 308 | return -1; |
| 309 | |
| 310 | /* Since the relocation handler always uses stack, adjust the number |
| 311 | * of concurrent stack users to be CONFIG_MAX_CPUS. */ |
Arthur Heymans | 2412c81 | 2021-10-28 15:19:39 +0200 | [diff] [blame] | 312 | if (params->num_cpus == 0) |
| 313 | params->num_cpus = CONFIG_MAX_CPUS; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 314 | |
John Zhao | 457c661 | 2021-04-21 10:13:17 -0700 | [diff] [blame] | 315 | printk(BIOS_SPEW, "%s: exit\n", __func__); |
Arthur Heymans | 1efca4d | 2023-05-17 18:10:47 +0200 | [diff] [blame] | 316 | return smm_module_setup_stub(smram, SMM_DEFAULT_SIZE, params); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 317 | } |
| 318 | |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 319 | static void setup_smihandler_params(struct smm_runtime *mod_params, |
| 320 | uintptr_t smram_base, |
| 321 | uintptr_t smram_size, |
| 322 | struct smm_loader_params *loader_params) |
| 323 | { |
| 324 | mod_params->smbase = smram_base; |
| 325 | mod_params->smm_size = smram_size; |
Arthur Heymans | 1684b0a | 2022-04-07 21:50:16 +0200 | [diff] [blame] | 326 | mod_params->save_state_size = loader_params->cpu_save_state_size; |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 327 | mod_params->num_cpus = loader_params->num_cpus; |
| 328 | mod_params->gnvs_ptr = (uint32_t)(uintptr_t)acpi_get_gnvs(); |
| 329 | const struct cbmem_entry *cbmemc; |
| 330 | if (CONFIG(CONSOLE_CBMEM) && (cbmemc = cbmem_entry_find(CBMEM_ID_CONSOLE))) { |
| 331 | mod_params->cbmemc = cbmem_entry_start(cbmemc); |
| 332 | mod_params->cbmemc_size = cbmem_entry_size(cbmemc); |
| 333 | } else { |
| 334 | mod_params->cbmemc = 0; |
| 335 | mod_params->cbmemc_size = 0; |
| 336 | } |
| 337 | |
Arthur Heymans | cb361da | 2022-04-07 21:20:50 +0200 | [diff] [blame] | 338 | for (int i = 0; i < loader_params->num_cpus; i++) |
Arthur Heymans | 0effeb5 | 2022-05-23 22:43:51 +0200 | [diff] [blame] | 339 | mod_params->save_state_top[i] = region_end(&cpus[i].ss); |
Johnny Lin | 107e7aa | 2021-01-14 17:49:08 +0800 | [diff] [blame] | 340 | |
| 341 | if (CONFIG(RUNTIME_CONFIGURABLE_SMM_LOGLEVEL)) |
| 342 | mod_params->smm_log_level = mainboard_set_smm_log_level(); |
| 343 | else |
| 344 | mod_params->smm_log_level = 0; |
Robert Zieba | ac8c378 | 2022-09-07 16:25:15 -0600 | [diff] [blame] | 345 | |
| 346 | if (CONFIG(SMM_PCI_RESOURCE_STORE)) |
| 347 | smm_pci_resource_store_init(mod_params); |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 348 | } |
Arthur Heymans | b4ba289 | 2021-10-28 16:48:36 +0200 | [diff] [blame] | 349 | |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 350 | static void print_region(const char *name, const struct region region) |
| 351 | { |
Elyes Haouas | 1f07797 | 2022-06-15 15:36:03 +0200 | [diff] [blame] | 352 | printk(BIOS_DEBUG, "%-12s [0x%zx-0x%zx]\n", name, region_offset(®ion), |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 353 | region_end(®ion)); |
| 354 | } |
| 355 | |
Arthur Heymans | 1efca4d | 2023-05-17 18:10:47 +0200 | [diff] [blame] | 356 | /* STM + Handler + (Stub + Save state) * CONFIG_MAX_CPUS + stacks */ |
| 357 | #define SMM_REGIONS_ARRAY_SIZE (1 + 1 + CONFIG_MAX_CPUS * 2 + 1) |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 358 | |
| 359 | static int append_and_check_region(const struct region smram, |
| 360 | const struct region region, |
| 361 | struct region *region_list, |
| 362 | const char *name) |
| 363 | { |
| 364 | unsigned int region_counter = 0; |
| 365 | for (; region_counter < SMM_REGIONS_ARRAY_SIZE; region_counter++) |
Nico Huber | d7612e9 | 2024-01-11 18:50:50 +0100 | [diff] [blame^] | 366 | if (region_sz(®ion_list[region_counter]) == 0) |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 367 | break; |
| 368 | |
| 369 | if (region_counter >= SMM_REGIONS_ARRAY_SIZE) { |
| 370 | printk(BIOS_ERR, "Array used to check regions too small\n"); |
| 371 | return 1; |
| 372 | } |
| 373 | |
| 374 | if (!region_is_subregion(&smram, ®ion)) { |
| 375 | printk(BIOS_ERR, "%s not in SMM\n", name); |
| 376 | return 1; |
| 377 | } |
| 378 | |
| 379 | print_region(name, region); |
| 380 | for (unsigned int i = 0; i < region_counter; i++) { |
| 381 | if (region_overlap(®ion_list[i], ®ion)) { |
| 382 | printk(BIOS_ERR, "%s overlaps with a previous region\n", name); |
| 383 | return 1; |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | region_list[region_counter] = region; |
| 388 | |
| 389 | return 0; |
| 390 | } |
| 391 | |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 392 | /* |
| 393 | *The SMM module is placed within the provided region in the following |
| 394 | * manner: |
| 395 | * +-----------------+ <- smram + size |
| 396 | * | BIOS resource | |
| 397 | * | list (STM) | |
| 398 | * +-----------------+ |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 399 | * | smi handler | |
| 400 | * | ... | |
| 401 | * +-----------------+ <- cpu0 |
| 402 | * | stub code | <- cpu1 |
| 403 | * | stub code | <- cpu2 |
| 404 | * | stub code | <- cpu3, etc |
| 405 | * | | |
| 406 | * | | |
| 407 | * | | |
| 408 | * | stacks | |
| 409 | * +-----------------+ <- smram start |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 410 | * |
| 411 | * With CONFIG(SMM_TSEG) the stubs will be placed in the same segment as the |
| 412 | * permanent handler and the stacks. |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 413 | */ |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 414 | int smm_load_module(const uintptr_t smram_base, const size_t smram_size, |
| 415 | struct smm_loader_params *params) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 416 | { |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 417 | /* |
| 418 | * Place in .bss to reduce stack usage. |
| 419 | * TODO: once CPU_INFO_V2 is used everywhere, use smaller stack for APs and move |
| 420 | * this back to the BSP stack. |
| 421 | */ |
| 422 | static struct region region_list[SMM_REGIONS_ARRAY_SIZE] = {}; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 423 | |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 424 | struct rmodule smi_handler; |
| 425 | if (rmodule_parse(&_binary_smm_start, &smi_handler)) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 426 | return -1; |
| 427 | |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 428 | const struct region smram = { .offset = smram_base, .size = smram_size }; |
| 429 | const uintptr_t smram_top = region_end(&smram); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 430 | |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 431 | const size_t stm_size = |
Eugene Myers | a19ff6d | 2022-09-15 15:33:58 -0400 | [diff] [blame] | 432 | CONFIG(STM) ? CONFIG_MSEG_SIZE + CONFIG_BIOS_RESOURCE_LIST_SIZE : 0; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 433 | |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 434 | if (CONFIG(STM)) { |
| 435 | struct region stm = {}; |
| 436 | stm.offset = smram_top - stm_size; |
| 437 | stm.size = stm_size; |
| 438 | if (append_and_check_region(smram, stm, region_list, "STM")) |
| 439 | return -1; |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 440 | printk(BIOS_DEBUG, "MSEG size 0x%x\n", CONFIG_MSEG_SIZE); |
| 441 | printk(BIOS_DEBUG, "BIOS res list 0x%x\n", CONFIG_BIOS_RESOURCE_LIST_SIZE); |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 442 | } |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 443 | |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 444 | const size_t handler_size = rmodule_memory_size(&smi_handler); |
| 445 | const size_t handler_alignment = rmodule_load_alignment(&smi_handler); |
| 446 | const uintptr_t handler_base = |
Arthur Heymans | 1efca4d | 2023-05-17 18:10:47 +0200 | [diff] [blame] | 447 | ALIGN_DOWN(smram_top - stm_size - handler_size, |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 448 | handler_alignment); |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 449 | struct region handler = { |
| 450 | .offset = handler_base, |
| 451 | .size = handler_size |
| 452 | }; |
| 453 | if (append_and_check_region(smram, handler, region_list, "HANDLER")) |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 454 | return -1; |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 455 | |
Arthur Heymans | f874fc2 | 2022-11-04 20:20:49 +0100 | [diff] [blame] | 456 | uintptr_t stub_segment_base = handler_base - SMM_CODE_SEGMENT_SIZE; |
Rocky Phagura | afb7a81 | 2020-07-21 14:48:48 -0700 | [diff] [blame] | 457 | |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 458 | if (!smm_create_map(stub_segment_base, params->num_concurrent_save_states, params)) { |
Arthur Heymans | dfff5c2 | 2021-02-15 23:39:01 +0100 | [diff] [blame] | 459 | printk(BIOS_ERR, "%s: Error creating CPU map\n", __func__); |
| 460 | return -1; |
| 461 | } |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 462 | for (unsigned int i = 0; i < params->num_concurrent_save_states; i++) { |
| 463 | printk(BIOS_DEBUG, "\nCPU %u\n", i); |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 464 | char string[13]; |
| 465 | snprintf(string, sizeof(string), " ss%d", i); |
Arthur Heymans | 0effeb5 | 2022-05-23 22:43:51 +0200 | [diff] [blame] | 466 | if (append_and_check_region(smram, cpus[i].ss, region_list, string)) |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 467 | return -1; |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 468 | snprintf(string, sizeof(string), " stub%d", i); |
Arthur Heymans | 0effeb5 | 2022-05-23 22:43:51 +0200 | [diff] [blame] | 469 | if (append_and_check_region(smram, cpus[i].stub_code, region_list, string)) |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 470 | return -1; |
Arthur Heymans | 64d9e85 | 2021-02-15 18:55:40 +0100 | [diff] [blame] | 471 | } |
| 472 | |
Arthur Heymans | af04f3c | 2022-04-13 02:10:15 +0200 | [diff] [blame] | 473 | struct region stacks = { |
| 474 | .offset = smram_base, |
| 475 | .size = params->num_concurrent_save_states * CONFIG_SMM_MODULE_STACK_SIZE |
| 476 | }; |
| 477 | printk(BIOS_DEBUG, "\n"); |
| 478 | if (append_and_check_region(smram, stacks, region_list, "stacks")) |
| 479 | return -1; |
| 480 | |
Arthur Heymans | 5747f6c | 2022-04-07 20:54:26 +0200 | [diff] [blame] | 481 | if (rmodule_load((void *)handler_base, &smi_handler)) |
| 482 | return -1; |
| 483 | |
| 484 | struct smm_runtime *smihandler_params = rmodule_parameters(&smi_handler); |
| 485 | params->handler = rmodule_entry(&smi_handler); |
| 486 | setup_smihandler_params(smihandler_params, smram_base, smram_size, params); |
| 487 | |
Arthur Heymans | 1efca4d | 2023-05-17 18:10:47 +0200 | [diff] [blame] | 488 | return smm_module_setup_stub(stub_segment_base, smram_size, params); |
Arthur Heymans | b4ba289 | 2021-10-28 16:48:36 +0200 | [diff] [blame] | 489 | } |