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