[BACK]Return to mm.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / arch / amd64 / stand / prekern

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>