Home | History | Annotate | Line # | Download | only in prekern
mm.c revision 1.7
      1 /*	$NetBSD: mm.c,v 1.7 2017/10/29 11:38:43 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 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 static size_t
    118 mm_nentries_range(vaddr_t startva, vaddr_t endva, size_t pgsz)
    119 {
    120 	size_t npages;
    121 
    122 	npages = roundup((endva / PAGE_SIZE), (pgsz / PAGE_SIZE)) -
    123 	    rounddown((startva / PAGE_SIZE), (pgsz / PAGE_SIZE));
    124 	return (npages / (pgsz / PAGE_SIZE));
    125 }
    126 
    127 static void
    128 mm_map_tree(vaddr_t startva, vaddr_t endva)
    129 {
    130 	size_t i, nL4e, nL3e, nL2e;
    131 	size_t L4e_idx, L3e_idx, L2e_idx;
    132 	paddr_t pa;
    133 
    134 	/*
    135 	 * Build L4.
    136 	 */
    137 	L4e_idx = pl4_i(startva);
    138 	nL4e = mm_nentries_range(startva, endva, NBPD_L4);
    139 	ASSERT(L4e_idx == 511);
    140 	ASSERT(nL4e == 1);
    141 	if (!mm_pte_is_valid(L4_BASE[L4e_idx])) {
    142 		pa = mm_palloc(1);
    143 		L4_BASE[L4e_idx] = pa | PG_V | PG_RW;
    144 	}
    145 
    146 	/*
    147 	 * Build L3.
    148 	 */
    149 	L3e_idx = pl3_i(startva);
    150 	nL3e = mm_nentries_range(startva, endva, NBPD_L3);
    151 	for (i = 0; i < nL3e; i++) {
    152 		if (mm_pte_is_valid(L3_BASE[L3e_idx+i])) {
    153 			continue;
    154 		}
    155 		pa = mm_palloc(1);
    156 		L3_BASE[L3e_idx+i] = pa | PG_V | PG_RW;
    157 	}
    158 
    159 	/*
    160 	 * Build L2.
    161 	 */
    162 	L2e_idx = pl2_i(startva);
    163 	nL2e = mm_nentries_range(startva, endva, NBPD_L2);
    164 	for (i = 0; i < nL2e; i++) {
    165 		if (mm_pte_is_valid(L2_BASE[L2e_idx+i])) {
    166 			continue;
    167 		}
    168 		pa = mm_palloc(1);
    169 		L2_BASE[L2e_idx+i] = pa | PG_V | PG_RW;
    170 	}
    171 }
    172 
    173 static uint64_t
    174 mm_rand_num64()
    175 {
    176 	/* XXX: yes, this is ridiculous, will be fixed soon */
    177 	return rdtsc();
    178 }
    179 
    180 static void
    181 mm_map_head()
    182 {
    183 	size_t i, npages, size;
    184 	uint64_t rnd;
    185 	vaddr_t randva;
    186 
    187 	/*
    188 	 * To get the size of the head, we give a look at the read-only
    189 	 * mapping of the kernel we created in locore. We're identity mapped,
    190 	 * so kernpa = kernva.
    191 	 */
    192 	size = elf_get_head_size((vaddr_t)kernpa_start);
    193 	npages = size / PAGE_SIZE;
    194 
    195 	rnd = mm_rand_num64();
    196 	randva = rounddown(HEAD_WINDOW_BASE + rnd % (HEAD_WINDOW_SIZE - size),
    197 	    PAGE_SIZE);
    198 	mm_map_tree(randva, randva + size);
    199 
    200 	/* Enter the area and build the ELF info */
    201 	for (i = 0; i < npages; i++) {
    202 		mm_enter_pa(kernpa_start + i * PAGE_SIZE,
    203 		    randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
    204 	}
    205 	elf_build_head(randva);
    206 
    207 	/* Register the values in bootspace */
    208 	bootspace.head.va = randva;
    209 	bootspace.head.pa = kernpa_start;
    210 	bootspace.head.sz = size;
    211 }
    212 
    213 static vaddr_t
    214 mm_randva_kregion(size_t size)
    215 {
    216 	static struct {
    217 		vaddr_t sva;
    218 		vaddr_t eva;
    219 	} regions[4];
    220 	static size_t idx = 0;
    221 	vaddr_t randva;
    222 	uint64_t rnd;
    223 	size_t i;
    224 	bool ok;
    225 
    226 	ASSERT(idx < 4);
    227 
    228 	while (1) {
    229 		rnd = mm_rand_num64();
    230 		randva = rounddown(KASLR_WINDOW_BASE +
    231 		    rnd % (KASLR_WINDOW_SIZE - size), PAGE_SIZE);
    232 
    233 		/* Detect collisions */
    234 		ok = true;
    235 		for (i = 0; i < idx; i++) {
    236 			if ((regions[i].sva <= randva) &&
    237 			    (randva < regions[i].eva)) {
    238 				ok = false;
    239 				break;
    240 			}
    241 			if ((regions[i].sva < randva + size) &&
    242 			    (randva + size <= regions[i].eva)) {
    243 				ok = false;
    244 				break;
    245 			}
    246 		}
    247 		if (ok) {
    248 			break;
    249 		}
    250 	}
    251 
    252 	regions[idx].eva = randva;
    253 	regions[idx].sva = randva + size;
    254 	idx++;
    255 
    256 	mm_map_tree(randva, randva + size);
    257 
    258 	return randva;
    259 }
    260 
    261 static void
    262 mm_map_segments()
    263 {
    264 	size_t i, npages, size;
    265 	vaddr_t randva;
    266 	paddr_t pa;
    267 
    268 	/*
    269 	 * Kernel text segment.
    270 	 */
    271 	elf_get_text(&pa, &size);
    272 	randva = mm_randva_kregion(size);
    273 	npages = size / PAGE_SIZE;
    274 
    275 	/* Enter the area and build the ELF info */
    276 	for (i = 0; i < npages; i++) {
    277 		mm_enter_pa(pa + i * PAGE_SIZE,
    278 		    randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
    279 	}
    280 	elf_build_text(randva, pa, size);
    281 
    282 	/* Register the values in bootspace */
    283 	bootspace.text.va = randva;
    284 	bootspace.text.pa = pa;
    285 	bootspace.text.sz = size;
    286 
    287 	/*
    288 	 * Kernel rodata segment.
    289 	 */
    290 	elf_get_rodata(&pa, &size);
    291 	randva = mm_randva_kregion(size);
    292 	npages = size / PAGE_SIZE;
    293 
    294 	/* Enter the area and build the ELF info */
    295 	for (i = 0; i < npages; i++) {
    296 		mm_enter_pa(pa + i * PAGE_SIZE,
    297 		    randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
    298 	}
    299 	elf_build_rodata(randva, pa, size);
    300 
    301 	/* Register the values in bootspace */
    302 	bootspace.rodata.va = randva;
    303 	bootspace.rodata.pa = pa;
    304 	bootspace.rodata.sz = size;
    305 
    306 	/*
    307 	 * Kernel data segment.
    308 	 */
    309 	elf_get_data(&pa, &size);
    310 	randva = mm_randva_kregion(size);
    311 	npages = size / PAGE_SIZE;
    312 
    313 	/* Enter the area and build the ELF info */
    314 	for (i = 0; i < npages; i++) {
    315 		mm_enter_pa(pa + i * PAGE_SIZE,
    316 		    randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
    317 	}
    318 	elf_build_data(randva, pa, size);
    319 
    320 	/* Register the values in bootspace */
    321 	bootspace.data.va = randva;
    322 	bootspace.data.pa = pa;
    323 	bootspace.data.sz = size;
    324 }
    325 
    326 static void
    327 mm_map_boot()
    328 {
    329 	size_t i, npages, size;
    330 	vaddr_t randva;
    331 	paddr_t bootpa;
    332 
    333 	/*
    334 	 * The "boot" region is special: its page tree has a fixed size, but
    335 	 * the number of pages entered is lower.
    336 	 */
    337 
    338 	/* Create the page tree */
    339 	size = (NKL2_KIMG_ENTRIES + 1) * NBPD_L2;
    340 	randva = mm_randva_kregion(size);
    341 
    342 	/* Enter the area and build the ELF info */
    343 	bootpa = bootspace.data.pa + bootspace.data.sz;
    344 	size = (pa_avail - bootpa);
    345 	npages = size / PAGE_SIZE;
    346 	for (i = 0; i < npages; i++) {
    347 		mm_enter_pa(bootpa + i * PAGE_SIZE,
    348 		    randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
    349 	}
    350 	elf_build_boot(randva, bootpa);
    351 
    352 	/* Enter the ISA I/O MEM */
    353 	iom_base = randva + npages * PAGE_SIZE;
    354 	npages = IOM_SIZE / PAGE_SIZE;
    355 	for (i = 0; i < npages; i++) {
    356 		mm_enter_pa(IOM_BEGIN + i * PAGE_SIZE,
    357 		    iom_base + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
    358 	}
    359 
    360 	/* Register the values in bootspace */
    361 	bootspace.boot.va = randva;
    362 	bootspace.boot.pa = bootpa;
    363 	bootspace.boot.sz = (size_t)(iom_base + IOM_SIZE) -
    364 	    (size_t)bootspace.boot.va;
    365 
    366 	/* Initialize the values that are located in the "boot" region */
    367 	extern uint64_t PDPpaddr;
    368 	bootspace.spareva = bootspace.boot.va + NKL2_KIMG_ENTRIES * NBPD_L2;
    369 	bootspace.pdir = bootspace.boot.va + (PDPpaddr - bootspace.boot.pa);
    370 	bootspace.emodule = bootspace.boot.va + NKL2_KIMG_ENTRIES * NBPD_L2;
    371 }
    372 
    373 /*
    374  * There are five independent regions: head, text, rodata, data, boot. They are
    375  * all mapped at random VAs.
    376  *
    377  * Head contains the ELF Header and ELF Section Headers, and we use them to
    378  * map the rest of the regions. Head must be placed in memory *before* the
    379  * other regions.
    380  *
    381  * At the end of this function, the bootspace structure is fully constructed.
    382  */
    383 void
    384 mm_map_kernel()
    385 {
    386 	memset(&bootspace, 0, sizeof(bootspace));
    387 	mm_map_head();
    388 	print_state(true, "Head region mapped");
    389 	mm_map_segments();
    390 	print_state(true, "Segments mapped");
    391 	mm_map_boot();
    392 	print_state(true, "Boot region mapped");
    393 }
    394 
    395