Annotation of src/sys/arch/amd64/stand/prekern/mm.c, Revision 1.8
1.8 ! maxv 1: /* $NetBSD: mm.c,v 1.7 2017/10/29 11:38:43 maxv Exp $ */
1.1 maxv 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:
1.6 maxv 40: struct bootspace bootspace;
41:
1.1 maxv 42: extern paddr_t kernpa_start, kernpa_end;
43: vaddr_t iom_base;
44:
45: paddr_t pa_avail = 0;
1.2 maxv 46: static const vaddr_t tmpva = (PREKERNBASE + NKL2_KIMG_ENTRIES * NBPD_L2);
1.1 maxv 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:
1.2 maxv 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:
1.3 maxv 87: static bool
88: mm_pte_is_valid(pt_entry_t pte)
89: {
90: return ((pte & PG_V) != 0);
91: }
92:
1.1 maxv 93: paddr_t
94: mm_vatopa(vaddr_t va)
95: {
96: return (PTE_BASE[pl1_i(va)] & PG_FRAME);
97: }
98:
1.8 ! maxv 99: static void
1.1 maxv 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:
1.8 ! maxv 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:
1.5 maxv 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:
1.1 maxv 141: static void
1.2 maxv 142: mm_map_tree(vaddr_t startva, vaddr_t endva)
1.1 maxv 143: {
1.5 maxv 144: size_t i, nL4e, nL3e, nL2e;
1.1 maxv 145: size_t L4e_idx, L3e_idx, L2e_idx;
1.3 maxv 146: paddr_t pa;
147:
1.1 maxv 148: /*
1.3 maxv 149: * Build L4.
1.1 maxv 150: */
1.3 maxv 151: L4e_idx = pl4_i(startva);
1.5 maxv 152: nL4e = mm_nentries_range(startva, endva, NBPD_L4);
1.3 maxv 153: ASSERT(L4e_idx == 511);
1.2 maxv 154: ASSERT(nL4e == 1);
1.3 maxv 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: }
1.1 maxv 159:
160: /*
1.3 maxv 161: * Build L3.
1.1 maxv 162: */
1.3 maxv 163: L3e_idx = pl3_i(startva);
1.5 maxv 164: nL3e = mm_nentries_range(startva, endva, NBPD_L3);
1.3 maxv 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: }
1.1 maxv 172:
173: /*
1.3 maxv 174: * Build L2.
1.1 maxv 175: */
1.3 maxv 176: L2e_idx = pl2_i(startva);
1.5 maxv 177: nL2e = mm_nentries_range(startva, endva, NBPD_L2);
1.2 maxv 178: for (i = 0; i < nL2e; i++) {
1.3 maxv 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;
1.1 maxv 184: }
185: }
186:
1.6 maxv 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:
1.1 maxv 227: static vaddr_t
1.6 maxv 228: mm_randva_kregion(size_t size)
1.1 maxv 229: {
1.6 maxv 230: static struct {
231: vaddr_t sva;
232: vaddr_t eva;
233: } regions[4];
234: static size_t idx = 0;
1.1 maxv 235: vaddr_t randva;
236: uint64_t rnd;
1.6 maxv 237: size_t i;
238: bool ok;
239:
240: ASSERT(idx < 4);
1.1 maxv 241:
1.6 maxv 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: }
1.1 maxv 265:
1.6 maxv 266: regions[idx].eva = randva;
267: regions[idx].sva = randva + size;
268: idx++;
1.1 maxv 269:
1.2 maxv 270: mm_map_tree(randva, randva + size);
1.1 maxv 271:
272: return randva;
273: }
274:
1.6 maxv 275: static void
276: mm_map_segments()
1.1 maxv 277: {
278: size_t i, npages, size;
1.6 maxv 279: vaddr_t randva;
280: paddr_t pa;
1.1 maxv 281:
1.6 maxv 282: /*
283: * Kernel text segment.
284: */
285: elf_get_text(&pa, &size);
286: randva = mm_randva_kregion(size);
1.1 maxv 287: npages = size / PAGE_SIZE;
288:
1.6 maxv 289: /* Enter the area and build the ELF info */
1.1 maxv 290: for (i = 0; i < npages; i++) {
1.6 maxv 291: mm_enter_pa(pa + i * PAGE_SIZE,
292: randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
293: }
1.8 ! maxv 294: elf_build_text(randva, pa);
1.6 maxv 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: }
1.8 ! maxv 313: elf_build_rodata(randva, pa);
1.6 maxv 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: }
1.8 ! maxv 332: elf_build_data(randva, pa);
1.6 maxv 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);
1.1 maxv 363: }
1.6 maxv 364: elf_build_boot(randva, bootpa);
1.1 maxv 365:
366: /* Enter the ISA I/O MEM */
1.6 maxv 367: iom_base = randva + npages * PAGE_SIZE;
1.1 maxv 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:
1.6 maxv 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;
1.1 maxv 385: }
1.6 maxv 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();
1.7 maxv 402: print_state(true, "Head region mapped");
1.6 maxv 403: mm_map_segments();
1.7 maxv 404: print_state(true, "Segments mapped");
1.6 maxv 405: mm_map_boot();
1.7 maxv 406: print_state(true, "Boot region mapped");
1.6 maxv 407: }
408:
CVSweb <webmaster@jp.NetBSD.org>