Home | History | Annotate | Line # | Download | only in prekern
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