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