1*dadf0eefSkhorben /* $NetBSD: mm.c,v 1.28 2021/05/04 21:09:16 khorben Exp $ */
2c9759921Smaxv
3c9759921Smaxv /*
4ad3185a9Smaxv * Copyright (c) 2017-2020 The NetBSD Foundation, Inc. All rights reserved.
5c9759921Smaxv *
6c9759921Smaxv * This code is derived from software contributed to The NetBSD Foundation
7c9759921Smaxv * by Maxime Villard.
8c9759921Smaxv *
9c9759921Smaxv * Redistribution and use in source and binary forms, with or without
10c9759921Smaxv * modification, are permitted provided that the following conditions
11c9759921Smaxv * are met:
12c9759921Smaxv * 1. Redistributions of source code must retain the above copyright
13c9759921Smaxv * notice, this list of conditions and the following disclaimer.
14c9759921Smaxv * 2. Redistributions in binary form must reproduce the above copyright
15c9759921Smaxv * notice, this list of conditions and the following disclaimer in the
16c9759921Smaxv * documentation and/or other materials provided with the distribution.
17c9759921Smaxv *
18c9759921Smaxv * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19c9759921Smaxv * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20c9759921Smaxv * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21c9759921Smaxv * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22c9759921Smaxv * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23c9759921Smaxv * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24c9759921Smaxv * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25c9759921Smaxv * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26c9759921Smaxv * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27c9759921Smaxv * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28c9759921Smaxv * POSSIBILITY OF SUCH DAMAGE.
29c9759921Smaxv */
30c9759921Smaxv
31c9759921Smaxv #include "prekern.h"
32c9759921Smaxv
3326e9e80dSmaxv #define ELFROUND 64
3426e9e80dSmaxv
35a98ea778Smaxv static const uint8_t pads[4] = {
365b84bc55Smaxv [BTSEG_NONE] = 0x00,
375b84bc55Smaxv [BTSEG_TEXT] = 0xCC,
385b84bc55Smaxv [BTSEG_RODATA] = 0x00,
395b84bc55Smaxv [BTSEG_DATA] = 0x00
405b84bc55Smaxv };
415b84bc55Smaxv
42d19e39baSmaxv #define MM_PROT_READ 0x00
43d19e39baSmaxv #define MM_PROT_WRITE 0x01
44d19e39baSmaxv #define MM_PROT_EXECUTE 0x02
45d19e39baSmaxv
46c9759921Smaxv static const pt_entry_t protection_codes[3] = {
47e4552b50Smaxv [MM_PROT_READ] = PTE_NX,
48e4552b50Smaxv [MM_PROT_WRITE] = PTE_W | PTE_NX,
4953821357Smaxv [MM_PROT_EXECUTE] = 0,
50c9759921Smaxv /* RWX does not exist */
51c9759921Smaxv };
52c9759921Smaxv
53569f0c08Smaxv struct bootspace bootspace;
54569f0c08Smaxv
55c9759921Smaxv extern paddr_t kernpa_start, kernpa_end;
56c9759921Smaxv vaddr_t iom_base;
57c9759921Smaxv
58c9759921Smaxv paddr_t pa_avail = 0;
59269f14c2Smaxv static const vaddr_t tmpva = (PREKERNBASE + NKL2_KIMG_ENTRIES * NBPD_L2);
60c9759921Smaxv
61c9759921Smaxv void
mm_init(paddr_t first_pa)62c9759921Smaxv mm_init(paddr_t first_pa)
63c9759921Smaxv {
64c9759921Smaxv pa_avail = first_pa;
65c9759921Smaxv }
66c9759921Smaxv
67c9759921Smaxv static void
mm_enter_pa(paddr_t pa,vaddr_t va,pte_prot_t prot)68c9759921Smaxv mm_enter_pa(paddr_t pa, vaddr_t va, pte_prot_t prot)
69c9759921Smaxv {
70e4552b50Smaxv if (PTE_BASE[pl1_i(va)] & PTE_P) {
714540a08eSmaxv fatal("mm_enter_pa: mapping already present");
724540a08eSmaxv }
73e4552b50Smaxv PTE_BASE[pl1_i(va)] = pa | PTE_P | protection_codes[prot];
744540a08eSmaxv }
754540a08eSmaxv
764540a08eSmaxv static void
mm_reenter_pa(paddr_t pa,vaddr_t va,pte_prot_t prot)774540a08eSmaxv mm_reenter_pa(paddr_t pa, vaddr_t va, pte_prot_t prot)
784540a08eSmaxv {
79e4552b50Smaxv PTE_BASE[pl1_i(va)] = pa | PTE_P | protection_codes[prot];
80c9759921Smaxv }
81c9759921Smaxv
82c9759921Smaxv static void
mm_flush_va(vaddr_t va)83c9759921Smaxv mm_flush_va(vaddr_t va)
84c9759921Smaxv {
85c9759921Smaxv asm volatile("invlpg (%0)" ::"r" (va) : "memory");
86c9759921Smaxv }
87c9759921Smaxv
88269f14c2Smaxv static paddr_t
mm_palloc(size_t npages)89269f14c2Smaxv mm_palloc(size_t npages)
90269f14c2Smaxv {
91269f14c2Smaxv paddr_t pa;
92269f14c2Smaxv size_t i;
93269f14c2Smaxv
94269f14c2Smaxv /* Allocate the physical pages */
95269f14c2Smaxv pa = pa_avail;
96269f14c2Smaxv pa_avail += npages * PAGE_SIZE;
97269f14c2Smaxv
98269f14c2Smaxv /* Zero them out */
99269f14c2Smaxv for (i = 0; i < npages; i++) {
1004540a08eSmaxv mm_reenter_pa(pa + i * PAGE_SIZE, tmpva,
101269f14c2Smaxv MM_PROT_READ|MM_PROT_WRITE);
102269f14c2Smaxv mm_flush_va(tmpva);
103269f14c2Smaxv memset((void *)tmpva, 0, PAGE_SIZE);
104269f14c2Smaxv }
105269f14c2Smaxv
106269f14c2Smaxv return pa;
107269f14c2Smaxv }
108269f14c2Smaxv
109bf5a5b0bSmaxv static bool
mm_pte_is_valid(pt_entry_t pte)110bf5a5b0bSmaxv mm_pte_is_valid(pt_entry_t pte)
111bf5a5b0bSmaxv {
112e4552b50Smaxv return ((pte & PTE_P) != 0);
113bf5a5b0bSmaxv }
114bf5a5b0bSmaxv
115aaeb67dfSmaxv static void
mm_mprotect(vaddr_t startva,size_t size,pte_prot_t prot)1165b84bc55Smaxv mm_mprotect(vaddr_t startva, size_t size, pte_prot_t prot)
117c9759921Smaxv {
118c9759921Smaxv size_t i, npages;
119c9759921Smaxv vaddr_t va;
120c9759921Smaxv paddr_t pa;
121c9759921Smaxv
122c9759921Smaxv ASSERT(size % PAGE_SIZE == 0);
123c9759921Smaxv npages = size / PAGE_SIZE;
124c9759921Smaxv
125c9759921Smaxv for (i = 0; i < npages; i++) {
126c9759921Smaxv va = startva + i * PAGE_SIZE;
127e4552b50Smaxv pa = (PTE_BASE[pl1_i(va)] & PTE_FRAME);
1284540a08eSmaxv mm_reenter_pa(pa, va, prot);
129c9759921Smaxv mm_flush_va(va);
130c9759921Smaxv }
131c9759921Smaxv }
132c9759921Smaxv
133aaeb67dfSmaxv void
mm_bootspace_mprotect(void)13492fe5cfbSmaxv mm_bootspace_mprotect(void)
135aaeb67dfSmaxv {
1365b84bc55Smaxv pte_prot_t prot;
137ce4ddbdbSmaxv size_t i;
138ce4ddbdbSmaxv
139ce4ddbdbSmaxv /* Remap the kernel segments with proper permissions. */
140ce4ddbdbSmaxv for (i = 0; i < BTSPACE_NSEGS; i++) {
141ce4ddbdbSmaxv if (bootspace.segs[i].type == BTSEG_TEXT) {
142ce4ddbdbSmaxv prot = MM_PROT_READ|MM_PROT_EXECUTE;
143ce4ddbdbSmaxv } else if (bootspace.segs[i].type == BTSEG_RODATA) {
144ce4ddbdbSmaxv prot = MM_PROT_READ;
145ce4ddbdbSmaxv } else {
146ce4ddbdbSmaxv continue;
147ce4ddbdbSmaxv }
148ce4ddbdbSmaxv mm_mprotect(bootspace.segs[i].va, bootspace.segs[i].sz, prot);
149ce4ddbdbSmaxv }
150aaeb67dfSmaxv
151*dadf0eefSkhorben print_state(STATE_NORMAL, "Segments protection updated");
152aaeb67dfSmaxv }
153aaeb67dfSmaxv
154be0afa4eSmaxv static size_t
mm_nentries_range(vaddr_t startva,vaddr_t endva,size_t pgsz)155be0afa4eSmaxv mm_nentries_range(vaddr_t startva, vaddr_t endva, size_t pgsz)
156be0afa4eSmaxv {
157be0afa4eSmaxv size_t npages;
158be0afa4eSmaxv
159be0afa4eSmaxv npages = roundup((endva / PAGE_SIZE), (pgsz / PAGE_SIZE)) -
160be0afa4eSmaxv rounddown((startva / PAGE_SIZE), (pgsz / PAGE_SIZE));
161be0afa4eSmaxv return (npages / (pgsz / PAGE_SIZE));
162be0afa4eSmaxv }
163be0afa4eSmaxv
164c9759921Smaxv static void
mm_map_tree(vaddr_t startva,vaddr_t endva)165269f14c2Smaxv mm_map_tree(vaddr_t startva, vaddr_t endva)
166c9759921Smaxv {
167be0afa4eSmaxv size_t i, nL4e, nL3e, nL2e;
168c9759921Smaxv size_t L4e_idx, L3e_idx, L2e_idx;
169bf5a5b0bSmaxv paddr_t pa;
170c9759921Smaxv
171a98ea778Smaxv /* Build L4. */
172c9759921Smaxv L4e_idx = pl4_i(startva);
173be0afa4eSmaxv nL4e = mm_nentries_range(startva, endva, NBPD_L4);
174269f14c2Smaxv ASSERT(L4e_idx == 511);
175bf5a5b0bSmaxv ASSERT(nL4e == 1);
176bf5a5b0bSmaxv if (!mm_pte_is_valid(L4_BASE[L4e_idx])) {
177bf5a5b0bSmaxv pa = mm_palloc(1);
178e4552b50Smaxv L4_BASE[L4e_idx] = pa | PTE_P | PTE_W;
179269f14c2Smaxv }
180bf5a5b0bSmaxv
181a98ea778Smaxv /* Build L3. */
182bf5a5b0bSmaxv L3e_idx = pl3_i(startva);
183be0afa4eSmaxv nL3e = mm_nentries_range(startva, endva, NBPD_L3);
184bf5a5b0bSmaxv for (i = 0; i < nL3e; i++) {
185bf5a5b0bSmaxv if (mm_pte_is_valid(L3_BASE[L3e_idx+i])) {
186bf5a5b0bSmaxv continue;
187bf5a5b0bSmaxv }
188bf5a5b0bSmaxv pa = mm_palloc(1);
189e4552b50Smaxv L3_BASE[L3e_idx+i] = pa | PTE_P | PTE_W;
190bf5a5b0bSmaxv }
191bf5a5b0bSmaxv
192a98ea778Smaxv /* Build L2. */
193bf5a5b0bSmaxv L2e_idx = pl2_i(startva);
194be0afa4eSmaxv nL2e = mm_nentries_range(startva, endva, NBPD_L2);
195269f14c2Smaxv for (i = 0; i < nL2e; i++) {
196bf5a5b0bSmaxv if (mm_pte_is_valid(L2_BASE[L2e_idx+i])) {
197bf5a5b0bSmaxv continue;
198bf5a5b0bSmaxv }
199bf5a5b0bSmaxv pa = mm_palloc(1);
200e4552b50Smaxv L2_BASE[L2e_idx+i] = pa | PTE_P | PTE_W;
201269f14c2Smaxv }
202c9759921Smaxv }
203c9759921Smaxv
204569f0c08Smaxv static vaddr_t
mm_randva_kregion(size_t size,size_t pagesz)2055b84bc55Smaxv mm_randva_kregion(size_t size, size_t pagesz)
206569f0c08Smaxv {
2075d72504cSmaxv vaddr_t sva, eva;
208c9759921Smaxv vaddr_t randva;
209c9759921Smaxv uint64_t rnd;
210569f0c08Smaxv size_t i;
211569f0c08Smaxv bool ok;
212c9759921Smaxv
213569f0c08Smaxv while (1) {
21449d35b9cSmaxv prng_get_rand(&rnd, sizeof(rnd));
215569f0c08Smaxv randva = rounddown(KASLR_WINDOW_BASE +
2165b84bc55Smaxv rnd % (KASLR_WINDOW_SIZE - size), pagesz);
217569f0c08Smaxv
218569f0c08Smaxv /* Detect collisions */
219569f0c08Smaxv ok = true;
2205d72504cSmaxv for (i = 0; i < BTSPACE_NSEGS; i++) {
2215d72504cSmaxv if (bootspace.segs[i].type == BTSEG_NONE) {
2225d72504cSmaxv continue;
2235d72504cSmaxv }
2245d72504cSmaxv sva = bootspace.segs[i].va;
2255d72504cSmaxv eva = sva + bootspace.segs[i].sz;
2265d72504cSmaxv
2275d72504cSmaxv if ((sva <= randva) && (randva < eva)) {
228569f0c08Smaxv ok = false;
229569f0c08Smaxv break;
230569f0c08Smaxv }
2315d72504cSmaxv if ((sva < randva + size) && (randva + size <= eva)) {
232569f0c08Smaxv ok = false;
233569f0c08Smaxv break;
234569f0c08Smaxv }
2354540a08eSmaxv if (randva < sva && eva < (randva + size)) {
2364540a08eSmaxv ok = false;
2374540a08eSmaxv break;
2384540a08eSmaxv }
239569f0c08Smaxv }
240569f0c08Smaxv if (ok) {
241569f0c08Smaxv break;
242569f0c08Smaxv }
243569f0c08Smaxv }
244569f0c08Smaxv
245269f14c2Smaxv mm_map_tree(randva, randva + size);
246c9759921Smaxv
247c9759921Smaxv return randva;
248c9759921Smaxv }
249c9759921Smaxv
250ce4ddbdbSmaxv static paddr_t
bootspace_get_kern_segs_end_pa(void)25193e18f60Smaxv bootspace_get_kern_segs_end_pa(void)
252ce4ddbdbSmaxv {
253ce4ddbdbSmaxv paddr_t pa, max = 0;
254ce4ddbdbSmaxv size_t i;
255ce4ddbdbSmaxv
256ce4ddbdbSmaxv for (i = 0; i < BTSPACE_NSEGS; i++) {
257ce4ddbdbSmaxv if (bootspace.segs[i].type == BTSEG_NONE) {
258ce4ddbdbSmaxv continue;
259ce4ddbdbSmaxv }
260ce4ddbdbSmaxv pa = bootspace.segs[i].pa + bootspace.segs[i].sz;
261ce4ddbdbSmaxv if (pa > max)
262ce4ddbdbSmaxv max = pa;
263ce4ddbdbSmaxv }
264ce4ddbdbSmaxv
265ce4ddbdbSmaxv return max;
266ce4ddbdbSmaxv }
267ce4ddbdbSmaxv
268ce4ddbdbSmaxv static void
bootspace_addseg(int type,vaddr_t va,paddr_t pa,size_t sz)269ce4ddbdbSmaxv bootspace_addseg(int type, vaddr_t va, paddr_t pa, size_t sz)
270ce4ddbdbSmaxv {
271ce4ddbdbSmaxv size_t i;
272ce4ddbdbSmaxv
273ce4ddbdbSmaxv for (i = 0; i < BTSPACE_NSEGS; i++) {
274ce4ddbdbSmaxv if (bootspace.segs[i].type == BTSEG_NONE) {
275ce4ddbdbSmaxv bootspace.segs[i].type = type;
276ce4ddbdbSmaxv bootspace.segs[i].va = va;
277ce4ddbdbSmaxv bootspace.segs[i].pa = pa;
278ce4ddbdbSmaxv bootspace.segs[i].sz = sz;
279ce4ddbdbSmaxv return;
280ce4ddbdbSmaxv }
281ce4ddbdbSmaxv }
282ce4ddbdbSmaxv
283ce4ddbdbSmaxv fatal("bootspace_addseg: segments full");
284ce4ddbdbSmaxv }
285ce4ddbdbSmaxv
28626e9e80dSmaxv static size_t
mm_shift_segment(vaddr_t va,size_t pagesz,size_t elfsz,size_t elfalign)28726e9e80dSmaxv mm_shift_segment(vaddr_t va, size_t pagesz, size_t elfsz, size_t elfalign)
288c9759921Smaxv {
28926e9e80dSmaxv size_t shiftsize, offset;
29026e9e80dSmaxv uint64_t rnd;
29126e9e80dSmaxv
292ad3185a9Smaxv /*
293ad3185a9Smaxv * If possible, shift the segment in memory using a random offset. Once
294ad3185a9Smaxv * shifted the segment remains in the same page, of size pagesz. Make
295ad3185a9Smaxv * sure to respect the ELF alignment constraint.
296ad3185a9Smaxv */
297ad3185a9Smaxv
29826e9e80dSmaxv if (elfalign == 0) {
29926e9e80dSmaxv elfalign = ELFROUND;
30026e9e80dSmaxv }
30126e9e80dSmaxv
3025b84bc55Smaxv ASSERT(pagesz >= elfalign);
3035b84bc55Smaxv ASSERT(pagesz % elfalign == 0);
30426e9e80dSmaxv shiftsize = roundup(elfsz, pagesz) - roundup(elfsz, elfalign);
30526e9e80dSmaxv if (shiftsize == 0) {
30626e9e80dSmaxv return 0;
30726e9e80dSmaxv }
30826e9e80dSmaxv
30949d35b9cSmaxv prng_get_rand(&rnd, sizeof(rnd));
31026e9e80dSmaxv offset = roundup(rnd % shiftsize, elfalign);
31126e9e80dSmaxv ASSERT((va + offset) % elfalign == 0);
31226e9e80dSmaxv
31326e9e80dSmaxv memmove((void *)(va + offset), (void *)va, elfsz);
31426e9e80dSmaxv
31526e9e80dSmaxv return offset;
31626e9e80dSmaxv }
31726e9e80dSmaxv
318a98ea778Smaxv static void
mm_map_head(void)319a98ea778Smaxv mm_map_head(void)
320a98ea778Smaxv {
321a98ea778Smaxv size_t i, npages, size;
322a98ea778Smaxv uint64_t rnd;
323a98ea778Smaxv vaddr_t randva;
324a98ea778Smaxv
325a98ea778Smaxv /*
326ad3185a9Smaxv * The HEAD window is 1GB below the main KASLR window. This is to
327ad3185a9Smaxv * ensure that head always comes first in virtual memory. The reason
328ad3185a9Smaxv * for that is that we use (headva + sh_offset), and sh_offset is
329ad3185a9Smaxv * unsigned.
330ad3185a9Smaxv */
331ad3185a9Smaxv
332ad3185a9Smaxv /*
333a98ea778Smaxv * To get the size of the head, we give a look at the read-only
334a98ea778Smaxv * mapping of the kernel we created in locore. We're identity mapped,
335a98ea778Smaxv * so kernpa = kernva.
336a98ea778Smaxv */
337a98ea778Smaxv size = elf_get_head_size((vaddr_t)kernpa_start);
338a98ea778Smaxv npages = size / PAGE_SIZE;
339a98ea778Smaxv
340ad3185a9Smaxv /*
341ad3185a9Smaxv * Choose a random range of VAs in the HEAD window, and create the page
342ad3185a9Smaxv * tree for it.
343ad3185a9Smaxv */
34449d35b9cSmaxv prng_get_rand(&rnd, sizeof(rnd));
345a98ea778Smaxv randva = rounddown(HEAD_WINDOW_BASE + rnd % (HEAD_WINDOW_SIZE - size),
346a98ea778Smaxv PAGE_SIZE);
347a98ea778Smaxv mm_map_tree(randva, randva + size);
348a98ea778Smaxv
349a98ea778Smaxv /* Enter the area and build the ELF info */
350a98ea778Smaxv for (i = 0; i < npages; i++) {
351a98ea778Smaxv mm_enter_pa(kernpa_start + i * PAGE_SIZE,
352a98ea778Smaxv randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
353a98ea778Smaxv }
354a98ea778Smaxv elf_build_head(randva);
355a98ea778Smaxv
356a98ea778Smaxv /* Register the values in bootspace */
357a98ea778Smaxv bootspace.head.va = randva;
358a98ea778Smaxv bootspace.head.pa = kernpa_start;
359a98ea778Smaxv bootspace.head.sz = size;
360a98ea778Smaxv }
361a98ea778Smaxv
36226e9e80dSmaxv vaddr_t
mm_map_segment(int segtype,paddr_t pa,size_t elfsz,size_t elfalign)36326e9e80dSmaxv mm_map_segment(int segtype, paddr_t pa, size_t elfsz, size_t elfalign)
36426e9e80dSmaxv {
36526e9e80dSmaxv size_t i, npages, size, pagesz, offset;
366569f0c08Smaxv vaddr_t randva;
3676ac8be24Smaxv char pad;
368c9759921Smaxv
369d38e6506Smaxv if (elfsz <= PAGE_SIZE) {
37026e9e80dSmaxv pagesz = NBPD_L1;
37126e9e80dSmaxv } else {
37226e9e80dSmaxv pagesz = NBPD_L2;
37326e9e80dSmaxv }
374c9759921Smaxv
375ad3185a9Smaxv /* Create the page tree */
37626e9e80dSmaxv size = roundup(elfsz, pagesz);
37726e9e80dSmaxv randva = mm_randva_kregion(size, pagesz);
37826e9e80dSmaxv
379ad3185a9Smaxv /* Enter the segment */
38026e9e80dSmaxv npages = size / PAGE_SIZE;
381c9759921Smaxv for (i = 0; i < npages; i++) {
382569f0c08Smaxv mm_enter_pa(pa + i * PAGE_SIZE,
383569f0c08Smaxv randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
384569f0c08Smaxv }
385569f0c08Smaxv
386ad3185a9Smaxv /* Shift the segment in memory */
38726e9e80dSmaxv offset = mm_shift_segment(randva, pagesz, elfsz, elfalign);
38826e9e80dSmaxv ASSERT(offset + elfsz <= size);
38926e9e80dSmaxv
390ad3185a9Smaxv /* Fill the paddings */
3915b84bc55Smaxv pad = pads[segtype];
39226e9e80dSmaxv memset((void *)randva, pad, offset);
39326e9e80dSmaxv memset((void *)(randva + offset + elfsz), pad, size - elfsz - offset);
394569f0c08Smaxv
395ad3185a9Smaxv /* Register the bootspace information */
3966ac8be24Smaxv bootspace_addseg(segtype, randva, pa, size);
3972ef67c05Smaxv
39826e9e80dSmaxv return (randva + offset);
399c9759921Smaxv }
400c9759921Smaxv
401569f0c08Smaxv static void
mm_map_boot(void)40292fe5cfbSmaxv mm_map_boot(void)
403569f0c08Smaxv {
404569f0c08Smaxv size_t i, npages, size;
405569f0c08Smaxv vaddr_t randva;
406569f0c08Smaxv paddr_t bootpa;
407569f0c08Smaxv
408569f0c08Smaxv /*
409569f0c08Smaxv * The "boot" region is special: its page tree has a fixed size, but
410569f0c08Smaxv * the number of pages entered is lower.
411569f0c08Smaxv */
412569f0c08Smaxv
41393e18f60Smaxv /* Create the page tree, starting at a random VA */
414569f0c08Smaxv size = (NKL2_KIMG_ENTRIES + 1) * NBPD_L2;
41526e9e80dSmaxv randva = mm_randva_kregion(size, PAGE_SIZE);
416569f0c08Smaxv
41793e18f60Smaxv /* The "boot" region begins right after the kernel segments */
41893e18f60Smaxv bootpa = bootspace_get_kern_segs_end_pa();
41993e18f60Smaxv
420d4a66c4eSmaxv /* The prekern consumed some EXTRA memory up until pa_avail, this
421d4a66c4eSmaxv * covers REL/RELA/SYM/STR and EXTRA */
422569f0c08Smaxv size = (pa_avail - bootpa);
423569f0c08Smaxv npages = size / PAGE_SIZE;
42493e18f60Smaxv
42593e18f60Smaxv /* Enter the whole area linearly */
426569f0c08Smaxv for (i = 0; i < npages; i++) {
427569f0c08Smaxv mm_enter_pa(bootpa + i * PAGE_SIZE,
428569f0c08Smaxv randva + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
429569f0c08Smaxv }
43093e18f60Smaxv
431d4a66c4eSmaxv /* Fix up the ELF sections located in the "boot" region */
432d4a66c4eSmaxv elf_fixup_boot(randva, bootpa);
433569f0c08Smaxv
434d4a66c4eSmaxv /* Map the ISA I/O MEM right after EXTRA, in pure VA */
435569f0c08Smaxv iom_base = randva + npages * PAGE_SIZE;
436c9759921Smaxv npages = IOM_SIZE / PAGE_SIZE;
437c9759921Smaxv for (i = 0; i < npages; i++) {
438c9759921Smaxv mm_enter_pa(IOM_BEGIN + i * PAGE_SIZE,
439c9759921Smaxv iom_base + i * PAGE_SIZE, MM_PROT_READ|MM_PROT_WRITE);
440c9759921Smaxv }
441c9759921Smaxv
442569f0c08Smaxv /* Register the values in bootspace */
443569f0c08Smaxv bootspace.boot.va = randva;
444569f0c08Smaxv bootspace.boot.pa = bootpa;
445569f0c08Smaxv bootspace.boot.sz = (size_t)(iom_base + IOM_SIZE) -
446569f0c08Smaxv (size_t)bootspace.boot.va;
447569f0c08Smaxv
448569f0c08Smaxv /* Initialize the values that are located in the "boot" region */
449569f0c08Smaxv extern uint64_t PDPpaddr;
450569f0c08Smaxv bootspace.spareva = bootspace.boot.va + NKL2_KIMG_ENTRIES * NBPD_L2;
451569f0c08Smaxv bootspace.pdir = bootspace.boot.va + (PDPpaddr - bootspace.boot.pa);
452bb09e40cSmaxv bootspace.smodule = (vaddr_t)iom_base + IOM_SIZE;
453569f0c08Smaxv bootspace.emodule = bootspace.boot.va + NKL2_KIMG_ENTRIES * NBPD_L2;
454c9759921Smaxv }
455569f0c08Smaxv
456569f0c08Smaxv /*
457ad3185a9Smaxv * The bootloader has set up the following layout of physical memory:
458d4a66c4eSmaxv * +------------+--------------+------------+------------------------+-------+
459d4a66c4eSmaxv * | ELF HEADER | SECT HEADERS | KERN SECTS | REL/RELA/SYM/STR SECTS | EXTRA |
460d4a66c4eSmaxv * +------------+--------------+------------+------------------------+-------+
46193e18f60Smaxv * This was done in the loadfile_elf32.c:loadfile_dynamic() function.
46293e18f60Smaxv *
46393e18f60Smaxv * We abstract this layout into several "regions":
464d4a66c4eSmaxv * +---------------------------+------------+--------------------------------+
465d4a66c4eSmaxv * | Head region | Kern segs | Boot region |
466d4a66c4eSmaxv * +---------------------------+------------+--------------------------------+
467ad3185a9Smaxv *
468ad3185a9Smaxv * There is a variable number of independent regions we create: one head,
469ad3185a9Smaxv * several kernel segments, one boot. They are all mapped at random VAs.
470569f0c08Smaxv *
47193e18f60Smaxv * "Head" contains the ELF Header and ELF Section Headers, and we use them to
47293e18f60Smaxv * map the rest of the regions. Head must be placed *before* the other
47393e18f60Smaxv * regions, in both virtual memory and physical memory.
474ad3185a9Smaxv *
47593e18f60Smaxv * The "Kernel Segments" contain the kernel SHT_NOBITS and SHT_PROGBITS
47693e18f60Smaxv * sections, in a 1:1 manner (one segment is associated with one section).
47793e18f60Smaxv * The segments are mapped at random VAs and referenced in bootspace.segs[].
478ad3185a9Smaxv *
479d4a66c4eSmaxv * "Boot" contains miscellaneous information:
480d4a66c4eSmaxv * - The ELF Rel/Rela/Sym/Str sections of the kernel
481d4a66c4eSmaxv * - Some extra memory the prekern has consumed so far
482d4a66c4eSmaxv * - The ISA I/O MEM, in pure VA
483d4a66c4eSmaxv * - Eventually the module_map, in pure VA (the kernel uses the available VA
484d4a66c4eSmaxv * at the end of "boot")
485d4a66c4eSmaxv * Boot is placed *after* the other regions in physical memory. In virtual
486d4a66c4eSmaxv * memory however there is no constraint, so its VA is randomly selected in
487d4a66c4eSmaxv * the main KASLR window.
488569f0c08Smaxv *
489569f0c08Smaxv * At the end of this function, the bootspace structure is fully constructed.
490569f0c08Smaxv */
491569f0c08Smaxv void
mm_map_kernel(void)49292fe5cfbSmaxv mm_map_kernel(void)
493569f0c08Smaxv {
494569f0c08Smaxv memset(&bootspace, 0, sizeof(bootspace));
495569f0c08Smaxv mm_map_head();
496*dadf0eefSkhorben print_state(STATE_NORMAL, "Head region mapped");
4976ac8be24Smaxv elf_map_sections();
498*dadf0eefSkhorben print_state(STATE_NORMAL, "Segments mapped");
499569f0c08Smaxv mm_map_boot();
500*dadf0eefSkhorben print_state(STATE_NORMAL, "Boot region mapped");
501569f0c08Smaxv }
502