root/sys/arch/amd64/stand/prekern/prekern.c
/*      $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 <machine/reg.h>
#include <machine/specialreg.h>
#include <machine/frame.h>

#define _KERNEL
#include <machine/bootinfo.h>
#undef _KERNEL

#include <machine/tss.h>
#include <machine/segments.h>

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(&region, &idtstore, PAGE_SIZE - 1);
        lidt(&region);
}

/* -------------------------------------------------------------------------- */

#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!");
}