blob: cf6bf16413cbb54aee42551ecbdfa135c743289d [file] [log] [blame]
Yinghai Lu72ee9b02005-12-14 02:39:33 +00001/*
Martin Roth6add44b2017-02-09 17:51:20 -08002 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2005 Yinghai Lu
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
Yinghai Lu72ee9b02005-12-14 02:39:33 +000015
Aaron Durbin0f35af82018-04-18 01:00:27 -060016#include <cbfs.h>
Stefan Reinauer6a001132017-07-13 02:20:27 +020017#include <compiler.h>
Aaron Durbin696c6422018-04-18 01:02:47 -060018#include <commonlib/helpers.h>
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000019#include <console/console.h>
20#include <cpu/cpu.h>
Aaron Durbin7f5e7342018-04-17 10:55:14 -060021#include <arch/cpu.h>
Aaron Durbind5e47462018-04-17 14:35:48 -060022#include <cpu/x86/cr.h>
Aaron Durbinae18f802018-04-17 11:37:28 -060023#include <cpu/x86/msr.h>
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000024#include <cpu/x86/pae.h>
Aaron Durbin7f5e7342018-04-17 10:55:14 -060025#include <rules.h>
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000026#include <string.h>
Aaron Durbin0f35af82018-04-18 01:00:27 -060027#include <symbols.h>
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000028
Aaron Durbin696c6422018-04-18 01:02:47 -060029#define PDPTE_PRES (1ULL << 0)
30#define PDPTE_ADDR_MASK (~((1ULL << 12) - 1))
31
32#define PDE_PRES (1ULL << 0)
33#define PDE_RW (1ULL << 1)
34#define PDE_US (1ULL << 2)
35#define PDE_PWT (1ULL << 3)
36#define PDE_PCD (1ULL << 4)
37#define PDE_A (1ULL << 5)
38#define PDE_D (1ULL << 6) // only valid with PS=1
39#define PDE_PS (1ULL << 7)
40#define PDE_G (1ULL << 8) // only valid with PS=1
41#define PDE_PAT (1ULL << 12) // only valid with PS=1
42#define PDE_XD (1ULL << 63)
43#define PDE_ADDR_MASK (~((1ULL << 12) - 1))
44
45#define PTE_PRES (1ULL << 0)
46#define PTE_RW (1ULL << 1)
47#define PTE_US (1ULL << 2)
48#define PTE_PWT (1ULL << 3)
49#define PTE_PCD (1ULL << 4)
50#define PTE_A (1ULL << 5)
51#define PTE_D (1ULL << 6)
52#define PTE_PAT (1ULL << 7)
53#define PTE_G (1ULL << 8)
54#define PTE_XD (1ULL << 63)
55
56#define PDPTE_IDX_SHIFT 30
57#define PDPTE_IDX_MASK 0x3
58
59#define PDE_IDX_SHIFT 21
60#define PDE_IDX_MASK 0x1ff
61
62#define PTE_IDX_SHIFT 12
63#define PTE_IDX_MASK 0x1ff
64
65static const size_t s2MiB = 2 * MiB;
66static const size_t s4KiB = 4 * KiB;
67
Aaron Durbind5e47462018-04-17 14:35:48 -060068void paging_enable_pae_cr3(uintptr_t cr3)
69{
70 /* Load the page table address */
71 write_cr3(cr3);
72 paging_enable_pae();
73}
74
75void paging_enable_pae(void)
76{
77 CRx_TYPE cr0;
78 CRx_TYPE cr4;
79
80 /* Enable PAE */
81 cr4 = read_cr4();
82 cr4 |= CR4_PAE;
83 write_cr4(cr4);
84
85 /* Enable Paging */
86 cr0 = read_cr0();
87 cr0 |= CR0_PG;
88 write_cr0(cr0);
89}
90
91void paging_disable_pae(void)
92{
93 CRx_TYPE cr0;
94 CRx_TYPE cr4;
95
96 /* Disable Paging */
97 cr0 = read_cr0();
98 cr0 &= ~(CRx_TYPE)CR0_PG;
99 write_cr0(cr0);
100
101 /* Disable PAE */
102 cr4 = read_cr4();
103 cr4 &= ~(CRx_TYPE)CR4_PAE;
104 write_cr4(cr4);
105}
106
Aaron Durbin7f5e7342018-04-17 10:55:14 -0600107#if ENV_RAMSTAGE
Stefan Reinauer14e22772010-04-27 06:56:47 +0000108void *map_2M_page(unsigned long page)
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000109{
110 struct pde {
111 uint32_t addr_lo;
112 uint32_t addr_hi;
Stefan Reinauer6a001132017-07-13 02:20:27 +0200113 } __packed;
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000114 struct pg_table {
115 struct pde pd[2048];
116 struct pde pdp[512];
Stefan Reinauer6a001132017-07-13 02:20:27 +0200117 } __packed;
Yinghai Lu72ee9b02005-12-14 02:39:33 +0000118
Lee Leahyc5917072017-03-15 16:38:51 -0700119 static struct pg_table pgtbl[CONFIG_MAX_CPUS]
Stefan Reinauer6a001132017-07-13 02:20:27 +0200120 __attribute__((aligned(4096)));
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000121 static unsigned long mapped_window[CONFIG_MAX_CPUS];
122 unsigned long index;
123 unsigned long window;
124 void *result;
125 int i;
126 index = cpu_index();
Lee Leahya15d8af2017-03-15 14:49:35 -0700127 if (index >= CONFIG_MAX_CPUS)
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000128 return MAPPING_ERROR;
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000129 window = page >> 10;
130 if (window != mapped_window[index]) {
Aaron Durbind5e47462018-04-17 14:35:48 -0600131 paging_disable_pae();
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000132 if (window > 1) {
133 struct pde *pd, *pdp;
Lee Leahyc5917072017-03-15 16:38:51 -0700134 /* Point the page directory pointers at the page
135 * directories
136 */
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000137 memset(&pgtbl[index].pdp, 0, sizeof(pgtbl[index].pdp));
138 pd = pgtbl[index].pd;
139 pdp = pgtbl[index].pdp;
140 pdp[0].addr_lo = ((uint32_t)&pd[512*0])|1;
141 pdp[1].addr_lo = ((uint32_t)&pd[512*1])|1;
142 pdp[2].addr_lo = ((uint32_t)&pd[512*2])|1;
143 pdp[3].addr_lo = ((uint32_t)&pd[512*3])|1;
Lee Leahyc5917072017-03-15 16:38:51 -0700144 /* The first half of the page table is identity mapped
145 */
Elyes HAOUAScbe7464c2016-08-23 21:07:28 +0200146 for (i = 0; i < 1024; i++) {
Lee Leahy8bad6d22017-03-15 14:15:38 -0700147 pd[i].addr_lo = ((i & 0x3ff) << 21) | 0xE3;
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000148 pd[i].addr_hi = 0;
149 }
Lee Leahyc5917072017-03-15 16:38:51 -0700150 /* The second half of the page table holds the mapped
151 * page
152 */
Elyes HAOUAScbe7464c2016-08-23 21:07:28 +0200153 for (i = 1024; i < 2048; i++) {
Lee Leahyc5917072017-03-15 16:38:51 -0700154 pd[i].addr_lo = ((window & 1) << 31)
155 | ((i & 0x3ff) << 21) | 0xE3;
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000156 pd[i].addr_hi = (window >> 1);
157 }
Aaron Durbind5e47462018-04-17 14:35:48 -0600158 paging_enable_pae_cr3((uintptr_t)pdp);
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000159 }
160 mapped_window[index] = window;
161 }
Lee Leahya15d8af2017-03-15 14:49:35 -0700162 if (window == 0)
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000163 result = (void *)(page << 21);
Lee Leahya15d8af2017-03-15 14:49:35 -0700164 else
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000165 result = (void *)(0x80000000 | ((page & 0x3ff) << 21));
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000166 return result;
167}
Aaron Durbin7f5e7342018-04-17 10:55:14 -0600168#endif
Aaron Durbinae18f802018-04-17 11:37:28 -0600169
170void paging_set_nxe(int enable)
171{
172 msr_t msr = rdmsr(IA32_EFER);
173
174 if (enable)
175 msr.lo |= EFER_NXE;
176 else
177 msr.lo &= ~EFER_NXE;
178
179 wrmsr(IA32_EFER, msr);
180}
181
182void paging_set_pat(uint64_t pat)
183{
184 msr_t msr;
185 msr.lo = pat;
186 msr.hi = pat >> 32;
187 wrmsr(MSR_IA32_PAT, msr);
188}
Aaron Durbinc82e48d2018-04-17 11:45:32 -0600189
190/* PAT encoding used in util/x86/x86_page_tables.go. It matches the linux
191 * kernel settings:
192 * PTE encoding:
193 * PAT
194 * |PCD
195 * ||PWT PAT
196 * ||| slot
197 * 000 0 WB : _PAGE_CACHE_MODE_WB
198 * 001 1 WC : _PAGE_CACHE_MODE_WC
199 * 010 2 UC-: _PAGE_CACHE_MODE_UC_MINUS
200 * 011 3 UC : _PAGE_CACHE_MODE_UC
201 * 100 4 WB : Reserved
202 * 101 5 WP : _PAGE_CACHE_MODE_WP
203 * 110 6 UC-: Reserved
204 * 111 7 WT : _PAGE_CACHE_MODE_WT
205 */
206void paging_set_default_pat(void)
207{
208 uint64_t pat = PAT_ENCODE(WB, 0) | PAT_ENCODE(WC, 1) |
209 PAT_ENCODE(UC_MINUS, 2) | PAT_ENCODE(UC, 3) |
210 PAT_ENCODE(WB, 4) | PAT_ENCODE(WP, 5) |
211 PAT_ENCODE(UC_MINUS, 6) | PAT_ENCODE(WT, 7);
212 paging_set_pat(pat);
213}
Aaron Durbin0f35af82018-04-18 01:00:27 -0600214
215static int read_from_cbfs(const char *name, void *buf, size_t size)
216{
217 struct cbfsf fh;
218 struct region_device rdev;
219 size_t rdev_sz;
220
221 if (cbfs_boot_locate(&fh, name, NULL))
222 return -1;
223
224 cbfs_file_data(&rdev, &fh);
225
226 rdev_sz = region_device_sz(&rdev);
227
228 if (size < rdev_sz) {
229 printk(BIOS_ERR, "%s region too small to load: %zx < %zx\n",
230 name, size, rdev_sz);
231 return -1;
232 }
233
234 if (rdev_readat(&rdev, buf, 0, rdev_sz) != rdev_sz)
235 return -1;
236
237 return 0;
238}
239
240int paging_enable_for_car(const char *pdpt_name, const char *pt_name)
241{
242 if (!ENV_CACHE_AS_RAM)
243 return -1;
244
245 if (read_from_cbfs(pdpt_name, _pdpt, _pdpt_size)) {
246 printk(BIOS_ERR, "Couldn't load pdpt\n");
247 return -1;
248 }
249
250 if (read_from_cbfs(pt_name, _pagetables, _pagetables_size)) {
251 printk(BIOS_ERR, "Couldn't load page tables\n");
252 return -1;
253 }
254
255 paging_enable_pae_cr3((uintptr_t)_pdpt);
256
257 return 0;
258}
Aaron Durbin696c6422018-04-18 01:02:47 -0600259
260static void *get_pdpt_addr(void)
261{
262 if (ENV_CACHE_AS_RAM)
263 return _pdpt;
264 return (void *)(uintptr_t)read_cr3();
265}
266
267static uint64_t pde_pat_flags(int pat)
268{
269 switch (pat) {
270 case PAT_UC:
271 return 0 | PDE_PCD | PDE_PWT;
272 case PAT_WC:
273 return 0 | 0 | PDE_PWT;
274 case PAT_WT:
275 return PDE_PAT | PDE_PCD | PDE_PWT;
276 case PAT_WP:
277 return PDE_PAT | 0 | PDE_PWT;
278 case PAT_WB:
279 return 0 | 0 | 0;
280 case PAT_UC_MINUS:
281 return 0 | PDE_PCD | 0;
282 default:
283 printk(BIOS_ERR, "PDE PAT defaulting to WB: %x\n", pat);
284 return pde_pat_flags(PAT_WB);
285 }
286}
287
288static uint64_t pde_page_flags(int pat)
289{
290 uint64_t flags = PDE_PS | PDE_PRES | PDE_RW | PDE_A | PDE_D;
291
292 return flags | pde_pat_flags(pat);
293}
294
295static uint64_t pte_pat_flags(int pat)
296{
297 switch (pat) {
298 case PAT_UC:
299 return 0 | PTE_PCD | PTE_PWT;
300 case PAT_WC:
301 return 0 | 0 | PTE_PWT;
302 case PAT_WT:
303 return PTE_PAT | PTE_PCD | PTE_PWT;
304 case PAT_WP:
305 return PTE_PAT | 0 | PTE_PWT;
306 case PAT_WB:
307 return 0 | 0 | 0;
308 case PAT_UC_MINUS:
309 return 0 | PTE_PCD | 0;
310 default:
311 printk(BIOS_ERR, "PTE PAT defaulting to WB: %x\n", pat);
312 return pte_pat_flags(PAT_WB);
313 }
314}
315
316static uint64_t pte_page_flags(int pat)
317{
318 uint64_t flags = PTE_PRES | PTE_RW | PTE_A | PTE_D;
319 return flags | pte_pat_flags(pat);
320}
321
322/* Identity map an address. This function does not handle splitting or adding
323 * new pages to the page tables. It's assumed all the page tables are already
324 * seeded with the correct amount and topology. */
325static int identity_map_one_page(uintptr_t base, size_t size, int pat,
326 int commit)
327{
328 uint64_t (*pdpt)[4];
329 uint64_t pdpte;
330 uint64_t (*pd)[512];
331 uint64_t pde;
332
333 pdpt = get_pdpt_addr();
334
335 pdpte = (*pdpt)[(base >> PDPTE_IDX_SHIFT) & PDPTE_IDX_MASK];
336
337 /* No page table page allocation. */
338 if (!(pdpte & PDPTE_PRES))
339 return -1;
340
341 pd = (void *)(uintptr_t)(pdpte & PDPTE_ADDR_MASK);
342
343 /* Map in a 2MiB page. */
344 if (size == s2MiB) {
345 if (!commit)
346 return 0;
347 pde = base;
348 pde |= pde_page_flags(pat);
349 (*pd)[(base >> PDE_IDX_SHIFT) & PDE_IDX_MASK] = pde;
350 return 0;
351 }
352
353 if (size == s4KiB) {
354 uint64_t (*pt)[512];
355 uint64_t pte;
356
357 pde = (*pd)[(base >> PDE_IDX_SHIFT) & PDE_IDX_MASK];
358
359 /* No page table page allocation. */
360 if (!(pde & PDE_PRES)) {
361 printk(BIOS_ERR, "Cannot allocate page table for pde %p\n",
362 (void *)base);
363 return -1;
364 }
365
366 /* No splitting pages */
367 if (pde & PDE_PS) {
368 printk(BIOS_ERR, "Cannot split pde %p\n", (void *)base);
369 return -1;
370 }
371
372 if (!commit)
373 return 0;
374
375 pt = (void *)(uintptr_t)(pde & PDE_ADDR_MASK);
376 pte = base;
377 pte |= pte_page_flags(pat);
378 (*pt)[(base >> PTE_IDX_SHIFT) & PTE_IDX_MASK] = pte;
379
380 return 0;
381 }
382
383 return -1;
384}
385
386static int _paging_identity_map_addr(uintptr_t base, size_t size, int pat,
387 int commit)
388{
389 while (size != 0) {
390 size_t map_size;
391
392 map_size = IS_ALIGNED(base, s2MiB) ? s2MiB : s4KiB;
393 map_size = MIN(size, map_size);
394
395 if (identity_map_one_page(base, map_size, pat, commit) < 0)
396 return -1;
397
398 base += map_size;
399 size -= map_size;
400 }
401
402 return 0;
403}
404
405static int paging_is_enabled(void)
406{
407 return !!(read_cr0() & CR0_PG);
408}
409
410int paging_identity_map_addr(uintptr_t base, size_t size, int pat)
411{
412 if (!paging_is_enabled()) {
413 printk(BIOS_ERR, "Paging is not enabled.\n");
414 return -1;
415 }
416
417 if (!IS_ALIGNED(base, s2MiB) && !IS_ALIGNED(base, s4KiB)) {
418 printk(BIOS_ERR, "base %p is not aligned.\n", (void *)base);
419 return -1;
420 }
421
422 if (!IS_ALIGNED(size, s2MiB) && !IS_ALIGNED(size, s4KiB)) {
423 printk(BIOS_ERR, "size %zx is not aligned.\n", size);
424 return -1;
425 }
426
427 /* First try without committing. If success commit. */
428 if (_paging_identity_map_addr(base, size, pat, 0))
429 return -1;
430
431 return _paging_identity_map_addr(base, size, pat, 1);
432}