mm.c revision 1.8 1 1.8 maxv /* $NetBSD: mm.c,v 1.8 2017/11/05 16:26:15 maxv Exp $ */
2 1.1 maxv
3 1.1 maxv /*
4 1.1 maxv * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved.
5 1.1 maxv *
6 1.1 maxv * This code is derived from software contributed to The NetBSD Foundation
7 1.1 maxv * by Maxime Villard.
8 1.1 maxv *
9 1.1 maxv * Redistribution and use in source and binary forms, with or without
10 1.1 maxv * modification, are permitted provided that the following conditions
11 1.1 maxv * are met:
12 1.1 maxv * 1. Redistributions of source code must retain the above copyright
13 1.1 maxv * notice, this list of conditions and the following disclaimer.
14 1.1 maxv * 2. Redistributions in binary form must reproduce the above copyright
15 1.1 maxv * notice, this list of conditions and the following disclaimer in the
16 1.1 maxv * documentation and/or other materials provided with the distribution.
17 1.1 maxv *
18 1.1 maxv * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 1.1 maxv * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 1.1 maxv * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 1.1 maxv * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 1.1 maxv * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 1.1 maxv * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 1.1 maxv * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 1.1 maxv * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 1.1 maxv * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 1.1 maxv * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 1.1 maxv * POSSIBILITY OF SUCH DAMAGE.
29 1.1 maxv */
30 1.1 maxv
31 1.1 maxv #include "prekern.h"
32 1.1 maxv
33 1.1 maxv static const pt_entry_t protection_codes[3] = {
34 1.1 maxv [MM_PROT_READ] = PG_RO | PG_NX,
35 1.1 maxv [MM_PROT_WRITE] = PG_RW | PG_NX,
36 1.1 maxv [MM_PROT_EXECUTE] = PG_RO,
37 1.1 maxv /* RWX does not exist */
38 1.1 maxv };
39 1.1 maxv
40 1.6 maxv struct bootspace bootspace;
41 1.6 maxv
42 1.1 maxv extern paddr_t kernpa_start, kernpa_end;
43 1.1 maxv vaddr_t iom_base;
44 1.1 maxv
45 1.1 maxv paddr_t pa_avail = 0;
46 1.2 maxv static const vaddr_t tmpva = (PREKERNBASE + NKL2_KIMG_ENTRIES * NBPD_L2);
47 1.1 maxv
48 1.1 maxv void
49 1.1 maxv mm_init(paddr_t first_pa)
50 1.1 maxv {
51 1.1 maxv pa_avail = first_pa;
52 1.1 maxv }
53 1.1 maxv
54 1.1 maxv static void
55 1.1 maxv mm_enter_pa(paddr_t pa, vaddr_t va, pte_prot_t prot)
56 1.1 maxv {
57 1.1 maxv PTE_BASE[pl1_i(va)] = pa | PG_V | protection_codes[prot];
58 1.1 maxv }
59 1.1 maxv
60 1.1 maxv static void
61 1.1 maxv mm_flush_va(vaddr_t va)
62 1.1 maxv {
63 1.1 maxv asm volatile("invlpg (%0)" ::"r" (va) : "memory");
64 1.1 maxv }
65 1.1 maxv
66 1.2 maxv static paddr_t
67 1.2 maxv mm_palloc(size_t npages)
68 1.2 maxv {
69 1.2 maxv paddr_t pa;
70 1.2 maxv size_t i;
71 1.2 maxv
72 1.2 maxv /* Allocate the physical pages */
73 1.2 maxv pa = pa_avail;
74 1.2 maxv pa_avail += npages * PAGE_SIZE;
75 1.2 maxv
76 1.2 maxv /* Zero them out */
77 1.2 maxv for (i = 0; i < npages; i++) {
78 1.2 maxv mm_enter_pa(pa + i * PAGE_SIZE, tmpva,
79 1.2 maxv MM_PROT_READ|MM_PROT_WRITE);
80 1.2 maxv mm_flush_va(tmpva);
81 1.2 maxv memset((void *)tmpva, 0, PAGE_SIZE);
82 1.2 maxv }
83 1.2 maxv
84 1.2 maxv return pa;
85 1.2 maxv }
86 1.2 maxv
87 1.3 maxv static bool
88 1.3 maxv mm_pte_is_valid(pt_entry_t pte)
89 1.3 maxv {
90 1.3 maxv return ((pte & PG_V) != 0);
91 1.3 maxv }
92 1.3 maxv
93 1.1 maxv paddr_t
94 1.1 maxv mm_vatopa(vaddr_t va)
95 1.1 maxv {
96 1.1 maxv return (PTE_BASE[pl1_i(va)] & PG_FRAME);
97 1.1 maxv }
98 1.1 maxv
99 1.8 maxv static void
100 1.1 maxv mm_mprotect(vaddr_t startva, size_t size, int prot)
101 1.1 maxv {
102 1.1 maxv size_t i, npages;
103 1.1 maxv vaddr_t va;
104 1.1 maxv paddr_t pa;
105 1.1 maxv
106 1.1 maxv ASSERT(size % PAGE_SIZE == 0);
107 1.1 maxv npages = size / PAGE_SIZE;
108 1.1 maxv
109 1.1 maxv for (i = 0; i < npages; i++) {
110 1.1 maxv va = startva + i * PAGE_SIZE;
111 1.1 maxv pa = (PTE_BASE[pl1_i(va)] & PG_FRAME);
112 1.1 maxv mm_enter_pa(pa, va, prot);
113 1.1 maxv mm_flush_va(va);
114 1.1 maxv }
115 1.1 maxv }
116 1.1 maxv
117 1.8 maxv void
118 1.8 maxv mm_bootspace_mprotect()
119 1.8 maxv {
120 1.8 maxv /*
121 1.8 maxv * Remap the kernel segments with proper permissions.
122 1.8 maxv */
123 1.8 maxv mm_mprotect(bootspace.text.va, bootspace.text.sz,
124 1.8 maxv MM_PROT_READ|MM_PROT_EXECUTE);
125 1.8 maxv mm_mprotect(bootspace.rodata.va, bootspace.rodata.sz,
126 1.8 maxv MM_PROT_READ);
127 1.8 maxv
128 1.8 maxv print_state(true, "Segments protection updated");
129 1.8 maxv }
130 1.8 maxv
131 1.5 maxv static size_t
132 1.5 maxv mm_nentries_range(vaddr_t startva, vaddr_t endva, size_t pgsz)
133 1.5 maxv {
134 1.5 maxv size_t npages;
135 1.5 maxv
136 1.5 maxv npages = roundup((endva / PAGE_SIZE), (pgsz / PAGE_SIZE)) -
137 1.5 maxv rounddown((startva / PAGE_SIZE), (pgsz / PAGE_SIZE));
138 1.5 maxv return (npages / (pgsz / PAGE_SIZE));
139 1.5 maxv }
140 1.5 maxv
141 1.1 maxv static void
142 1.2 maxv mm_map_tree(vaddr_t startva, vaddr_t endva)
143 1.1 maxv {
144 1.5 maxv size_t i, nL4e, nL3e, nL2e;
145 1.1 maxv size_t L4e_idx, L3e_idx, L2e_idx;
146 1.3 maxv paddr_t pa;
147 1.3 maxv
148 1.1 maxv /*
149 1.3 maxv * Build L4.
150 1.1 maxv */
151 1.3 maxv L4e_idx = pl4_i(startva);
152 1.5 maxv nL4e = mm_nentries_range(startva, endva, NBPD_L4);
153 1.3 maxv ASSERT(L4e_idx == 511);
154 1.2 maxv ASSERT(nL4e == 1);
155 1.3 maxv if (!mm_pte_is_valid(L4_BASE[L4e_idx])) {
156 1.3 maxv pa = mm_palloc(1);
157 1.3 maxv L4_BASE[L4e_idx] = pa | PG_V | PG_RW;
158 1.3 maxv }
159 1.1 maxv
160 1.1 maxv /*
161 1.3 maxv * Build L3.
162 1.1 maxv */
163 1.3 maxv L3e_idx = pl3_i(startva);
164 1.5 maxv nL3e = mm_nentries_range(startva, endva, NBPD_L3);
165 1.3 maxv for (i = 0; i < nL3e; i++) {
166 1.3 maxv if (mm_pte_is_valid(L3_BASE[L3e_idx+i])) {
167 1.3 maxv continue;
168 1.3 maxv }
169 1.3 maxv pa = mm_palloc(1);
170 1.3 maxv L3_BASE[L3e_idx+i] = pa | PG_V | PG_RW;
171 1.3 maxv }
172 1.1 maxv
173 1.1 maxv /*
174 1.3 maxv * Build L2.
175 1.1 maxv */
176 1.3 maxv L2e_idx = pl2_i(startva);
177 1.5 maxv nL2e = mm_nentries_range(startva, endva, NBPD_L2);
178 1.2 maxv for (i = 0; i < nL2e; i++) {
179 1.3 maxv if (mm_pte_is_valid(L2_BASE[L2e_idx+i])) {
180 1.3 maxv continue;
181 1.3 maxv }
182 1.3 maxv pa = mm_palloc(1);
183 1.3 maxv L2_BASE[L2e_idx+i] = pa | PG_V | PG_RW;
184 1.1 maxv }
185 1.1 maxv }
186 1.1 maxv
187 1.6 maxv static uint64_t
188 1.6 maxv mm_rand_num64()
189 1.6 maxv {
190 1.6 maxv /* XXX: yes, this is ridiculous, will be fixed soon */
191 1.6 maxv return rdtsc();
192 1.6 maxv }
193 1.6 maxv
194 1.6 maxv static void
195 1.6 maxv mm_map_head()
196 1.6 maxv {
197 1.6 maxv size_t i, npages, size;
198 1.6 maxv uint64_t rnd;
199 1.6 maxv vaddr_t randva;
200 1.6 maxv
201 1.6 maxv /*
202 1.6 maxv * To get the size of the head, we give a look at the read-only
203 1.6 maxv * mapping of the kernel we created in locore. We're identity mapped,
204 1.6 maxv * so kernpa = kernva.
205 1.6 maxv */
206 1.6 maxv size = elf_get_head_size((vaddr_t)kernpa_start);
207 1.6 maxv npages = size / PAGE_SIZE;
208 1.6 maxv
209 1.6 maxv rnd = mm_rand_num64();
210 1.6 maxv randva = rounddown(HEAD_WINDOW_BASE + rnd % (HEAD_WINDOW_SIZE - size),
211 1.6 maxv PAGE_SIZE);
212 1.6 maxv mm_map_tree(randva, randva + size);
213 1.6 maxv
214 1.6 maxv /* Enter the area and build the ELF info */
215 1.6 maxv for (i = 0; i < npages; i++) {
216 1.6 maxv mm_enter_pa(kernpa_start + i * PAGE_SIZE,
217 1.6 maxv randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
218 1.6 maxv }
219 1.6 maxv elf_build_head(randva);
220 1.6 maxv
221 1.6 maxv /* Register the values in bootspace */
222 1.6 maxv bootspace.head.va = randva;
223 1.6 maxv bootspace.head.pa = kernpa_start;
224 1.6 maxv bootspace.head.sz = size;
225 1.6 maxv }
226 1.6 maxv
227 1.1 maxv static vaddr_t
228 1.6 maxv mm_randva_kregion(size_t size)
229 1.1 maxv {
230 1.6 maxv static struct {
231 1.6 maxv vaddr_t sva;
232 1.6 maxv vaddr_t eva;
233 1.6 maxv } regions[4];
234 1.6 maxv static size_t idx = 0;
235 1.1 maxv vaddr_t randva;
236 1.1 maxv uint64_t rnd;
237 1.6 maxv size_t i;
238 1.6 maxv bool ok;
239 1.6 maxv
240 1.6 maxv ASSERT(idx < 4);
241 1.1 maxv
242 1.6 maxv while (1) {
243 1.6 maxv rnd = mm_rand_num64();
244 1.6 maxv randva = rounddown(KASLR_WINDOW_BASE +
245 1.6 maxv rnd % (KASLR_WINDOW_SIZE - size), PAGE_SIZE);
246 1.6 maxv
247 1.6 maxv /* Detect collisions */
248 1.6 maxv ok = true;
249 1.6 maxv for (i = 0; i < idx; i++) {
250 1.6 maxv if ((regions[i].sva <= randva) &&
251 1.6 maxv (randva < regions[i].eva)) {
252 1.6 maxv ok = false;
253 1.6 maxv break;
254 1.6 maxv }
255 1.6 maxv if ((regions[i].sva < randva + size) &&
256 1.6 maxv (randva + size <= regions[i].eva)) {
257 1.6 maxv ok = false;
258 1.6 maxv break;
259 1.6 maxv }
260 1.6 maxv }
261 1.6 maxv if (ok) {
262 1.6 maxv break;
263 1.6 maxv }
264 1.6 maxv }
265 1.1 maxv
266 1.6 maxv regions[idx].eva = randva;
267 1.6 maxv regions[idx].sva = randva + size;
268 1.6 maxv idx++;
269 1.1 maxv
270 1.2 maxv mm_map_tree(randva, randva + size);
271 1.1 maxv
272 1.1 maxv return randva;
273 1.1 maxv }
274 1.1 maxv
275 1.6 maxv static void
276 1.6 maxv mm_map_segments()
277 1.1 maxv {
278 1.1 maxv size_t i, npages, size;
279 1.6 maxv vaddr_t randva;
280 1.6 maxv paddr_t pa;
281 1.1 maxv
282 1.6 maxv /*
283 1.6 maxv * Kernel text segment.
284 1.6 maxv */
285 1.6 maxv elf_get_text(&pa, &size);
286 1.6 maxv randva = mm_randva_kregion(size);
287 1.1 maxv npages = size / PAGE_SIZE;
288 1.1 maxv
289 1.6 maxv /* Enter the area and build the ELF info */
290 1.1 maxv for (i = 0; i < npages; i++) {
291 1.6 maxv mm_enter_pa(pa + i * PAGE_SIZE,
292 1.6 maxv randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
293 1.6 maxv }
294 1.8 maxv elf_build_text(randva, pa);
295 1.6 maxv
296 1.6 maxv /* Register the values in bootspace */
297 1.6 maxv bootspace.text.va = randva;
298 1.6 maxv bootspace.text.pa = pa;
299 1.6 maxv bootspace.text.sz = size;
300 1.6 maxv
301 1.6 maxv /*
302 1.6 maxv * Kernel rodata segment.
303 1.6 maxv */
304 1.6 maxv elf_get_rodata(&pa, &size);
305 1.6 maxv randva = mm_randva_kregion(size);
306 1.6 maxv npages = size / PAGE_SIZE;
307 1.6 maxv
308 1.6 maxv /* Enter the area and build the ELF info */
309 1.6 maxv for (i = 0; i < npages; i++) {
310 1.6 maxv mm_enter_pa(pa + i * PAGE_SIZE,
311 1.6 maxv randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
312 1.6 maxv }
313 1.8 maxv elf_build_rodata(randva, pa);
314 1.6 maxv
315 1.6 maxv /* Register the values in bootspace */
316 1.6 maxv bootspace.rodata.va = randva;
317 1.6 maxv bootspace.rodata.pa = pa;
318 1.6 maxv bootspace.rodata.sz = size;
319 1.6 maxv
320 1.6 maxv /*
321 1.6 maxv * Kernel data segment.
322 1.6 maxv */
323 1.6 maxv elf_get_data(&pa, &size);
324 1.6 maxv randva = mm_randva_kregion(size);
325 1.6 maxv npages = size / PAGE_SIZE;
326 1.6 maxv
327 1.6 maxv /* Enter the area and build the ELF info */
328 1.6 maxv for (i = 0; i < npages; i++) {
329 1.6 maxv mm_enter_pa(pa + i * PAGE_SIZE,
330 1.6 maxv randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
331 1.6 maxv }
332 1.8 maxv elf_build_data(randva, pa);
333 1.6 maxv
334 1.6 maxv /* Register the values in bootspace */
335 1.6 maxv bootspace.data.va = randva;
336 1.6 maxv bootspace.data.pa = pa;
337 1.6 maxv bootspace.data.sz = size;
338 1.6 maxv }
339 1.6 maxv
340 1.6 maxv static void
341 1.6 maxv mm_map_boot()
342 1.6 maxv {
343 1.6 maxv size_t i, npages, size;
344 1.6 maxv vaddr_t randva;
345 1.6 maxv paddr_t bootpa;
346 1.6 maxv
347 1.6 maxv /*
348 1.6 maxv * The "boot" region is special: its page tree has a fixed size, but
349 1.6 maxv * the number of pages entered is lower.
350 1.6 maxv */
351 1.6 maxv
352 1.6 maxv /* Create the page tree */
353 1.6 maxv size = (NKL2_KIMG_ENTRIES + 1) * NBPD_L2;
354 1.6 maxv randva = mm_randva_kregion(size);
355 1.6 maxv
356 1.6 maxv /* Enter the area and build the ELF info */
357 1.6 maxv bootpa = bootspace.data.pa + bootspace.data.sz;
358 1.6 maxv size = (pa_avail - bootpa);
359 1.6 maxv npages = size / PAGE_SIZE;
360 1.6 maxv for (i = 0; i < npages; i++) {
361 1.6 maxv mm_enter_pa(bootpa + i * PAGE_SIZE,
362 1.6 maxv randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
363 1.1 maxv }
364 1.6 maxv elf_build_boot(randva, bootpa);
365 1.1 maxv
366 1.1 maxv /* Enter the ISA I/O MEM */
367 1.6 maxv iom_base = randva + npages * PAGE_SIZE;
368 1.1 maxv npages = IOM_SIZE / PAGE_SIZE;
369 1.1 maxv for (i = 0; i < npages; i++) {
370 1.1 maxv mm_enter_pa(IOM_BEGIN + i * PAGE_SIZE,
371 1.1 maxv iom_base + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
372 1.1 maxv }
373 1.1 maxv
374 1.6 maxv /* Register the values in bootspace */
375 1.6 maxv bootspace.boot.va = randva;
376 1.6 maxv bootspace.boot.pa = bootpa;
377 1.6 maxv bootspace.boot.sz = (size_t)(iom_base + IOM_SIZE) -
378 1.6 maxv (size_t)bootspace.boot.va;
379 1.6 maxv
380 1.6 maxv /* Initialize the values that are located in the "boot" region */
381 1.6 maxv extern uint64_t PDPpaddr;
382 1.6 maxv bootspace.spareva = bootspace.boot.va + NKL2_KIMG_ENTRIES * NBPD_L2;
383 1.6 maxv bootspace.pdir = bootspace.boot.va + (PDPpaddr - bootspace.boot.pa);
384 1.6 maxv bootspace.emodule = bootspace.boot.va + NKL2_KIMG_ENTRIES * NBPD_L2;
385 1.1 maxv }
386 1.6 maxv
387 1.6 maxv /*
388 1.6 maxv * There are five independent regions: head, text, rodata, data, boot. They are
389 1.6 maxv * all mapped at random VAs.
390 1.6 maxv *
391 1.6 maxv * Head contains the ELF Header and ELF Section Headers, and we use them to
392 1.6 maxv * map the rest of the regions. Head must be placed in memory *before* the
393 1.6 maxv * other regions.
394 1.6 maxv *
395 1.6 maxv * At the end of this function, the bootspace structure is fully constructed.
396 1.6 maxv */
397 1.6 maxv void
398 1.6 maxv mm_map_kernel()
399 1.6 maxv {
400 1.6 maxv memset(&bootspace, 0, sizeof(bootspace));
401 1.6 maxv mm_map_head();
402 1.7 maxv print_state(true, "Head region mapped");
403 1.6 maxv mm_map_segments();
404 1.7 maxv print_state(true, "Segments mapped");
405 1.6 maxv mm_map_boot();
406 1.7 maxv print_state(true, "Boot region mapped");
407 1.6 maxv }
408 1.6 maxv
409