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