blob: c11101b44ece25611b8c23c8aa401fc911bcaa07 [file] [log] [blame]
Gabe Blackd64c6ad2012-10-23 18:04:46 +00001/*
2 * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 *
6 * Alternatively, this software may be distributed under the terms of the
7 * GNU General Public License ("GPL") version 2 as published by the Free
8 * Software Foundation.
9 */
10
11#include <common.h>
Simon Glass0f2af882020-05-10 11:40:05 -060012#include <log.h>
Gabe Blackd64c6ad2012-10-23 18:04:46 +000013#include <physmem.h>
Simon Glassd8d9fec2014-10-10 08:21:52 -060014#include <asm/cpu.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060015#include <asm/global_data.h>
Gabe Blackd64c6ad2012-10-23 18:04:46 +000016#include <linux/compiler.h>
17
Simon Glassbb6306c2013-04-17 16:13:33 +000018DECLARE_GLOBAL_DATA_PTR;
19
Gabe Blackd64c6ad2012-10-23 18:04:46 +000020/* Large pages are 2MB. */
21#define LARGE_PAGE_SIZE ((1 << 20) * 2)
22
23/*
24 * Paging data structures.
25 */
26
27struct pdpe {
28 uint64_t p:1;
29 uint64_t mbz_0:2;
30 uint64_t pwt:1;
31 uint64_t pcd:1;
32 uint64_t mbz_1:4;
33 uint64_t avl:3;
34 uint64_t base:40;
35 uint64_t mbz_2:12;
36};
37
38typedef struct pdpe pdpt_t[512];
39
40struct pde {
41 uint64_t p:1; /* present */
42 uint64_t rw:1; /* read/write */
43 uint64_t us:1; /* user/supervisor */
44 uint64_t pwt:1; /* page-level writethrough */
45 uint64_t pcd:1; /* page-level cache disable */
46 uint64_t a:1; /* accessed */
47 uint64_t d:1; /* dirty */
48 uint64_t ps:1; /* page size */
49 uint64_t g:1; /* global page */
50 uint64_t avl:3; /* available to software */
51 uint64_t pat:1; /* page-attribute table */
52 uint64_t mbz_0:8; /* must be zero */
53 uint64_t base:31; /* base address */
54};
55
56typedef struct pde pdt_t[512];
57
58static pdpt_t pdpt __aligned(4096);
59static pdt_t pdts[4] __aligned(4096);
60
61/*
62 * Map a virtual address to a physical address and optionally invalidate any
63 * old mapping.
64 *
65 * @param virt The virtual address to use.
66 * @param phys The physical address to use.
67 * @param invlpg Whether to use invlpg to clear any old mappings.
68 */
69static void x86_phys_map_page(uintptr_t virt, phys_addr_t phys, int invlpg)
70{
71 /* Extract the two bit PDPT index and the 9 bit PDT index. */
72 uintptr_t pdpt_idx = (virt >> 30) & 0x3;
73 uintptr_t pdt_idx = (virt >> 21) & 0x1ff;
74
75 /* Set up a handy pointer to the appropriate PDE. */
76 struct pde *pde = &(pdts[pdpt_idx][pdt_idx]);
77
78 memset(pde, 0, sizeof(struct pde));
79 pde->p = 1;
80 pde->rw = 1;
81 pde->us = 1;
82 pde->ps = 1;
83 pde->base = phys >> 21;
84
85 if (invlpg) {
86 /* Flush any stale mapping out of the TLBs. */
87 __asm__ __volatile__(
88 "invlpg %0\n\t"
89 :
90 : "m" (*(uint8_t *)virt)
91 );
92 }
93}
94
95/* Identity map the lower 4GB and turn on paging with PAE. */
96static void x86_phys_enter_paging(void)
97{
98 phys_addr_t page_addr;
99 unsigned i;
100
101 /* Zero out the page tables. */
102 memset(pdpt, 0, sizeof(pdpt));
103 memset(pdts, 0, sizeof(pdts));
104
105 /* Set up the PDPT. */
106 for (i = 0; i < ARRAY_SIZE(pdts); i++) {
107 pdpt[i].p = 1;
108 pdpt[i].base = ((uintptr_t)&pdts[i]) >> 12;
109 }
110
111 /* Identity map everything up to 4GB. */
112 for (page_addr = 0; page_addr < (1ULL << 32);
113 page_addr += LARGE_PAGE_SIZE) {
114 /* There's no reason to invalidate the TLB with paging off. */
115 x86_phys_map_page(page_addr, page_addr, 0);
116 }
117
Simon Glassd8d9fec2014-10-10 08:21:52 -0600118 cpu_enable_paging_pae((ulong)pdpt);
Gabe Blackd64c6ad2012-10-23 18:04:46 +0000119}
120
121/* Disable paging and PAE mode. */
122static void x86_phys_exit_paging(void)
123{
Simon Glassd8d9fec2014-10-10 08:21:52 -0600124 cpu_disable_paging_pae();
Gabe Blackd64c6ad2012-10-23 18:04:46 +0000125}
126
127/*
128 * Set physical memory to a particular value when the whole region fits on one
129 * page.
130 *
131 * @param map_addr The address that starts the physical page.
132 * @param offset How far into that page to start setting a value.
133 * @param c The value to set memory to.
134 * @param size The size in bytes of the area to set.
135 */
136static void x86_phys_memset_page(phys_addr_t map_addr, uintptr_t offset, int c,
137 unsigned size)
138{
139 /*
140 * U-Boot should be far away from the beginning of memory, so that's a
141 * good place to map our window on top of.
142 */
143 const uintptr_t window = LARGE_PAGE_SIZE;
144
145 /* Make sure the window is below U-Boot. */
146 assert(window + LARGE_PAGE_SIZE <
147 gd->relocaddr - CONFIG_SYS_MALLOC_LEN - CONFIG_SYS_STACK_SIZE);
148 /* Map the page into the window and then memset the appropriate part. */
149 x86_phys_map_page(window, map_addr, 1);
150 memset((void *)(window + offset), c, size);
151}
152
153/*
154 * A physical memory anologue to memset with matching parameters and return
155 * value.
156 */
157phys_addr_t arch_phys_memset(phys_addr_t start, int c, phys_size_t size)
158{
159 const phys_addr_t max_addr = (phys_addr_t)~(uintptr_t)0;
160 const phys_addr_t orig_start = start;
161
162 if (!size)
163 return orig_start;
164
165 /* Handle memory below 4GB. */
166 if (start <= max_addr) {
Masahiro Yamadab62b39b2014-09-18 13:28:06 +0900167 phys_size_t low_size = min(max_addr + 1 - start, size);
Gabe Blackd64c6ad2012-10-23 18:04:46 +0000168 void *start_ptr = (void *)(uintptr_t)start;
169
170 assert(((phys_addr_t)(uintptr_t)start) == start);
171 memset(start_ptr, c, low_size);
172 start += low_size;
173 size -= low_size;
174 }
175
176 /* Use paging and PAE to handle memory above 4GB up to 64GB. */
177 if (size) {
178 phys_addr_t map_addr = start & ~(LARGE_PAGE_SIZE - 1);
179 phys_addr_t offset = start - map_addr;
180
181 x86_phys_enter_paging();
182
183 /* Handle the first partial page. */
184 if (offset) {
185 phys_addr_t end =
Masahiro Yamadab62b39b2014-09-18 13:28:06 +0900186 min(map_addr + LARGE_PAGE_SIZE, start + size);
Gabe Blackd64c6ad2012-10-23 18:04:46 +0000187 phys_size_t cur_size = end - start;
188 x86_phys_memset_page(map_addr, offset, c, cur_size);
189 size -= cur_size;
190 map_addr += LARGE_PAGE_SIZE;
191 }
192 /* Handle the complete pages. */
193 while (size > LARGE_PAGE_SIZE) {
194 x86_phys_memset_page(map_addr, 0, c, LARGE_PAGE_SIZE);
195 size -= LARGE_PAGE_SIZE;
196 map_addr += LARGE_PAGE_SIZE;
197 }
198 /* Handle the last partial page. */
199 if (size)
200 x86_phys_memset_page(map_addr, 0, c, size);
201
202 x86_phys_exit_paging();
203 }
204 return orig_start;
205}