#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",
"breakpoint trap",
"arithmetic trap",
"asynchronous system trap",
"protection fault",
"trace trap",
"page fault",
"alignment fault",
"integer divide fault",
"non-maskable interrupt",
"overflow trap",
"bounds check fault",
"FPU not available fault",
"double fault",
"FPU operand fetch fault",
"invalid TSS fault",
"segment not present fault",
"stack fault",
"machine check fault",
"SSE FP exception",
"reserved trap",
};
static int trap_types = __arraycount(trap_type);
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;
jumpfunc = (void *)ent;
ret = (*jumpfunc)(&pkargs);
if (ret == -1) {
fatal("kernel returned: wrong API version");
} else {
fatal("kernel returned: unknown value");
}
}
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");
}
if (pa_start < kernpa_end) {
fatal("init_prekern: physical space inside kernel");
}
mm_init(pa_start);
init_idt();
print_state(STATE_NORMAL, "Prekern loaded");
prng_init();
mm_map_kernel();
elf_build_info();
ent = elf_kernel_reloc();
mm_bootspace_mprotect();
init_prekern_args();
print_state(STATE_NORMAL, "Jumping into the kernel");
jump_kernel(ent);
fatal("init_prekern: unreachable!");
}