/* $NetBSD: prekern.c,v 1.14 2021/05/04 21:09:16 khorben Exp $ */ /* * Copyright (c) 2017-2020 The NetBSD Foundation, Inc. All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Maxime Villard. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "prekern.h" #include #include #include #define _KERNEL #include #undef _KERNEL #include #include int boothowto; struct bootinfo bootinfo; extern paddr_t kernpa_start, kernpa_end; static uint8_t idtstore[PAGE_SIZE] __aligned(PAGE_SIZE); #define IDTVEC(name) __CONCAT(X, name) typedef void (vector)(void); extern vector *x86_exceptions[]; void fatal(char *msg) { print("\n"); print_ext(RED_ON_BLACK, "********** FATAL ***********\n"); print_ext(RED_ON_BLACK, msg); print("\n"); print_ext(RED_ON_BLACK, "****************************\n"); while (1); } /* -------------------------------------------------------------------------- */ struct smallframe { uint64_t sf_trapno; uint64_t sf_err; uint64_t sf_rip; uint64_t sf_cs; uint64_t sf_rflags; uint64_t sf_rsp; uint64_t sf_ss; }; void trap(struct smallframe *); static char *trap_type[] = { "privileged instruction fault", /* 0 T_PRIVINFLT */ "breakpoint trap", /* 1 T_BPTFLT */ "arithmetic trap", /* 2 T_ARITHTRAP */ "asynchronous system trap", /* 3 T_ASTFLT */ "protection fault", /* 4 T_PROTFLT */ "trace trap", /* 5 T_TRCTRAP */ "page fault", /* 6 T_PAGEFLT */ "alignment fault", /* 7 T_ALIGNFLT */ "integer divide fault", /* 8 T_DIVIDE */ "non-maskable interrupt", /* 9 T_NMI */ "overflow trap", /* 10 T_OFLOW */ "bounds check fault", /* 11 T_BOUND */ "FPU not available fault", /* 12 T_DNA */ "double fault", /* 13 T_DOUBLEFLT */ "FPU operand fetch fault", /* 14 T_FPOPFLT */ "invalid TSS fault", /* 15 T_TSSFLT */ "segment not present fault", /* 16 T_SEGNPFLT */ "stack fault", /* 17 T_STKFLT */ "machine check fault", /* 18 T_MCA */ "SSE FP exception", /* 19 T_XMM */ "reserved trap", /* 20 T_RESERVED */ }; static int trap_types = __arraycount(trap_type); /* * Trap handler. */ void trap(struct smallframe *sf) { uint64_t trapno = sf->sf_trapno; char *buf; if (trapno < trap_types) { buf = trap_type[trapno]; } else { buf = "unknown trap"; } print("\n"); print_ext(RED_ON_BLACK, "****** FAULT OCCURRED ******\n"); print_ext(RED_ON_BLACK, buf); print("\n"); print_ext(RED_ON_BLACK, "****************************\n"); while (1); } /* -------------------------------------------------------------------------- */ static void setregion(struct region_descriptor *rd, void *base, uint16_t limit) { rd->rd_limit = limit; rd->rd_base = (uint64_t)base; } static void setgate(struct gate_descriptor *gd, void *func, int ist, int type, int dpl, int sel) { gd->gd_looffset = (uint64_t)func & 0xffff; gd->gd_selector = sel; gd->gd_ist = ist; gd->gd_type = type; gd->gd_dpl = dpl; gd->gd_p = 1; gd->gd_hioffset = (uint64_t)func >> 16; gd->gd_zero = 0; gd->gd_xx1 = 0; gd->gd_xx2 = 0; gd->gd_xx3 = 0; } static void init_idt(void) { struct region_descriptor region; struct gate_descriptor *idt; size_t i; idt = (struct gate_descriptor *)&idtstore; for (i = 0; i < NCPUIDT; i++) { setgate(&idt[i], x86_exceptions[i], 0, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } setregion(®ion, &idtstore, PAGE_SIZE - 1); lidt(®ion); } /* -------------------------------------------------------------------------- */ #define PREKERN_API_VERSION 2 struct prekern_args { int version; int boothowto; void *bootinfo; void *bootspace; int esym; int biosextmem; int biosbasemem; int cpuid_level; uint32_t nox_flag; uint64_t PDPpaddr; vaddr_t atdevbase; vaddr_t lwp0uarea; paddr_t first_avail; }; struct prekern_args pkargs; static void init_prekern_args(void) { extern struct bootspace bootspace; extern int esym; extern int biosextmem; extern int biosbasemem; extern int cpuid_level; extern uint32_t nox_flag; extern uint64_t PDPpaddr; extern vaddr_t iom_base; extern paddr_t stkpa; extern paddr_t pa_avail; memset(&pkargs, 0, sizeof(pkargs)); pkargs.version = PREKERN_API_VERSION; pkargs.boothowto = boothowto; pkargs.bootinfo = (void *)&bootinfo; pkargs.bootspace = &bootspace; pkargs.esym = esym; pkargs.biosextmem = biosextmem; pkargs.biosbasemem = biosbasemem; pkargs.cpuid_level = cpuid_level; pkargs.nox_flag = nox_flag; pkargs.PDPpaddr = PDPpaddr; pkargs.atdevbase = iom_base; pkargs.lwp0uarea = bootspace.boot.va + (stkpa - bootspace.boot.pa); pkargs.first_avail = pa_avail; extern vaddr_t stkva; stkva = pkargs.lwp0uarea + (USPACE - FRAMESIZE); } void exec_kernel(vaddr_t ent) { int (*jumpfunc)(struct prekern_args *); int ret; /* * Normally, the function does not return. If it does, it means the * kernel had trouble processing the arguments, and we panic here. The * return value is here for debug. */ jumpfunc = (void *)ent; ret = (*jumpfunc)(&pkargs); if (ret == -1) { fatal("kernel returned: wrong API version"); } else { fatal("kernel returned: unknown value"); } } /* * Main entry point of the Prekern. */ void init_prekern(paddr_t pa_start) { vaddr_t ent; init_cons(); print_banner(); if (kernpa_start == 0 || kernpa_end == 0) { fatal("init_prekern: unable to locate the kernel"); } if (kernpa_start != (1UL << 21)) { fatal("init_prekern: invalid kernpa_start"); } if (kernpa_start % PAGE_SIZE != 0) { fatal("init_prekern: kernpa_start not aligned"); } if (kernpa_end % PAGE_SIZE != 0) { fatal("init_prekern: kernpa_end not aligned"); } if (kernpa_end <= kernpa_start) { fatal("init_prekern: kernpa_end >= kernpa_start"); } /* * Our physical space starts after the end of the kernel. */ if (pa_start < kernpa_end) { fatal("init_prekern: physical space inside kernel"); } mm_init(pa_start); /* * Init the IDT. We mostly don't care about this, it's just here * to properly handle traps. */ init_idt(); print_state(STATE_NORMAL, "Prekern loaded"); /* * Init the PRNG. */ prng_init(); /* * Relocate the kernel. */ mm_map_kernel(); elf_build_info(); ent = elf_kernel_reloc(); mm_bootspace_mprotect(); /* * Build the arguments. */ init_prekern_args(); /* * Finally, jump into the kernel. */ print_state(STATE_NORMAL, "Jumping into the kernel"); jump_kernel(ent); fatal("init_prekern: unreachable!"); }