take over old x86-qemu
This commit is contained in:
parent
e6aa03d69c
commit
05f7e8b088
|
@ -1,25 +1,25 @@
|
|||
include $(AM_HOME)/am/arch/isa/x86.mk
|
||||
|
||||
AM_SRCS := x86/qemu/trm.c \
|
||||
x86/qemu/ioe.c \
|
||||
x86/qemu/cte.c \
|
||||
x86/qemu/cte_trap.S \
|
||||
x86/qemu/vme.c \
|
||||
x86/qemu/mpe.c \
|
||||
x86/qemu/devices/apic.c \
|
||||
x86/qemu/devices/cpu.c \
|
||||
x86/qemu/devices/input.c \
|
||||
x86/qemu/devices/pcdev.c \
|
||||
x86/qemu/devices/timer.c \
|
||||
x86/qemu/devices/video.c \
|
||||
AM_SRCS := x86/qemu/start32.S \
|
||||
x86/qemu/trap32.S \
|
||||
x86/qemu/trm.c \
|
||||
x86/qemu/cte.c \
|
||||
x86/qemu/ioe.c \
|
||||
x86/qemu/vme.c \
|
||||
x86/qemu/mpe.c
|
||||
|
||||
image:
|
||||
@make -s -C $(AM_HOME)/am/src/x86/qemu/boot
|
||||
@echo + LD "->" $(BINARY_REL).o
|
||||
@$(LD) $(LDFLAGS) -Ttext 0x00100000 -o $(BINARY).o $(LINK_FILES)
|
||||
@echo + CREATE "->" $(BINARY_REL)
|
||||
@( cat $(AM_HOME)/am/src/x86/qemu/boot/mbr; head -c 512 /dev/zero; cat $(BINARY).o ) > $(BINARY)
|
||||
@( cat $(AM_HOME)/am/src/x86/qemu/boot/mbr; head -c 1024 /dev/zero; cat $(BINARY).o ) > $(BINARY)
|
||||
|
||||
|
||||
run:
|
||||
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=1 seek=1 conv=notrunc status=none
|
||||
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=2 seek=1 conv=notrunc status=none
|
||||
@qemu-system-i386 -serial stdio -machine accel=kvm:tcg -smp "$(smp)" -drive format=raw,file=$(BINARY)
|
||||
|
||||
debug:
|
||||
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=2 seek=1 conv=notrunc status=none
|
||||
@qemu-system-i386 -S -s -serial none -machine accel=kvm:tcg -smp "$(smp)" -drive format=raw,file=$(BINARY) -nographic # & pid=$$!; gdb -x x.gdb && kill -9 $$pid
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
include $(AM_HOME)/am/arch/isa/x86.mk
|
||||
|
||||
AM_SRCS := x86_64/qemu/start32.S \
|
||||
x86_64/qemu/trap32.S \
|
||||
x86_64/qemu/trm.c \
|
||||
x86_64/qemu/cte.c \
|
||||
x86_64/qemu/ioe.c \
|
||||
x86_64/qemu/vme.c \
|
||||
x86_64/qemu/mpe.c
|
||||
|
||||
image:
|
||||
@make -s -C $(AM_HOME)/am/src/x86_64/qemu/boot
|
||||
@echo + LD "->" $(BINARY_REL).o
|
||||
@$(LD) $(LDFLAGS) -Ttext 0x00100000 -o $(BINARY).o $(LINK_FILES)
|
||||
@echo + CREATE "->" $(BINARY_REL)
|
||||
@( cat $(AM_HOME)/am/src/x86_64/qemu/boot/mbr; head -c 1024 /dev/zero; cat $(BINARY).o ) > $(BINARY)
|
||||
|
||||
|
||||
run:
|
||||
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=2 seek=1 conv=notrunc status=none
|
||||
@qemu-system-i386 -serial stdio -machine accel=kvm:tcg -smp "$(smp)" -drive format=raw,file=$(BINARY)
|
||||
|
||||
debug:
|
||||
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=2 seek=1 conv=notrunc status=none
|
||||
@qemu-system-i386 -S -s -serial none -machine accel=kvm:tcg -smp "$(smp)" -drive format=raw,file=$(BINARY) -nographic # & pid=$$!; gdb -x x.gdb && kill -9 $$pid
|
|
@ -1,19 +1,19 @@
|
|||
include $(AM_HOME)/am/arch/isa/x86_64.mk
|
||||
|
||||
AM_SRCS := x86_64/qemu/start64.S \
|
||||
x86_64/qemu/trap64.S \
|
||||
x86_64/qemu/trm.c \
|
||||
x86_64/qemu/cte.c \
|
||||
x86_64/qemu/ioe.c \
|
||||
x86_64/qemu/vme.c \
|
||||
x86_64/qemu/mpe.c
|
||||
AM_SRCS := x86/qemu/start64.S \
|
||||
x86/qemu/trap64.S \
|
||||
x86/qemu/trm.c \
|
||||
x86/qemu/cte.c \
|
||||
x86/qemu/ioe.c \
|
||||
x86/qemu/vme.c \
|
||||
x86/qemu/mpe.c
|
||||
|
||||
image:
|
||||
@make -s -C $(AM_HOME)/am/src/x86_64/qemu/boot
|
||||
@make -s -C $(AM_HOME)/am/src/x86/qemu/boot
|
||||
@echo + LD "->" $(BINARY_REL).o
|
||||
@$(LD) $(LDFLAGS) -N -Ttext-segment=0x00100000 -o $(BINARY).o $(LINK_FILES)
|
||||
@echo + CREATE "->" $(BINARY_REL)
|
||||
@( cat $(AM_HOME)/am/src/x86_64/qemu/boot/mbr; head -c 1024 /dev/zero; cat $(BINARY).o ) > $(BINARY)
|
||||
@( cat $(AM_HOME)/am/src/x86/qemu/boot/mbr; head -c 1024 /dev/zero; cat $(BINARY).o ) > $(BINARY)
|
||||
|
||||
run:
|
||||
@( echo -n $(mainargs); ) | dd if=/dev/stdin of=$(BINARY) bs=512 count=2 seek=1 conv=notrunc status=none
|
||||
|
|
|
@ -3,11 +3,9 @@
|
|||
|
||||
struct _Context {
|
||||
struct _AddressSpace *uvm;
|
||||
uint32_t eax, ebx, ecx, edx,
|
||||
esi, edi, ebp, esp3,
|
||||
eip, eflags,
|
||||
cs, ds, es, ss,
|
||||
ss0, esp0;
|
||||
uint32_t ds, eax, ebx, ecx, edx,
|
||||
esp0, esi, edi, ebp,
|
||||
eip, cs, eflags, esp, ss3;
|
||||
};
|
||||
|
||||
#define GPR1 eax
|
||||
|
@ -16,4 +14,4 @@ struct _Context {
|
|||
#define GPR4 edx
|
||||
#define GPRx eax
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#ifndef __ARCH_H__
|
||||
#define __ARCH_H__
|
||||
|
||||
struct _Context {
|
||||
struct _AddressSpace *uvm;
|
||||
uint32_t ds, eax, ebx, ecx, edx,
|
||||
esp0, esi, edi, ebp,
|
||||
eip, cs, eflags, esp, ss3;
|
||||
};
|
||||
|
||||
#define GPR1 eax
|
||||
#define GPR2 ebx
|
||||
#define GPR3 ecx
|
||||
#define GPR4 edx
|
||||
#define GPRx eax
|
||||
|
||||
#endif
|
|
@ -2,7 +2,7 @@ $(info # Building bootblock [$(ARCH)])
|
|||
|
||||
mbr: start.S main.c
|
||||
@echo + CC start.S main.c
|
||||
@gcc -nostdlib -Os start.S main.c -m32 -Ttext 0x7C00 -o bootblock.o
|
||||
@gcc -nostdlib -I$(AM_HOME)/am/include -Os start.S main.c -m32 -Ttext 0x7C00 -o bootblock.o
|
||||
@objcopy -S -O binary -j .text bootblock.o mbr
|
||||
@perl genboot.pl mbr
|
||||
|
||||
|
|
|
@ -1,115 +1,90 @@
|
|||
#include <stdint.h>
|
||||
|
||||
struct ELFHeader {
|
||||
uint32_t magic;
|
||||
uint8_t elf[12];
|
||||
uint16_t type;
|
||||
uint16_t machine;
|
||||
uint32_t version;
|
||||
uint32_t entry;
|
||||
uint32_t phoff;
|
||||
uint32_t shoff;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize;
|
||||
uint16_t phentsize;
|
||||
uint16_t phnum;
|
||||
uint16_t shentsize;
|
||||
uint16_t shnum;
|
||||
uint16_t shstrndx;
|
||||
};
|
||||
|
||||
struct ProgramHeader {
|
||||
uint32_t type;
|
||||
uint32_t off;
|
||||
uint32_t vaddr;
|
||||
uint32_t paddr;
|
||||
uint32_t filesz;
|
||||
uint32_t memsz;
|
||||
uint32_t flags;
|
||||
uint32_t align;
|
||||
};
|
||||
|
||||
static inline char in_byte(short port) {
|
||||
char data;
|
||||
__asm__ volatile ("in %1,%0" : "=a" (data) : "d" (port));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline int in_long(short port) {
|
||||
int data;
|
||||
__asm__ volatile ("in %1, %0" : "=a" (data) : "d" (port));
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void out_byte(short port, char data) {
|
||||
__asm__ volatile ("out %0,%1" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
static inline void hlt() {
|
||||
__asm__ volatile ("hlt");
|
||||
}
|
||||
#include <linux/elf.h>
|
||||
#include <linux/elf-em.h>
|
||||
#include <x86.h>
|
||||
|
||||
#define SECTSIZE 512
|
||||
|
||||
void readseg(void *, int, int);
|
||||
|
||||
struct boot_info {
|
||||
int is_ap;
|
||||
void (*entry)();
|
||||
};
|
||||
|
||||
void
|
||||
bootmain(void) {
|
||||
volatile struct boot_info *boot = (void*)0x7000;
|
||||
int is_ap = boot->is_ap;
|
||||
if (is_ap == 1) {
|
||||
boot->entry();
|
||||
}
|
||||
struct ELFHeader *elf;
|
||||
struct ProgramHeader *ph, *eph;
|
||||
unsigned char *pa, *i;
|
||||
|
||||
elf = (struct ELFHeader*)0x8000;
|
||||
|
||||
readseg(elf, 4096, 0);
|
||||
|
||||
ph = (struct ProgramHeader*)((char *)elf + elf->phoff);
|
||||
eph = ph + elf->phnum;
|
||||
for(; ph < eph; ph ++) {
|
||||
pa = (void *)(ph->paddr);
|
||||
readseg(pa, ph->filesz, ph->off);
|
||||
for (i = pa + ph->filesz; i < pa + ph->memsz; *i++ = 0);
|
||||
}
|
||||
|
||||
char *mainargs = (void *)0x7e00;
|
||||
readseg(mainargs, 512, -512);
|
||||
((void(*)(const char *))elf->entry)(mainargs);
|
||||
static inline void wait_disk(void) {
|
||||
while ((inb(0x1f7) & 0xc0) != 0x40);
|
||||
}
|
||||
|
||||
void waitdisk(void) {
|
||||
while ((in_byte(0x1F7) & 0xC0) != 0x40);
|
||||
}
|
||||
|
||||
void readsect(volatile void *dst, int offset) {
|
||||
waitdisk();
|
||||
out_byte(0x1F2, 1);
|
||||
out_byte(0x1F3, offset);
|
||||
out_byte(0x1F4, offset >> 8);
|
||||
out_byte(0x1F5, offset >> 16);
|
||||
out_byte(0x1F6, (offset >> 24) | 0xE0);
|
||||
out_byte(0x1F7, 0x20);
|
||||
|
||||
waitdisk();
|
||||
static inline void read_disk(void *buf, int sect) {
|
||||
wait_disk();
|
||||
outb(0x1f2, 1);
|
||||
outb(0x1f3, sect);
|
||||
outb(0x1f4, sect >> 8);
|
||||
outb(0x1f5, sect >> 16);
|
||||
outb(0x1f6, (sect >> 24) | 0xE0);
|
||||
outb(0x1f7, 0x20);
|
||||
wait_disk();
|
||||
for (int i = 0; i < SECTSIZE / 4; i ++) {
|
||||
((int *)dst)[i] = in_long(0x1F0);
|
||||
((uint32_t *)buf)[i] = inl(0x1f0);
|
||||
}
|
||||
}
|
||||
|
||||
void readseg(void *addr, int count, int offset) {
|
||||
unsigned char *pa = addr;
|
||||
unsigned char *epa = pa + count;
|
||||
pa -= offset % SECTSIZE;
|
||||
offset = (offset / SECTSIZE) + 1 + 1 /* args */;
|
||||
for(; pa < epa; pa += SECTSIZE, offset ++)
|
||||
readsect(pa, offset);
|
||||
static inline void copy_from_disk(void *buf, int nbytes, int disk_offset) {
|
||||
uint32_t cur = (uint32_t)buf & ~511;
|
||||
uint32_t ed = (uint32_t)buf + nbytes;
|
||||
uint32_t sect = (disk_offset >> 9) + 3;
|
||||
for(; cur < ed; cur += SECTSIZE, sect ++)
|
||||
read_disk((void *)cur, sect);
|
||||
}
|
||||
|
||||
static void load_program(uint32_t filesz, uint32_t memsz, uint32_t paddr, uint32_t offset) {
|
||||
copy_from_disk((void *)paddr, filesz, offset);
|
||||
char *bss = (void *)(paddr + filesz);
|
||||
for (uint32_t i = filesz; i != memsz; i++) {
|
||||
*bss++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_elf64(struct elf64_hdr *elf) {
|
||||
struct elf64_phdr *ph = (struct elf64_phdr *)((char *)elf + elf->e_phoff);
|
||||
for (int i = 0; i < elf->e_phnum; i++, ph++) {
|
||||
load_program(
|
||||
(uint32_t)ph->p_filesz,
|
||||
(uint32_t)ph->p_memsz,
|
||||
(uint32_t)ph->p_paddr,
|
||||
(uint32_t)ph->p_offset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void load_elf32(struct elf32_hdr *elf) {
|
||||
struct elf32_phdr *ph = (struct elf32_phdr *)((char *)elf + elf->e_phoff);
|
||||
for (int i = 0; i < elf->e_phnum; i++, ph++) {
|
||||
load_program(
|
||||
(uint32_t)ph->p_filesz,
|
||||
(uint32_t)ph->p_memsz,
|
||||
(uint32_t)ph->p_paddr,
|
||||
(uint32_t)ph->p_offset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void load_kernel(void) {
|
||||
struct elf32_hdr *elf32 = (void *)0x8000;
|
||||
struct elf64_hdr *elf64 = (void *)0x8000;
|
||||
int is_ap = boot_record()->is_ap;
|
||||
|
||||
if (!is_ap) {
|
||||
// load argument (string) to memory
|
||||
copy_from_disk((void *)MAINARG_ADDR, 1024, -1024);
|
||||
// load elf header to memory
|
||||
copy_from_disk(elf32, 4096, 0);
|
||||
if (elf32->e_machine == EM_X86_64) {
|
||||
load_elf64(elf64);
|
||||
} else {
|
||||
load_elf32(elf32);
|
||||
}
|
||||
} else {
|
||||
// everything should be loaded
|
||||
}
|
||||
|
||||
if (elf32->e_machine == EM_X86_64) {
|
||||
((void(*)())(uint32_t)elf64->e_entry)();
|
||||
} else {
|
||||
((void(*)())(uint32_t)elf32->e_entry)();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@ start32:
|
|||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
|
||||
movl $0x7000, %esp
|
||||
call bootmain
|
||||
movl $0xa000, %esp
|
||||
call load_kernel
|
||||
|
||||
# GDT
|
||||
.p2align 2
|
||||
|
|
|
@ -1,120 +1,71 @@
|
|||
#include "../x86-qemu.h"
|
||||
#include "x86-qemu.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
static _Context* (*user_handler)(_Event, _Context*) = NULL;
|
||||
static GateDesc idt[NR_IRQ];
|
||||
#if __x86_64__
|
||||
static GateDesc64 idt[NR_IRQ];
|
||||
#define GATE GATE64
|
||||
#else
|
||||
static GateDesc32 idt[NR_IRQ];
|
||||
#define GATE GATE32
|
||||
#endif
|
||||
|
||||
#define IRQHANDLE_DECL(id, dpl, err) \
|
||||
void __am_irq##id();
|
||||
|
||||
#define IRQHANDLE_DECL(id, dpl, err) void __am_irq##id();
|
||||
IRQS(IRQHANDLE_DECL)
|
||||
void __am_irqall();
|
||||
|
||||
int _cte_init(_Context *(*handler)(_Event, _Context *)) {
|
||||
if (_cpu() != 0) panic("init CTE in non-bootstrap CPU");
|
||||
|
||||
for (int i = 0; i < NR_IRQ; i ++) {
|
||||
idt[i] = GATE(STS_TG32, KSEL(SEG_KCODE), __am_irqall, DPL_KERN);
|
||||
}
|
||||
#define IDT_ENTRY(id, dpl, err) \
|
||||
idt[id] = GATE(STS_TG32, KSEL(SEG_KCODE), __am_irq##id, DPL_##dpl);
|
||||
IRQS(IDT_ENTRY)
|
||||
|
||||
user_handler = handler;
|
||||
__am_percpu_initirq();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _yield() {
|
||||
if (!user_handler) panic("no interrupt handler");
|
||||
asm volatile ("int $0x80" : : "a"(-1));
|
||||
}
|
||||
|
||||
int _intr_read() {
|
||||
if (!user_handler) panic("no interrupt handler");
|
||||
return (get_efl() & FL_IF) != 0;
|
||||
}
|
||||
|
||||
void _intr_write(int enable) {
|
||||
if (!user_handler) panic("no interrupt handler");
|
||||
if (enable) {
|
||||
sti();
|
||||
} else {
|
||||
cli();
|
||||
}
|
||||
}
|
||||
|
||||
static void panic_on_return() { panic("kernel context returns"); }
|
||||
|
||||
_Context *_kcontext(_Area stack, void (*entry)(void *), void *arg) {
|
||||
_Area stk_safe = {
|
||||
(void *)ROUNDUP(stack.start, 64),
|
||||
(void *)ROUNDDOWN(stack.end, 64),
|
||||
};
|
||||
|
||||
_Context *ctx = (_Context *)stk_safe.start;
|
||||
*ctx = (_Context) {
|
||||
.eax = 0, .ebx = 0, .ecx = 0, .edx = 0,
|
||||
.esi = 0, .edi = 0, .ebp = 0, .esp3 = 0,
|
||||
.ss0 = 0, .esp0 = (uint32_t)stk_safe.end,
|
||||
.cs = KSEL(SEG_KCODE), .eip = (uint32_t)entry, .eflags = FL_IF,
|
||||
.ds = KSEL(SEG_KDATA), .es = KSEL(SEG_KDATA), .ss = KSEL(SEG_KDATA),
|
||||
.uvm = NULL,
|
||||
};
|
||||
|
||||
void *values[] = { panic_on_return, arg }; // copy to stack
|
||||
ctx->esp0 -= sizeof(values);
|
||||
for (int i = 0; i < LENGTH(values); i++) {
|
||||
((uintptr_t *)ctx->esp0)[i] = (uintptr_t)values[i];
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
#define IRQ T_IRQ0 +
|
||||
#define MSG(m) ev.msg = m;
|
||||
|
||||
void __am_irq_handle(TrapFrame *tf) {
|
||||
// saving processor context
|
||||
_Context ctx = {
|
||||
.eax = tf->eax, .ebx = tf->ebx, .ecx = tf->ecx, .edx = tf->edx,
|
||||
.esi = tf->esi, .edi = tf->edi, .ebp = tf->ebp, .esp3 = 0,
|
||||
.eip = tf->eip, .eflags = tf->eflags,
|
||||
.cs = tf->cs, .ds = tf->ds, .es = tf->es, .ss = 0,
|
||||
.ss0 = KSEL(SEG_KDATA), .esp0 = (uint32_t)(tf + 1),
|
||||
.uvm = CPU->uvm,
|
||||
};
|
||||
|
||||
if (tf->cs & DPL_USER) { // interrupt at user code
|
||||
ctx.ss = tf->ss;
|
||||
ctx.esp3 = tf->esp;
|
||||
} else { // interrupt at kernel code
|
||||
// tf (without ss0/esp0) is everything saved on the stack
|
||||
ctx.esp0 -= sizeof(uint32_t) * 2;
|
||||
}
|
||||
|
||||
// sending end-of-interrupt
|
||||
if (IRQ 0 <= tf->irq && tf->irq < IRQ 32) {
|
||||
__am_lapic_eoi();
|
||||
}
|
||||
|
||||
// creating an event
|
||||
static void __am_irq_handle_internal(struct trap_frame *tf) {
|
||||
_Context saved_ctx;
|
||||
_Event ev = {
|
||||
.event = _EVENT_NULL,
|
||||
.cause = 0, .ref = 0,
|
||||
.msg = "(no message)",
|
||||
};
|
||||
|
||||
|
||||
#if __x86_64
|
||||
saved_ctx = tf->saved_context;
|
||||
saved_ctx.rip = tf->rip;
|
||||
saved_ctx.cs = tf->cs;
|
||||
saved_ctx.rflags = tf->rflags;
|
||||
saved_ctx.rsp = tf->rsp;
|
||||
saved_ctx.rsp0 = CPU->tss.rsp0;
|
||||
saved_ctx.ss = tf->ss;
|
||||
saved_ctx.uvm = (void *)get_cr3();
|
||||
#else
|
||||
saved_ctx = tf->saved_context;
|
||||
saved_ctx.eip = tf->eip;
|
||||
saved_ctx.cs = tf->cs;
|
||||
saved_ctx.eflags = tf->eflags;
|
||||
saved_ctx.esp0 = CPU->tss.esp0;
|
||||
saved_ctx.ss3 = USEL(SEG_UDATA);
|
||||
saved_ctx.uvm = (void *)get_cr3();
|
||||
if (!(tf->cs & DPL_USER)) {
|
||||
saved_ctx.esp = (uint32_t)(tf + 1) - 8; // no ss/esp saved
|
||||
}
|
||||
#endif
|
||||
|
||||
#define IRQ T_IRQ0 +
|
||||
#define MSG(m) ev.msg = m;
|
||||
|
||||
if (IRQ 0 <= tf->irq && tf->irq < IRQ 32) {
|
||||
__am_lapic_eoi();
|
||||
}
|
||||
|
||||
switch (tf->irq) {
|
||||
case IRQ 0: MSG("timer interrupt (lapic)")
|
||||
ev.event = _EVENT_IRQ_TIMER; break;
|
||||
case IRQ 1: MSG("I/O device IRQ1 (keyboard)")
|
||||
ev.event = _EVENT_IRQ_IODEV; break;
|
||||
case EX_SYSCALL: MSG("int $0x80 trap: _yield() or system call")
|
||||
if ((int32_t)tf->eax == -1) {
|
||||
if ((int32_t)saved_ctx.GPR1 == -1) {
|
||||
ev.event = _EVENT_YIELD;
|
||||
} else {
|
||||
ev.event = _EVENT_SYSCALL;
|
||||
}
|
||||
break;
|
||||
case EX_DIV: MSG("divide by zero")
|
||||
case EX_DE: MSG("DE #0 divide by zero")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_UD: MSG("UD #6 invalid opcode")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
|
@ -132,69 +83,98 @@ void __am_irq_handle(TrapFrame *tf) {
|
|||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_PF: MSG("PF #14, page fault, @cause: _PROT_XXX")
|
||||
ev.event = _EVENT_PAGEFAULT;
|
||||
if (tf->err & 0x1) ev.cause |= _PROT_NONE;
|
||||
if (tf->err & 0x2) ev.cause |= _PROT_WRITE;
|
||||
else ev.cause |= _PROT_READ;
|
||||
if (tf->errcode & 0x1) ev.cause |= _PROT_NONE;
|
||||
if (tf->errcode & 0x2) ev.cause |= _PROT_WRITE;
|
||||
else ev.cause |= _PROT_READ;
|
||||
ev.ref = get_cr2();
|
||||
break;
|
||||
default: MSG("unrecognized interrupt/exception")
|
||||
ev.event = _EVENT_ERROR;
|
||||
ev.cause = tf->err;
|
||||
ev.cause = tf->errcode;
|
||||
break;
|
||||
}
|
||||
|
||||
// call user handlers (registered in _cte_init)
|
||||
_Context *ret_ctx = &ctx;
|
||||
if (user_handler) {
|
||||
_Context *next = user_handler(ev, &ctx);
|
||||
if (!next) {
|
||||
panic("return to a null context");
|
||||
}
|
||||
ret_ctx = next;
|
||||
_Context *ret_ctx = user_handler(ev, &saved_ctx);
|
||||
|
||||
if (ret_ctx->uvm) {
|
||||
set_cr3(ret_ctx->uvm);
|
||||
#if __x86_64__
|
||||
CPU->tss.rsp0 = ret_ctx->rsp0;
|
||||
#else
|
||||
CPU->tss.ss0 = KSEL(SEG_KDATA);
|
||||
CPU->tss.esp0 = ret_ctx->esp0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return to context @ret_ctx
|
||||
#define REGS_KERNEL(_) \
|
||||
_(eflags) _(cs) _(eip) _(ds) _(es) \
|
||||
_(eax) _(ecx) _(edx) _(ebx) _(esp0) _(ebp) _(esi) _(edi)
|
||||
#define REGS_USER(_) \
|
||||
_(ss) _(esp3) REGS_KERNEL(_)
|
||||
#define push(r) "push %[" #r "];" // -> push %[eax]
|
||||
#define def(r) , [r] "m"(ret_ctx->r) // -> [eax] "m"(ret_ctx->eax)
|
||||
|
||||
CPU->uvm = ret_ctx->uvm;
|
||||
if (ret_ctx->cs & DPL_USER) { // return to user
|
||||
_AddressSpace *uvm = ret_ctx->uvm;
|
||||
if (uvm) {
|
||||
set_cr3(uvm->ptr);
|
||||
}
|
||||
__am_thiscpu_setstk0(ret_ctx->ss0, ret_ctx->esp0);
|
||||
asm volatile goto (
|
||||
"movl %[esp], %%esp;" // move stack
|
||||
REGS_USER(push) // push reg context onto stack
|
||||
"jmp %l[iret]" // goto iret
|
||||
: : [esp] "m"(ret_ctx->esp0)
|
||||
REGS_USER(def) : : iret );
|
||||
} else { // return to kernel
|
||||
asm volatile goto (
|
||||
"movl %[esp], %%esp;" // move stack
|
||||
REGS_KERNEL(push) // push reg context onto stack
|
||||
"jmp %l[iret]" // goto iret
|
||||
: : [esp] "m"(ret_ctx->esp0)
|
||||
REGS_KERNEL(def) : : iret );
|
||||
__am_iret(ret_ctx ? ret_ctx : &saved_ctx);
|
||||
}
|
||||
|
||||
void __am_irq_handle(struct trap_frame *tf) {
|
||||
stack_switch_call(stack_top(&CPU->irq_stack), __am_irq_handle_internal, (uintptr_t)tf);
|
||||
}
|
||||
|
||||
int _cte_init(_Context *(*handler)(_Event, _Context *)) {
|
||||
panic_on(_cpu() != 0, "init CTE in non-bootstrap CPU");
|
||||
panic_on(!handler, "no interrupt handler");
|
||||
|
||||
for (int i = 0; i < NR_IRQ; i ++) {
|
||||
idt[i] = GATE(STS_TG, KSEL(SEG_KCODE), __am_irqall, DPL_KERN);
|
||||
}
|
||||
iret:
|
||||
asm volatile (
|
||||
"popal;" // restore context
|
||||
"popl %es;"
|
||||
"popl %ds;"
|
||||
"iret;" // interrupt return
|
||||
);
|
||||
#define IDT_ENTRY(id, dpl, err) \
|
||||
idt[id] = GATE(STS_TG, KSEL(SEG_KCODE), __am_irq##id, DPL_##dpl);
|
||||
IRQS(IDT_ENTRY)
|
||||
|
||||
user_handler = handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _yield() {
|
||||
interrupt(0x80, -1);
|
||||
}
|
||||
|
||||
int _intr_read() {
|
||||
return (get_efl() & FL_IF) != 0;
|
||||
}
|
||||
|
||||
void _intr_write(int enable) {
|
||||
if (enable) {
|
||||
sti();
|
||||
} else {
|
||||
cli();
|
||||
}
|
||||
}
|
||||
|
||||
static void panic_on_return() { panic("kernel context returns"); }
|
||||
|
||||
_Context *_kcontext(_Area stack, void (*entry)(void *), void *arg) {
|
||||
_Context *ctx = (_Context *)stack.start;
|
||||
*ctx = (_Context) { 0 };
|
||||
|
||||
#if __x86_64__
|
||||
#define sp rsp
|
||||
ctx->rsp = (uintptr_t)stack.end;
|
||||
ctx->cs = KSEL(SEG_KCODE);
|
||||
ctx->rip = (uintptr_t)entry;
|
||||
ctx->rflags = FL_IF;
|
||||
ctx->rdi = (uintptr_t)arg;
|
||||
void *stk[] = { panic_on_return };
|
||||
#else
|
||||
#define sp esp
|
||||
ctx->esp = (uintptr_t)stack.end;
|
||||
ctx->ds = KSEL(SEG_KDATA);
|
||||
ctx->cs = KSEL(SEG_KCODE);
|
||||
ctx->eip = (uint32_t)entry;
|
||||
ctx->eflags = FL_IF;
|
||||
void *stk[] = { panic_on_return, arg };
|
||||
#endif
|
||||
ctx->sp -= sizeof(stk);
|
||||
for (int i = 0; i < LENGTH(stk); i++) {
|
||||
((uintptr_t *)ctx->sp)[i] = (uintptr_t)stk[i];
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void __am_percpu_initirq() {
|
||||
if (user_handler) {
|
||||
__am_ioapic_enable(IRQ_KBD, 0);
|
||||
set_idt(idt, sizeof(idt));
|
||||
}
|
||||
__am_ioapic_enable(IRQ_KBD, 0);
|
||||
set_idt(idt, sizeof(idt));
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
#include <x86.h>
|
||||
|
||||
trap:
|
||||
pushl %ds
|
||||
pushl %es
|
||||
pushal
|
||||
|
||||
movw $KSEL(SEG_KDATA), %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
|
||||
pushl %esp
|
||||
call __am_irq_handle // iret in the C code
|
||||
|
||||
#define NOERR pushl $0
|
||||
#define ERR
|
||||
#define IRQ_DEF(id, dpl, err) \
|
||||
.globl __am_irq##id; __am_irq##id: cli; err; pushl $id; jmp trap;
|
||||
IRQS(IRQ_DEF)
|
||||
.globl __am_irqall; __am_irqall: cli; pushl $0; pushl $-1; jmp trap;
|
|
@ -1,128 +0,0 @@
|
|||
// LAPIC/IOAPIC related code
|
||||
// from xv6
|
||||
|
||||
#include "../../x86-qemu.h"
|
||||
|
||||
#define ID (0x0020/4) // ID
|
||||
#define VER (0x0030/4) // Version
|
||||
#define TPR (0x0080/4) // Task Priority
|
||||
#define EOI (0x00B0/4) // EOI
|
||||
#define SVR (0x00F0/4) // Spurious Interrupt Vector
|
||||
#define ENABLE 0x00000100 // Unit Enable
|
||||
#define ESR (0x0280/4) // Error Status
|
||||
#define ICRLO (0x0300/4) // Interrupt Command
|
||||
#define INIT 0x00000500 // INIT/RESET
|
||||
#define STARTUP 0x00000600 // Startup IPI
|
||||
#define DELIVS 0x00001000 // Delivery status
|
||||
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
|
||||
#define DEASSERT 0x00000000
|
||||
#define LEVEL 0x00008000 // Level triggered
|
||||
#define BCAST 0x00080000 // Send to all APICs, including self.
|
||||
#define BUSY 0x00001000
|
||||
#define FIXED 0x00000000
|
||||
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
|
||||
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
|
||||
#define X1 0x0000000B // divide counts by 1
|
||||
#define PERIODIC 0x00020000 // Periodic
|
||||
#define PCINT (0x0340/4) // Performance Counter LVT
|
||||
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
|
||||
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
|
||||
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
|
||||
#define MASKED 0x00010000 // Interrupt masked
|
||||
#define TICR (0x0380/4) // Timer Initial Count
|
||||
#define TCCR (0x0390/4) // Timer Current Count
|
||||
#define TDCR (0x03E0/4) // Timer Divide Configuration
|
||||
|
||||
#define IOAPIC_ADDR 0xFEC00000 // Default physical address of IO APIC
|
||||
#define REG_ID 0x00 // Register index: ID
|
||||
#define REG_VER 0x01 // Register index: version
|
||||
#define REG_TABLE 0x10 // Redirection table base
|
||||
|
||||
#define INT_DISABLED 0x00010000 // Interrupt disabled
|
||||
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
|
||||
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
|
||||
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
|
||||
|
||||
volatile unsigned int * __am_lapic = NULL; // Initialized in mp.c
|
||||
struct IOAPIC {
|
||||
uint32_t reg, pad[3], data;
|
||||
};
|
||||
typedef struct IOAPIC IOAPIC;
|
||||
|
||||
static volatile IOAPIC *ioapic;
|
||||
|
||||
static void lapicw(int index, int value) {
|
||||
__am_lapic[index] = value;
|
||||
__am_lapic[ID];
|
||||
}
|
||||
|
||||
void __am_percpu_initlapic(void) {
|
||||
lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
|
||||
lapicw(TDCR, X1);
|
||||
lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
|
||||
lapicw(TICR, 10000000);
|
||||
lapicw(LINT0, MASKED);
|
||||
lapicw(LINT1, MASKED);
|
||||
if(((__am_lapic[VER]>>16) & 0xFF) >= 4)
|
||||
lapicw(PCINT, MASKED);
|
||||
lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
|
||||
lapicw(ESR, 0);
|
||||
lapicw(ESR, 0);
|
||||
lapicw(EOI, 0);
|
||||
lapicw(ICRHI, 0);
|
||||
lapicw(ICRLO, BCAST | INIT | LEVEL);
|
||||
while(__am_lapic[ICRLO] & DELIVS) ;
|
||||
lapicw(TPR, 0);
|
||||
}
|
||||
|
||||
// Acknowledge interrupt.
|
||||
void __am_lapic_eoi(void) {
|
||||
if(__am_lapic)
|
||||
lapicw(EOI, 0);
|
||||
}
|
||||
|
||||
void __am_lapic_bootap(unsigned int apicid, unsigned int addr) {
|
||||
int i;
|
||||
unsigned short *wrv;
|
||||
outb(0x70, 0xF);
|
||||
outb(0x71, 0x0A);
|
||||
wrv = (unsigned short*)((0x40<<4 | 0x67));
|
||||
wrv[0] = 0;
|
||||
wrv[1] = addr >> 4;
|
||||
|
||||
lapicw(ICRHI, apicid<<24);
|
||||
lapicw(ICRLO, INIT | LEVEL | ASSERT);
|
||||
lapicw(ICRLO, INIT | LEVEL);
|
||||
|
||||
for(i = 0; i < 2; i++){
|
||||
lapicw(ICRHI, apicid<<24);
|
||||
lapicw(ICRLO, STARTUP | (addr>>12));
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int ioapicread(int reg) {
|
||||
ioapic->reg = reg;
|
||||
return ioapic->data;
|
||||
}
|
||||
|
||||
static void ioapicwrite(int reg, unsigned int data) {
|
||||
ioapic->reg = reg;
|
||||
ioapic->data = data;
|
||||
}
|
||||
|
||||
void __am_ioapic_init(void) {
|
||||
int i, maxintr;
|
||||
|
||||
ioapic = (volatile IOAPIC*)IOAPIC_ADDR;
|
||||
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
|
||||
|
||||
for(i = 0; i <= maxintr; i++){
|
||||
ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i));
|
||||
ioapicwrite(REG_TABLE+2*i+1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void __am_ioapic_enable(int irq, int cpunum) {
|
||||
ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq);
|
||||
ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
#include "../../x86-qemu.h"
|
||||
|
||||
void __am_bootcpu_init() {
|
||||
#define MAGIC 0x5f504d5f
|
||||
for (char *st = (char *)0xf0000; st != (char *)0xffffff; st ++) {
|
||||
if (*(volatile uint32_t *)st == MAGIC) {
|
||||
volatile MPConf *conf = ((volatile MPDesc *)st)->conf;
|
||||
__am_lapic = conf->lapicaddr;
|
||||
for (volatile char *ptr = (char *)(conf + 1);
|
||||
ptr < (char *)conf + conf->length; ptr += 8) {
|
||||
if (*ptr == '\0') {
|
||||
ptr += 12;
|
||||
if (++__am_ncpu > MAX_CPU) {
|
||||
panic("cannot support > MAX_CPU processors");
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic("seems not an x86-qemu virtual machine");
|
||||
}
|
||||
|
||||
void __am_percpu_initgdt() {
|
||||
SegDesc *gdt = CPU->gdt;
|
||||
TSS *tss = &CPU->tss;
|
||||
gdt[SEG_KCODE] = SEG (STA_X | STA_R, 0, 0xffffffff, DPL_KERN);
|
||||
gdt[SEG_KDATA] = SEG (STA_W, 0, 0xffffffff, DPL_KERN);
|
||||
gdt[SEG_UCODE] = SEG (STA_X | STA_R, 0, 0xffffffff, DPL_USER);
|
||||
gdt[SEG_UDATA] = SEG (STA_W, 0, 0xffffffff, DPL_USER);
|
||||
gdt[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, DPL_KERN);
|
||||
set_gdt(gdt, sizeof(SegDesc) * NR_SEG);
|
||||
set_tr(KSEL(SEG_TSS));
|
||||
}
|
||||
|
||||
void __am_thiscpu_setstk0(uintptr_t ss0, uintptr_t esp0) {
|
||||
CPU->tss.ss0 = ss0;
|
||||
CPU->tss.esp0 = esp0;
|
||||
}
|
||||
|
||||
void __am_thiscpu_halt() {
|
||||
cli();
|
||||
while (1) hlt();
|
||||
}
|
||||
|
||||
void __am_othercpu_halt() {
|
||||
BOOTREC->is_ap = 1;
|
||||
BOOTREC->entry = __am_thiscpu_halt;
|
||||
for (int cpu = 0; cpu < __am_ncpu; cpu++) {
|
||||
if (cpu != _cpu()) {
|
||||
__am_lapic_bootap(cpu, 0x7c00);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
#include "../../x86-qemu.h"
|
||||
|
||||
static int scan_code[] = {
|
||||
0, 1, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
|
||||
41, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 43,
|
||||
58, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 28,
|
||||
42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
||||
29, 91, 56, 57, 56, 29,
|
||||
72, 80, 75, 77, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
size_t __am_input_read(uintptr_t reg, void *buf, size_t size) {
|
||||
_DEV_INPUT_KBD_t *kbd = (_DEV_INPUT_KBD_t *)buf;
|
||||
|
||||
int status = inb(0x64);
|
||||
kbd->keydown = 0;
|
||||
kbd->keycode = _KEY_NONE;
|
||||
|
||||
if ((status & 0x1) == 0) {
|
||||
} else {
|
||||
if (status & 0x20) { // mouse
|
||||
} else {
|
||||
int code = inb(0x60) & 0xff;
|
||||
|
||||
for (int i = 0; i < sizeof(scan_code) / sizeof(scan_code[0]); i ++) {
|
||||
if (scan_code[i] == 0) continue;
|
||||
if (scan_code[i] == code) {
|
||||
kbd->keydown = 1;
|
||||
kbd->keycode = i;
|
||||
break;
|
||||
} else if (scan_code[i] + 128 == code) {
|
||||
kbd->keydown = 0;
|
||||
kbd->keycode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sizeof(*kbd);
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
#include "../../x86-qemu.h"
|
||||
|
||||
static inline uintptr_t port_read(int port, size_t nmemb) {
|
||||
switch (nmemb) {
|
||||
case 1: return inb(port);
|
||||
case 2: return inw(port);
|
||||
case 4: return inl(port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void port_write(int port, size_t nmemb, uintptr_t data) {
|
||||
switch (nmemb) {
|
||||
case 1: return outb(port, data);
|
||||
case 2: return outw(port, data);
|
||||
case 4: return outl(port, data);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __am_pciconf_read(uintptr_t reg, void *buf, size_t size) {
|
||||
outl(0xcf8, reg);
|
||||
switch (size) {
|
||||
case 1: *(uint8_t *) buf = inb(0xcfc + (reg & 3));
|
||||
case 2: *(uint16_t *)buf = inw(0xcfc + (reg & 2));
|
||||
case 4: *(uint32_t *)buf = inl(0xcfc);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t __am_pciconf_write(uintptr_t reg, void *buf, size_t size) {
|
||||
outl(0xcf8, reg);
|
||||
switch (size) {
|
||||
case 1: outb(0xcfc + (reg & 3), *(uint8_t *) buf);
|
||||
case 2: outw(0xcfc + (reg & 2), *(uint16_t *)buf);
|
||||
case 4: outl(0xcfc , *(uint32_t *)buf);
|
||||
}
|
||||
return size;
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
#include "../../x86-qemu.h"
|
||||
|
||||
static _DEV_TIMER_DATE_t boot_date;
|
||||
static uint32_t freq_mhz = 2000;
|
||||
static uint64_t uptsc;
|
||||
|
||||
static inline uint64_t rdtsc() {
|
||||
uint32_t lo, hi;
|
||||
asm volatile ("rdtsc": "=a"(lo), "=d"(hi));
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
}
|
||||
|
||||
static int read_rtc(int reg) {
|
||||
outb(0x70, reg);
|
||||
int ret = inb(0x71);
|
||||
return (ret & 0xf) + (ret >> 4) * 10;
|
||||
}
|
||||
|
||||
static void wait_sec() {
|
||||
while (1) {
|
||||
int volatile s1 = read_rtc(0);
|
||||
for (int volatile i = 0; i < 10000; i++) ;
|
||||
int volatile s2 = read_rtc(0);
|
||||
if (s1 != s2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t estimate_freq() {
|
||||
int h, m, s, t1, t2;
|
||||
uint64_t tsc1, tsc2;
|
||||
wait_sec();
|
||||
tsc1 = rdtsc();
|
||||
h = read_rtc(4); m = read_rtc(2); s = read_rtc(0);
|
||||
t1 = h * 3600 + m * 60 + s;
|
||||
wait_sec();
|
||||
tsc2 = rdtsc();
|
||||
h = read_rtc(4); m = read_rtc(2); s = read_rtc(0);
|
||||
t2 = h * 3600 + m * 60 + s;
|
||||
if (t1 >= t2) return 0; // passed a day, unlikely to happen
|
||||
|
||||
uint32_t freq = (tsc2 - tsc1) >> 20;
|
||||
freq /= (t2 - t1);
|
||||
return freq;
|
||||
}
|
||||
|
||||
static void get_date(_DEV_TIMER_DATE_t *rtc) {
|
||||
int tmp;
|
||||
do {
|
||||
rtc->second = read_rtc(0);
|
||||
rtc->minute = read_rtc(2);
|
||||
rtc->hour = read_rtc(4);
|
||||
rtc->day = read_rtc(7);
|
||||
rtc->month = read_rtc(8);
|
||||
rtc->year = read_rtc(9) + 2000;
|
||||
tmp = read_rtc(0);
|
||||
} while (tmp != rtc->second);
|
||||
}
|
||||
|
||||
void __am_timer_init() {
|
||||
freq_mhz = estimate_freq();
|
||||
get_date(&boot_date);
|
||||
uptsc = rdtsc();
|
||||
}
|
||||
|
||||
size_t __am_timer_read(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_TIMER_UPTIME: {
|
||||
uint64_t tsc = rdtsc() - uptsc;
|
||||
uint32_t mticks = (tsc >> 20);
|
||||
uint32_t ms = mticks * 1000 / freq_mhz;
|
||||
_DEV_TIMER_UPTIME_t *uptime = (_DEV_TIMER_UPTIME_t *)buf;
|
||||
uptime->hi = 0;
|
||||
uptime->lo = ms;
|
||||
return sizeof(_DEV_TIMER_UPTIME_t);
|
||||
}
|
||||
case _DEVREG_TIMER_DATE: {
|
||||
get_date((_DEV_TIMER_DATE_t *)buf);
|
||||
return sizeof(_DEV_TIMER_DATE_t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
#include "../../x86-qemu.h"
|
||||
|
||||
struct VBEInfo {
|
||||
uint16_t attributes;
|
||||
uint8_t window_a;
|
||||
uint8_t window_b;
|
||||
uint16_t granularity;
|
||||
uint16_t window_size;
|
||||
uint16_t segment_a;
|
||||
uint16_t segment_b;
|
||||
uint32_t win_func_ptr;
|
||||
uint16_t pitch;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t w_char;
|
||||
uint8_t y_char;
|
||||
uint8_t planes;
|
||||
uint8_t bpp;
|
||||
uint8_t banks;
|
||||
uint8_t memory_model;
|
||||
uint8_t bank_size;
|
||||
uint8_t image_pages;
|
||||
uint8_t reserved0;
|
||||
|
||||
uint8_t red_mask;
|
||||
uint8_t red_position;
|
||||
uint8_t green_mask;
|
||||
uint8_t green_position;
|
||||
uint8_t blue_mask;
|
||||
uint8_t blue_position;
|
||||
uint8_t reserved_mask;
|
||||
uint8_t reserved_position;
|
||||
uint8_t direct_color_attributes;
|
||||
|
||||
uint32_t framebuffer;
|
||||
uint32_t off_screen_mem_off;
|
||||
uint16_t off_screen_mem_size;
|
||||
uint8_t reserved1[206];
|
||||
} __attribute__ ((packed));
|
||||
typedef struct VBEInfo VBEInfo;
|
||||
|
||||
static inline uint32_t pixel(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
static uint8_t R(uint32_t p) { return p >> 16; }
|
||||
static uint8_t G(uint32_t p) { return p >> 8; }
|
||||
static uint8_t B(uint32_t p) { return p; }
|
||||
|
||||
static struct FBPixel {
|
||||
uint8_t b, g, r;
|
||||
} __attribute__ ((packed)) *fb;
|
||||
typedef struct FBPixel FBPixel;
|
||||
static int W, H;
|
||||
|
||||
void __am_vga_init() {
|
||||
VBEInfo *info = (VBEInfo *)0x00004000;
|
||||
W = info->width;
|
||||
H = info->height;
|
||||
fb = (FBPixel*)(info->framebuffer);
|
||||
}
|
||||
|
||||
size_t __am_video_read(uintptr_t reg, void *buf, size_t size) {
|
||||
switch(reg) {
|
||||
case _DEVREG_VIDEO_INFO: {
|
||||
_DEV_VIDEO_INFO_t *info = (_DEV_VIDEO_INFO_t *)buf;
|
||||
info->width = W;
|
||||
info->height = H;
|
||||
return sizeof(_DEV_VIDEO_INFO_t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t __am_video_write(uintptr_t reg, void *buf, size_t size) {
|
||||
switch(reg) {
|
||||
case _DEVREG_VIDEO_FBCTRL: {
|
||||
_DEV_VIDEO_FBCTRL_t *ctl = (_DEV_VIDEO_FBCTRL_t *)buf;
|
||||
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
|
||||
uint32_t *pixels = ctl->pixels;
|
||||
int len = (x + w >= W) ? W - x : w;
|
||||
FBPixel *v;
|
||||
for (int j = 0; j < h; j ++) {
|
||||
if (y + j < H) {
|
||||
v = &fb[x + (j + y) * W];
|
||||
for (int i = 0; i < len; i ++, v ++) {
|
||||
uint32_t p = pixels[i];
|
||||
v->r = R(p); v->g = G(p); v->b = B(p);
|
||||
}
|
||||
}
|
||||
pixels += w;
|
||||
}
|
||||
if (ctl->sync) {
|
||||
// do nothing, hardware syncs.
|
||||
}
|
||||
return sizeof(*ctl);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "../x86-qemu.h"
|
||||
#include "x86-qemu.h"
|
||||
#include <amdev.h>
|
||||
|
||||
void __am_vga_init();
|
||||
void __am_timer_init();
|
||||
|
@ -10,15 +11,245 @@ DEF_DEVOP(__am_input_read);
|
|||
DEF_DEVOP(__am_timer_read);
|
||||
DEF_DEVOP(__am_video_read);
|
||||
DEF_DEVOP(__am_video_write);
|
||||
DEF_DEVOP(__am_pciconf_read);
|
||||
DEF_DEVOP(__am_pciconf_write);
|
||||
|
||||
|
||||
// AM INPUT (keyboard)
|
||||
|
||||
static int scan_code[] = {
|
||||
0, 1, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
|
||||
41, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 43,
|
||||
58, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 28,
|
||||
42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
||||
29, 91, 56, 57, 56, 29,
|
||||
72, 80, 75, 77
|
||||
};
|
||||
|
||||
size_t __am_input_read(uintptr_t reg, void *buf, size_t size) {
|
||||
_DEV_INPUT_KBD_t *kbd = (_DEV_INPUT_KBD_t *)buf;
|
||||
|
||||
int status = inb(0x64);
|
||||
kbd->keydown = 0;
|
||||
kbd->keycode = _KEY_NONE;
|
||||
|
||||
if ((status & 0x1) == 0) {
|
||||
} else {
|
||||
if (status & 0x20) { // mouse
|
||||
} else {
|
||||
int code = inb(0x60) & 0xff;
|
||||
|
||||
for (int i = 0; i < LENGTH(scan_code); i ++) {
|
||||
if (scan_code[i] == 0) continue;
|
||||
if (scan_code[i] == code) {
|
||||
kbd->keydown = 1;
|
||||
kbd->keycode = i;
|
||||
break;
|
||||
} else if (scan_code[i] + 128 == code) {
|
||||
kbd->keydown = 0;
|
||||
kbd->keycode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sizeof(*kbd);
|
||||
}
|
||||
|
||||
// AM TIMER (based on rdtsc)
|
||||
|
||||
static _DEV_TIMER_DATE_t boot_date;
|
||||
static uint32_t freq_mhz = 2000;
|
||||
static uint64_t uptsc;
|
||||
|
||||
static inline int read_rtc(int reg) {
|
||||
outb(0x70, reg);
|
||||
int ret = inb(0x71);
|
||||
return (ret & 0xf) + (ret >> 4) * 10;
|
||||
}
|
||||
|
||||
static void read_rtc_async(_DEV_TIMER_DATE_t *rtc) {
|
||||
rtc->second = read_rtc(0);
|
||||
rtc->minute = read_rtc(2);
|
||||
rtc->hour = read_rtc(4);
|
||||
rtc->day = read_rtc(7);
|
||||
rtc->month = read_rtc(8);
|
||||
rtc->year = read_rtc(9) + 2000;
|
||||
}
|
||||
|
||||
static void wait_sec(_DEV_TIMER_DATE_t *t1) {
|
||||
_DEV_TIMER_DATE_t t0;
|
||||
while (1) {
|
||||
read_rtc_async(&t0);
|
||||
for (int volatile i = 0; i < 100000; i++) ;
|
||||
read_rtc_async(t1);
|
||||
if (t0.second != t1->second) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t estimate_freq() {
|
||||
_DEV_TIMER_DATE_t rtc1, rtc2;
|
||||
uint64_t tsc1, tsc2, t1, t2;
|
||||
wait_sec(&rtc1); tsc1 = rdtsc(); t1 = rtc1.minute * 60 + rtc1.second;
|
||||
wait_sec(&rtc2); tsc2 = rdtsc(); t2 = rtc2.minute * 60 + rtc2.second;
|
||||
if (t1 >= t2) return estimate_freq(); // passed an hour; try again
|
||||
return ((tsc2 - tsc1) >> 20) / (t2 - t1);
|
||||
}
|
||||
|
||||
static void get_date(_DEV_TIMER_DATE_t *rtc) {
|
||||
int tmp;
|
||||
do {
|
||||
read_rtc_async(rtc);
|
||||
tmp = read_rtc(0);
|
||||
} while (tmp != rtc->second);
|
||||
}
|
||||
|
||||
void __am_timer_init() {
|
||||
freq_mhz = estimate_freq();
|
||||
get_date(&boot_date);
|
||||
uptsc = rdtsc();
|
||||
}
|
||||
|
||||
size_t __am_timer_read(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_TIMER_UPTIME: {
|
||||
_DEV_TIMER_UPTIME_t *uptime = (_DEV_TIMER_UPTIME_t *)buf;
|
||||
uint64_t tsc = rdtsc() - uptsc;
|
||||
uint32_t mticks = (tsc >> 20);
|
||||
uint32_t ms = mticks * 1000 / freq_mhz;
|
||||
uptime->hi = 0;
|
||||
uptime->lo = ms;
|
||||
return sizeof(_DEV_TIMER_UPTIME_t);
|
||||
}
|
||||
case _DEVREG_TIMER_DATE: {
|
||||
get_date((_DEV_TIMER_DATE_t *)buf);
|
||||
return sizeof(_DEV_TIMER_DATE_t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// AM VIDEO
|
||||
|
||||
struct vbe_info {
|
||||
uint8_t ignore[18];
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t ignore1[18];
|
||||
uint32_t framebuffer;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static inline uint32_t pixel(uint8_t r, uint8_t g, uint8_t b) { return (r << 16) | (g << 8) | b; }
|
||||
static inline uint8_t R(uint32_t p) { return p >> 16; }
|
||||
static inline uint8_t G(uint32_t p) { return p >> 8; }
|
||||
static inline uint8_t B(uint32_t p) { return p; }
|
||||
|
||||
struct pixel {
|
||||
uint8_t b, g, r;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pixel *fb;
|
||||
static int W, H;
|
||||
|
||||
void __am_vga_init() {
|
||||
struct vbe_info *info = (struct vbe_info *)0x00004000;
|
||||
W = info->width;
|
||||
H = info->height;
|
||||
fb = upcast(info->framebuffer);
|
||||
}
|
||||
|
||||
size_t __am_video_read(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_VIDEO_INFO: {
|
||||
_DEV_VIDEO_INFO_t *info = (_DEV_VIDEO_INFO_t *)buf;
|
||||
info->width = W;
|
||||
info->height = H;
|
||||
return sizeof(_DEV_VIDEO_INFO_t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t __am_video_write(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_VIDEO_FBCTRL: {
|
||||
_DEV_VIDEO_FBCTRL_t *ctl = (_DEV_VIDEO_FBCTRL_t *)buf;
|
||||
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
|
||||
uint32_t *pixels = ctl->pixels;
|
||||
int len = (x + w >= W) ? W - x : w;
|
||||
for (int j = 0; j < h; j ++, pixels += w) {
|
||||
if (y + j < H) {
|
||||
struct pixel *px = &fb[x + (j + y) * W];
|
||||
for (int i = 0; i < len; i ++, px ++) {
|
||||
uint32_t p = pixels[i];
|
||||
px->r = R(p); px->g = G(p); px->b = B(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sizeof(*ctl);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// AM STORAGE
|
||||
|
||||
size_t __am_storage_read(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_STORAGE_INFO: {
|
||||
_DEV_STORAGE_INFO_t *info = (void *)buf;
|
||||
info->blksz = 512;
|
||||
info->blkcnt = 524288;
|
||||
return sizeof(*info);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void wait_disk(void) {
|
||||
while ((inb(0x1f7) & 0xc0) != 0x40);
|
||||
}
|
||||
|
||||
size_t __am_storage_write(uintptr_t reg, void *buf, size_t size) {
|
||||
_DEV_STORAGE_RDCTRL_t *ctl = (void *)buf;
|
||||
int is_read = 0;
|
||||
switch (reg) {
|
||||
case _DEVREG_STORAGE_RDCTRL: is_read = 1; break;
|
||||
case _DEVREG_STORAGE_WRCTRL: break;
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
uint32_t blkno = ctl->blkno, remain = ctl->blkcnt;
|
||||
uint32_t *ptr = ctl->buf;
|
||||
for (remain = ctl->blkcnt; remain; remain--, blkno++) {
|
||||
wait_disk();
|
||||
outb(0x1f2, 1);
|
||||
outb(0x1f3, blkno);
|
||||
outb(0x1f4, blkno >> 8);
|
||||
outb(0x1f5, blkno >> 16);
|
||||
outb(0x1f6, (blkno >> 24) | 0xe0);
|
||||
outb(0x1f7, is_read ? 0x20 : 0x30);
|
||||
wait_disk();
|
||||
if (is_read) {
|
||||
for (int i = 0; i < 512 / 4; i ++) {
|
||||
*ptr++ = inl(0x1f0);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 512 / 4; i ++) {
|
||||
outl(0x1f0, *ptr++);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sizeof(*ctl);
|
||||
}
|
||||
|
||||
size_t _io_read(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
|
||||
switch (dev) {
|
||||
case _DEV_INPUT: return __am_input_read(reg, buf, size);
|
||||
case _DEV_TIMER: return __am_timer_read(reg, buf, size);
|
||||
case _DEV_VIDEO: return __am_video_read(reg, buf, size);
|
||||
case _DEV_PCICONF: return __am_pciconf_read(reg, buf, size);
|
||||
case _DEV_STORAGE: return __am_storage_read(reg, buf, size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -26,14 +257,139 @@ size_t _io_read(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
|
|||
size_t _io_write(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
|
||||
switch (dev) {
|
||||
case _DEV_VIDEO: return __am_video_write(reg, buf, size);
|
||||
case _DEV_PCICONF: return __am_pciconf_write(reg, buf, size);
|
||||
case _DEV_STORAGE: return __am_storage_write(reg, buf, size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _ioe_init() {
|
||||
if (_cpu() != 0) panic("init IOE in non-bootstrap CPU");
|
||||
panic_on(_cpu() != 0, "init IOE in non-bootstrap CPU");
|
||||
__am_timer_init();
|
||||
__am_vga_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// LAPIC/IOAPIC (from xv6)
|
||||
|
||||
#define ID (0x0020/4) // ID
|
||||
#define VER (0x0030/4) // Version
|
||||
#define TPR (0x0080/4) // Task Priority
|
||||
#define EOI (0x00B0/4) // EOI
|
||||
#define SVR (0x00F0/4) // Spurious Interrupt Vector
|
||||
#define ENABLE 0x00000100 // Unit Enable
|
||||
#define ESR (0x0280/4) // Error Status
|
||||
#define ICRLO (0x0300/4) // Interrupt Command
|
||||
#define INIT 0x00000500 // INIT/RESET
|
||||
#define STARTUP 0x00000600 // Startup IPI
|
||||
#define DELIVS 0x00001000 // Delivery status
|
||||
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
|
||||
#define DEASSERT 0x00000000
|
||||
#define LEVEL 0x00008000 // Level triggered
|
||||
#define BCAST 0x00080000 // Send to all APICs, including self.
|
||||
#define BUSY 0x00001000
|
||||
#define FIXED 0x00000000
|
||||
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
|
||||
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
|
||||
#define X1 0x0000000B // divide counts by 1
|
||||
#define PERIODIC 0x00020000 // Periodic
|
||||
#define PCINT (0x0340/4) // Performance Counter LVT
|
||||
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
|
||||
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
|
||||
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
|
||||
#define MASKED 0x00010000 // Interrupt masked
|
||||
#define TICR (0x0380/4) // Timer Initial Count
|
||||
#define TCCR (0x0390/4) // Timer Current Count
|
||||
#define TDCR (0x03E0/4) // Timer Divide Configuration
|
||||
|
||||
#define IOAPIC_ADDR 0xFEC00000 // Default physical address of IO APIC
|
||||
#define REG_ID 0x00 // Register index: ID
|
||||
#define REG_VER 0x01 // Register index: version
|
||||
#define REG_TABLE 0x10 // Redirection table base
|
||||
|
||||
#define INT_DISABLED 0x00010000 // Interrupt disabled
|
||||
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
|
||||
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
|
||||
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
|
||||
|
||||
volatile unsigned int *__am_lapic = NULL; // Initialized in mp.c
|
||||
struct IOAPIC {
|
||||
uint32_t reg, pad[3], data;
|
||||
} __attribute__((packed));
|
||||
typedef struct IOAPIC IOAPIC;
|
||||
|
||||
static volatile IOAPIC *ioapic;
|
||||
|
||||
static void lapicw(int index, int value) {
|
||||
__am_lapic[index] = value;
|
||||
__am_lapic[ID];
|
||||
}
|
||||
|
||||
void __am_percpu_initlapic(void) {
|
||||
lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
|
||||
lapicw(TDCR, X1);
|
||||
lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
|
||||
lapicw(TICR, 10000000);
|
||||
lapicw(LINT0, MASKED);
|
||||
lapicw(LINT1, MASKED);
|
||||
if (((__am_lapic[VER]>>16) & 0xFF) >= 4)
|
||||
lapicw(PCINT, MASKED);
|
||||
lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
|
||||
lapicw(ESR, 0);
|
||||
lapicw(ESR, 0);
|
||||
lapicw(EOI, 0);
|
||||
lapicw(ICRHI, 0);
|
||||
lapicw(ICRLO, BCAST | INIT | LEVEL);
|
||||
while(__am_lapic[ICRLO] & DELIVS) ;
|
||||
lapicw(TPR, 0);
|
||||
}
|
||||
|
||||
void __am_lapic_eoi(void) {
|
||||
if (__am_lapic)
|
||||
lapicw(EOI, 0);
|
||||
}
|
||||
|
||||
void __am_lapic_bootap(uint32_t apicid, void *addr) {
|
||||
int i;
|
||||
uint16_t *wrv;
|
||||
outb(0x70, 0xF);
|
||||
outb(0x71, 0x0A);
|
||||
wrv = (unsigned short*)((0x40<<4 | 0x67));
|
||||
wrv[0] = 0;
|
||||
wrv[1] = (uintptr_t)addr >> 4;
|
||||
|
||||
lapicw(ICRHI, apicid<<24);
|
||||
lapicw(ICRLO, INIT | LEVEL | ASSERT);
|
||||
lapicw(ICRLO, INIT | LEVEL);
|
||||
|
||||
for (i = 0; i < 2; i++){
|
||||
lapicw(ICRHI, apicid<<24);
|
||||
lapicw(ICRLO, STARTUP | ((uintptr_t)addr>>12));
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int ioapicread(int reg) {
|
||||
ioapic->reg = reg;
|
||||
return ioapic->data;
|
||||
}
|
||||
|
||||
static void ioapicwrite(int reg, unsigned int data) {
|
||||
ioapic->reg = reg;
|
||||
ioapic->data = data;
|
||||
}
|
||||
|
||||
void __am_ioapic_init(void) {
|
||||
int i, maxintr;
|
||||
|
||||
ioapic = (volatile IOAPIC*)IOAPIC_ADDR;
|
||||
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
|
||||
|
||||
for (i = 0; i <= maxintr; i++){
|
||||
ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i));
|
||||
ioapicwrite(REG_TABLE+2*i+1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void __am_ioapic_enable(int irq, int cpunum) {
|
||||
ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq);
|
||||
ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24);
|
||||
}
|
||||
|
|
|
@ -1,20 +1,36 @@
|
|||
#include "../x86-qemu.h"
|
||||
#include "x86-qemu.h"
|
||||
|
||||
int __am_ncpu = 0;
|
||||
struct cpu_local __am_cpuinfo[MAX_CPU] = {};
|
||||
|
||||
static void (* volatile user_entry)();
|
||||
static volatile intptr_t apboot_done = 0;
|
||||
static volatile intptr_t ap_ready = 0;
|
||||
|
||||
static void percpu_entry();
|
||||
static void ap_entry();
|
||||
static void jump_to(void (*entry)());
|
||||
static void call_user_entry() {
|
||||
user_entry();
|
||||
panic("MPE entry should not return");
|
||||
}
|
||||
|
||||
int _mpe_init(void (*entry)()) {
|
||||
user_entry = entry;
|
||||
jump_to(percpu_entry);
|
||||
panic("bug: should not return");
|
||||
return 1;
|
||||
boot_record()->jmp_code = 0x000bfde9; // (16-bit) jmp (0x7c00)
|
||||
for (int cpu = 1; cpu < __am_ncpu; cpu++) {
|
||||
boot_record()->is_ap = 1;
|
||||
__am_lapic_bootap(cpu, (void *)boot_record());
|
||||
while (_atomic_xchg(&ap_ready, 0) != 1) {
|
||||
pause();
|
||||
}
|
||||
}
|
||||
call_user_entry();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void othercpu_entry() {
|
||||
__am_percpu_init();
|
||||
_atomic_xchg(&ap_ready, 1);
|
||||
call_user_entry();
|
||||
}
|
||||
|
||||
void __am_othercpu_entry() {
|
||||
stack_switch_call(stack_top(&CPU->stack), othercpu_entry, 0);
|
||||
}
|
||||
|
||||
int _ncpu() {
|
||||
|
@ -26,41 +42,14 @@ int _cpu(void) {
|
|||
}
|
||||
|
||||
intptr_t _atomic_xchg(volatile intptr_t *addr, intptr_t newval) {
|
||||
intptr_t result;
|
||||
asm volatile ("lock xchgl %0, %1":
|
||||
"+m"(*addr), "=a"(result) : "1"(newval) : "cc");
|
||||
return result;
|
||||
return xchg(addr, newval);
|
||||
}
|
||||
|
||||
static void percpu_entry() {
|
||||
if (_cpu() == 0) { // bootstrap cpu, boot all aps
|
||||
for (int cpu = 1; cpu < __am_ncpu; cpu++) {
|
||||
BOOTREC->is_ap = 1;
|
||||
BOOTREC->entry = percpu_entry;
|
||||
__am_lapic_bootap(cpu, 0x7000);
|
||||
while (_atomic_xchg(&apboot_done, 0) != 1) {
|
||||
pause();
|
||||
}
|
||||
void __am_stop_the_world() {
|
||||
boot_record()->jmp_code = 0x0000feeb; // (16-bit) jmp .
|
||||
for (int cpu = 0; cpu < __am_ncpu; cpu++) {
|
||||
if (cpu != _cpu()) {
|
||||
__am_lapic_bootap(cpu, (void *)boot_record());
|
||||
}
|
||||
user_entry();
|
||||
} else { // this is an ap
|
||||
jump_to(ap_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void ap_entry() {
|
||||
__am_percpu_initgdt();
|
||||
__am_percpu_initirq();
|
||||
__am_percpu_initlapic();
|
||||
__am_percpu_initpg();
|
||||
_atomic_xchg(&apboot_done, 1);
|
||||
user_entry();
|
||||
}
|
||||
|
||||
static void jump_to(void (*entry)()) {
|
||||
void *esp = CPU->stack + sizeof(CPU->stack);
|
||||
asm volatile (
|
||||
"movl %0, %%esp;"
|
||||
"call *%1"
|
||||
: : "r"(esp) , "r"(entry));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include <x86.h>
|
||||
#include "x86_64-qemu.h"
|
||||
#include "x86-qemu.h"
|
||||
|
||||
.globl _start
|
||||
_start:
|
|
@ -1,5 +1,5 @@
|
|||
#include <x86.h>
|
||||
#include "x86_64-qemu.h"
|
||||
#include "x86-qemu.h"
|
||||
|
||||
.code32
|
||||
.globl _start
|
|
@ -1,4 +1,4 @@
|
|||
#include "x86_64-qemu.h"
|
||||
#include "x86-qemu.h"
|
||||
|
||||
trap:
|
||||
cli
|
|
@ -1,4 +1,4 @@
|
|||
#include <x86.h>
|
||||
#include "x86-qemu.h"
|
||||
|
||||
trap:
|
||||
cli
|
||||
|
@ -18,7 +18,7 @@ trap:
|
|||
pushq %rcx
|
||||
pushq %rbx
|
||||
pushq %rax
|
||||
pushq $0 //uvm
|
||||
pushq $0 // uvm, saved in __am_irq_handle
|
||||
|
||||
movq %rsp, %rdi
|
||||
call __am_irq_handle
|
|
@ -1,44 +1,120 @@
|
|||
#include "../x86-qemu.h"
|
||||
#include "x86-qemu.h"
|
||||
|
||||
_Area _heap = {}; // the heap memory defined in AM spec
|
||||
_Area _heap = {};
|
||||
volatile uint32_t *__am_lapic;
|
||||
int __am_ncpu = 0;
|
||||
struct cpu_local __am_cpuinfo[MAX_CPU];
|
||||
|
||||
int main(const char *args);
|
||||
static void heap_init();
|
||||
|
||||
void _start(const char *args) { // the bootloader jumps here
|
||||
__am_bootcpu_init();
|
||||
heap_init();
|
||||
__am_percpu_initgdt();
|
||||
__am_percpu_initlapic();
|
||||
__am_ioapic_init();
|
||||
static void call_main(const char *args) {
|
||||
_halt(main(args));
|
||||
}
|
||||
|
||||
void _putc(char ch) { // only works for x86-qemu
|
||||
void _start_c(char *args) {
|
||||
if (boot_record()->is_ap) {
|
||||
__am_othercpu_entry();
|
||||
} else {
|
||||
__am_bootcpu_init();
|
||||
stack_switch_call(stack_top(&CPU->stack), call_main, (uintptr_t)args);
|
||||
}
|
||||
}
|
||||
|
||||
void __am_bootcpu_init() {
|
||||
_heap = __am_heap_init();
|
||||
__am_lapic_init();
|
||||
__am_ioapic_init();
|
||||
__am_percpu_init();
|
||||
}
|
||||
|
||||
void __am_percpu_init() {
|
||||
__am_percpu_initgdt();
|
||||
__am_percpu_initlapic();
|
||||
__am_percpu_initirq();
|
||||
}
|
||||
|
||||
void _putc(char ch) {
|
||||
#define COM1 0x3f8
|
||||
outb(COM1, ch); // first -serial device
|
||||
outb(COM1, ch);
|
||||
}
|
||||
|
||||
void _halt(int code) {
|
||||
const char *hex = "0123456789abcdef";
|
||||
const char *fmt = "CPU #$ Halt (40).\n";
|
||||
cli();
|
||||
__am_othercpu_halt();
|
||||
char buf[] = "Exited (#).\n";
|
||||
for (char *p = buf; *p; p++) {
|
||||
_putc((*p == '#') ? ('0' + code) : *p);
|
||||
__am_stop_the_world();
|
||||
for (const char *p = fmt; *p; p++) {
|
||||
char ch = *p;
|
||||
switch (ch) {
|
||||
case '$':
|
||||
_putc(hex[_cpu()]);
|
||||
break;
|
||||
case '0': case '4':
|
||||
_putc(hex[(code >> (ch - '0')) & 0xf]);
|
||||
break;
|
||||
default:
|
||||
_putc(ch);
|
||||
}
|
||||
}
|
||||
__am_thiscpu_halt();
|
||||
outw(0x604, 0x2000); // offer of qemu :)
|
||||
while (1) hlt();
|
||||
|
||||
}
|
||||
|
||||
static void heap_init() {
|
||||
#define MAGIC 0x5a5aa5a5
|
||||
#define STEP (1L << 20)
|
||||
_Area __am_heap_init() {
|
||||
int32_t magic = 0x5a5aa5a5;
|
||||
int32_t step = 1L << 20;
|
||||
extern char end;
|
||||
uintptr_t st, ed;
|
||||
for (st = ed = ROUNDUP(&end, STEP); ; ed += STEP) {
|
||||
for (st = ed = ROUNDUP(&end, step); ; ed += step) {
|
||||
volatile uint32_t *ptr = (uint32_t *)ed;
|
||||
if ((*ptr = MAGIC, *ptr) != MAGIC) {
|
||||
if ((*ptr = magic, *ptr) != magic) {
|
||||
break; // read-after-write fail
|
||||
}
|
||||
}
|
||||
_heap = RANGE(st, ed);
|
||||
return RANGE(st, ed);
|
||||
}
|
||||
|
||||
void __am_lapic_init() {
|
||||
for (char *st = (char *)0xf0000; st != (char *)0xffffff; st ++) {
|
||||
if (*(volatile uint32_t *)st == 0x5f504d5f) {
|
||||
uint32_t mpconf_ptr = ((volatile MPDesc *)st)->conf;
|
||||
MPConf *conf = upcast(mpconf_ptr);
|
||||
__am_lapic = upcast(conf->lapicaddr);
|
||||
for (volatile char *ptr = (char *)(conf + 1);
|
||||
ptr < (char *)conf + conf->length; ptr += 8) {
|
||||
if (*ptr == '\0') {
|
||||
ptr += 12;
|
||||
panic_on(++__am_ncpu > MAX_CPU, "cannot support > MAX_CPU processors");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
bug();
|
||||
}
|
||||
|
||||
void __am_percpu_initgdt() {
|
||||
#if __x86_64__
|
||||
SegDesc *gdt = CPU->gdt;
|
||||
TSS64 *tss = &CPU->tss;
|
||||
gdt[SEG_KCODE] = SEG64(STA_X | STA_R, DPL_KERN);
|
||||
gdt[SEG_KDATA] = SEG64(STA_W, DPL_KERN);
|
||||
gdt[SEG_UCODE] = SEG64(STA_X | STA_R, DPL_USER);
|
||||
gdt[SEG_UDATA] = SEG64(STA_W, DPL_USER);
|
||||
gdt[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, DPL_KERN);
|
||||
bug_on((uintptr_t)tss >> 32);
|
||||
set_gdt(gdt, sizeof(gdt[0]) * (NR_SEG + 1));
|
||||
set_tr(KSEL(SEG_TSS));
|
||||
#else
|
||||
SegDesc *gdt = CPU->gdt;
|
||||
TSS32 *tss = &CPU->tss;
|
||||
gdt[SEG_KCODE] = SEG32(STA_X | STA_R, 0, 0xffffffff, DPL_KERN);
|
||||
gdt[SEG_KDATA] = SEG32(STA_W, 0, 0xffffffff, DPL_KERN);
|
||||
gdt[SEG_UCODE] = SEG32(STA_X | STA_R, 0, 0xffffffff, DPL_USER);
|
||||
gdt[SEG_UDATA] = SEG32(STA_W, 0, 0xffffffff, DPL_USER);
|
||||
gdt[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, DPL_KERN);
|
||||
set_gdt(gdt, sizeof(gdt[0]) * NR_SEG);
|
||||
set_tr(KSEL(SEG_TSS));
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,135 +1,184 @@
|
|||
#include "../x86-qemu.h"
|
||||
#include "x86-qemu.h"
|
||||
|
||||
struct vm_area {
|
||||
_Area area;
|
||||
int physical;
|
||||
const struct mmu_config mmu = {
|
||||
.pgsize = 4096,
|
||||
#if __x86_64__
|
||||
.ptlevels = 4,
|
||||
.pgtables = {
|
||||
{ "CR3", 0x000000000000, 0, 0 },
|
||||
{ "PML4", 0xff8000000000, 39, 9 },
|
||||
{ "PDPT", 0x007fc0000000, 30, 9 },
|
||||
{ "PD", 0x00003fe00000, 21, 9 },
|
||||
{ "PT", 0x0000001ff000, 12, 9 },
|
||||
},
|
||||
#else
|
||||
.ptlevels = 2,
|
||||
.pgtables = {
|
||||
{ "CR3", 0x00000000, 0, 0 },
|
||||
{ "PD", 0xffc00000, 22, 10 },
|
||||
{ "PT", 0x003ff000, 12, 10 },
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct vm_area areas[] = {
|
||||
{ RANGE(0x00000000, 0x20000000), 1 }, // kernel code/data
|
||||
{ RANGE(0x40000000, 0x80000000), 0 }, // protected address space range
|
||||
{ RANGE(0xf0000000, 0x00000000), 1 }, // system mmap area
|
||||
static const struct vm_area vm_areas[] = {
|
||||
#ifdef __x86_64__
|
||||
{ RANGE(0x100000000000, 0x108000000000), 0 }, // 512 GiB user space
|
||||
{ RANGE(0x000000000000, 0x008000000000), 1 }, // 512 GiB kernel
|
||||
#else
|
||||
{ RANGE( 0x40000000, 0x80000000), 0 }, // 1 GiB user space
|
||||
{ RANGE( 0x00000000, 0x40000000), 1 }, // 1 GiB kernel
|
||||
{ RANGE( 0xfd000000, 0x00000000), 1 }, // memory-mapped I/O
|
||||
#endif
|
||||
};
|
||||
#define uvm_area (areas[1].area)
|
||||
#define uvm_area (vm_areas[0].area)
|
||||
|
||||
static void *pgalloc();
|
||||
static void pgfree(void *ptr);
|
||||
static uintptr_t *kpt;
|
||||
static void *(*pgalloc)(size_t size);
|
||||
static void (*pgfree)(void *);
|
||||
|
||||
static PDE *kpt;
|
||||
static void *(*user_pgalloc)(size_t);
|
||||
static void (*user_pgfree)(void *);
|
||||
static void *pgallocz() {
|
||||
uintptr_t *base = pgalloc(mmu.pgsize);
|
||||
panic_on(!base, "cannot allocate page");
|
||||
for (int i = 0; i < mmu.pgsize / sizeof(uintptr_t); i++) {
|
||||
base[i] = 0;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
int _vme_init(void *(*pgalloc_f)(size_t), void (*pgfree_f)(void *)) {
|
||||
if (_cpu() != 0) panic("init VME in non-bootstrap CPU");
|
||||
static int indexof(uintptr_t addr, const struct ptinfo *info) {
|
||||
return ((uintptr_t)addr & info->mask) >> info->shift;
|
||||
}
|
||||
|
||||
user_pgalloc = pgalloc_f;
|
||||
user_pgfree = pgfree_f;
|
||||
static uintptr_t baseof(uintptr_t addr) {
|
||||
return addr & ~(mmu.pgsize - 1);
|
||||
}
|
||||
|
||||
kpt = pgalloc();
|
||||
for (int i = 0; i < LENGTH(areas); i++) {
|
||||
const struct vm_area *seg = &areas[i];
|
||||
if (!seg->physical) continue;
|
||||
for (uint32_t pa = (uint32_t)seg->area.start;
|
||||
pa != (uint32_t)seg->area.end;
|
||||
pa += PGSIZE) {
|
||||
PTE *ptab;
|
||||
if (!(kpt[PDX(pa)] & PTE_P)) {
|
||||
ptab = pgalloc();
|
||||
kpt[PDX(pa)] = PTE_P | PTE_W | (uint32_t)ptab;
|
||||
} else {
|
||||
ptab = (PTE*)PTE_ADDR(kpt[PDX(pa)]);
|
||||
static uintptr_t *ptwalk(_AddressSpace *as, uintptr_t addr, int flags) {
|
||||
uintptr_t cur = (uintptr_t)&as->ptr;
|
||||
|
||||
for (int i = 0; i <= mmu.ptlevels; i++) {
|
||||
const struct ptinfo *ptinfo = &mmu.pgtables[i];
|
||||
uintptr_t *pt = (uintptr_t *)cur, next_page;
|
||||
int index = indexof(addr, ptinfo);
|
||||
if (i == mmu.ptlevels) return &pt[index];
|
||||
|
||||
if (!(pt[index] & PTE_P)) {
|
||||
next_page = (uintptr_t)pgallocz();
|
||||
pt[index] = next_page | PTE_P | flags;
|
||||
} else {
|
||||
next_page = baseof(pt[index]);
|
||||
}
|
||||
cur = next_page;
|
||||
}
|
||||
bug();
|
||||
}
|
||||
|
||||
static void teardown(int level, uintptr_t *pt) {
|
||||
if (level > mmu.ptlevels) return;
|
||||
for (int index = 0; index < (1 << mmu.pgtables[level].bits); index++) {
|
||||
if ((pt[index] & PTE_P) && (pt[index] & PTE_U)) {
|
||||
teardown(level + 1, (void *)baseof(pt[index]));
|
||||
}
|
||||
}
|
||||
if (level >= 1) {
|
||||
pgfree(pt);
|
||||
}
|
||||
}
|
||||
|
||||
int _vme_init(void *(*_pgalloc)(size_t size), void (*_pgfree)(void *)) {
|
||||
panic_on(_cpu() != 0, "init VME in non-bootstrap CPU");
|
||||
pgalloc = _pgalloc;
|
||||
pgfree = _pgfree;
|
||||
|
||||
#if __x86_64__
|
||||
kpt = (void *)PML4_ADDR;
|
||||
#else
|
||||
_AddressSpace as;
|
||||
as.ptr = NULL;
|
||||
for (int i = 0; i < LENGTH(vm_areas); i++) {
|
||||
const struct vm_area *vma = &vm_areas[i];
|
||||
if (vma->kernel) {
|
||||
for (uintptr_t cur = (uintptr_t)vma->area.start;
|
||||
cur != (uintptr_t)vma->area.end;
|
||||
cur += mmu.pgsize) {
|
||||
*ptwalk(&as, cur, PTE_W) = cur | PTE_P | PTE_W;
|
||||
}
|
||||
ptab[PTX(pa)] = PTE_P | PTE_W | pa;
|
||||
}
|
||||
}
|
||||
__am_percpu_initpg(); // set CR3 and CR0 if kpt is not NULL
|
||||
kpt = (void *)baseof((uintptr_t)as.ptr);
|
||||
#endif
|
||||
|
||||
set_cr3(kpt);
|
||||
set_cr0(get_cr0() | CR0_PG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __am_percpu_initpg() { // called by all cpus
|
||||
if (kpt) {
|
||||
set_cr3(kpt);
|
||||
set_cr0(get_cr0() | CR0_PG);
|
||||
}
|
||||
}
|
||||
int _protect(_AddressSpace *as) {
|
||||
uintptr_t *upt = pgallocz();
|
||||
|
||||
int _protect(_AddressSpace *p) {
|
||||
PDE *upt = pgalloc();
|
||||
for (int i = 0; i < PGSIZE / sizeof(PDE *); i++) {
|
||||
upt[i] = kpt[i];
|
||||
for (int i = 0; i < LENGTH(vm_areas); i++) {
|
||||
const struct vm_area *vma = &vm_areas[i];
|
||||
if (vma->kernel) {
|
||||
const struct ptinfo *info = &mmu.pgtables[1]; // level-1 page table
|
||||
for (uintptr_t cur = (uintptr_t)vma->area.start;
|
||||
cur != (uintptr_t)vma->area.end;
|
||||
cur += (1L << info->shift)) {
|
||||
int index = indexof(cur, info);
|
||||
upt[index] = kpt[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
*p = (_AddressSpace) {
|
||||
.pgsize = PGSIZE,
|
||||
.area = uvm_area,
|
||||
.ptr = upt,
|
||||
};
|
||||
|
||||
as->pgsize = mmu.pgsize;
|
||||
as->area = uvm_area;
|
||||
as->ptr = (void *)((uintptr_t)upt | PTE_P | PTE_U);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _unprotect(_AddressSpace *p) {
|
||||
PDE *upt = p->ptr;
|
||||
for (uint32_t va = (uint32_t)uvm_area.start;
|
||||
va != (uint32_t)uvm_area.end;
|
||||
va += (1 << PDXSHFT)) {
|
||||
PDE pde = upt[PDX(va)];
|
||||
if (pde & PTE_P) {
|
||||
pgfree((void *)PTE_ADDR(pde));
|
||||
}
|
||||
}
|
||||
pgfree(upt);
|
||||
void _unprotect(_AddressSpace *as) {
|
||||
teardown(0, (void *)&as->ptr);
|
||||
}
|
||||
|
||||
int _map(_AddressSpace *p, void *va, void *pa, int prot) {
|
||||
// panic because the below cases are likely bugs
|
||||
if ((prot & _PROT_NONE) && (prot != _PROT_NONE))
|
||||
panic("invalid protection flags");
|
||||
if ((uintptr_t)va != ROUNDDOWN(va, PGSIZE) ||
|
||||
(uintptr_t)pa != ROUNDDOWN(pa, PGSIZE)) {
|
||||
panic("unaligned memory address");
|
||||
}
|
||||
if (!IN_RANGE(va, uvm_area)) {
|
||||
return 1; // mapping an out-of-range address
|
||||
}
|
||||
PDE *upt = (PDE*)p->ptr;
|
||||
PDE *pde = &upt[PDX(va)];
|
||||
int _map(_AddressSpace *as, void *va, void *pa, int prot) {
|
||||
panic_on(!IN_RANGE(va, uvm_area), "mapping an invalid address");
|
||||
panic_on((prot & _PROT_NONE) && (prot != _PROT_NONE), "invalid protection flags");
|
||||
panic_on((uintptr_t)va != ROUNDDOWN(va, mmu.pgsize) ||
|
||||
(uintptr_t)pa != ROUNDDOWN(pa, mmu.pgsize), "non-page-boundary address");
|
||||
|
||||
if (!(*pde & PTE_P)) {
|
||||
*pde = PTE_P | PTE_W | PTE_U | (uint32_t)(pgalloc());
|
||||
}
|
||||
|
||||
PTE *pte = &((PTE*)PTE_ADDR(*pde))[PTX(va)];
|
||||
uintptr_t *ptentry = ptwalk(as, (uintptr_t)va, PTE_W | PTE_U);
|
||||
if (prot & _PROT_NONE) {
|
||||
*pte = 0; // unmap @va
|
||||
panic_on(!(*ptentry & PTE_P), "unmapping a non-mapped page");
|
||||
*ptentry = 0;
|
||||
} else {
|
||||
*pte = PTE_P | ((prot & _PROT_WRITE) ? PTE_W : 0) | PTE_U
|
||||
| (uint32_t)(pa); // map @va -> @pa
|
||||
panic_on(*ptentry & PTE_P, "remapping a mapped page");
|
||||
uintptr_t pte = (uintptr_t)pa | PTE_P | PTE_U | ((prot & _PROT_WRITE) ? PTE_W : 0);
|
||||
*ptentry = pte;
|
||||
}
|
||||
ptwalk(as, (uintptr_t)va, PTE_W | PTE_U);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_Context *_ucontext(_AddressSpace *as, _Area ustack, _Area kstack,
|
||||
void *entry, void *args) {
|
||||
_Context *ctx = (_Context*)kstack.start;
|
||||
*ctx = (_Context) {
|
||||
.cs = USEL(SEG_UCODE), .eip = (uint32_t)entry, .eflags = FL_IF,
|
||||
.ds = USEL(SEG_UDATA), .es = USEL(SEG_UDATA),
|
||||
.ss = USEL(SEG_UDATA), .esp3 = (uint32_t)ustack.end,
|
||||
.ss0 = KSEL(SEG_KDATA), .esp0 = (uint32_t)kstack.end,
|
||||
.eax = (uint32_t)args, // just use eax to pass @args
|
||||
.uvm = as,
|
||||
};
|
||||
_Context *_ucontext(_AddressSpace *as, _Area kstack, void *entry) {
|
||||
_Context *ctx = (_Context *)kstack.start;
|
||||
*ctx = (_Context) { 0 };
|
||||
|
||||
#if __x86_64__
|
||||
ctx->cs = USEL(SEG_UCODE);
|
||||
ctx->ss = USEL(SEG_UDATA);
|
||||
ctx->rip = (uintptr_t)entry;
|
||||
ctx->rflags = FL_IF;
|
||||
ctx->rsp = (uintptr_t)uvm_area.end;
|
||||
ctx->rsp0 = (uintptr_t)kstack.end;
|
||||
#else
|
||||
ctx->cs = USEL(SEG_UCODE);
|
||||
ctx->ds = USEL(SEG_UDATA);
|
||||
ctx->ss3 = USEL(SEG_UDATA);
|
||||
ctx->eip = (uintptr_t)entry;
|
||||
ctx->eflags = FL_IF;
|
||||
ctx->esp = (uintptr_t)uvm_area.end;
|
||||
ctx->esp0 = (uintptr_t)kstack.end;
|
||||
#endif
|
||||
ctx->uvm = as->ptr;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void *pgalloc() {
|
||||
void *ret = user_pgalloc(PGSIZE);
|
||||
if (!ret) panic("page allocation fail"); // for ease of debugging
|
||||
for (int i = 0; i < PGSIZE / sizeof(uint32_t); i++) {
|
||||
((uint32_t *)ret)[i] = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void pgfree(void *ptr) {
|
||||
user_pgfree(ptr);
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
#ifndef __AM_X86_H__
|
||||
#define __AM_X86_H__
|
||||
|
||||
#include <am.h>
|
||||
#include <amdev.h>
|
||||
#include <x86.h>
|
||||
|
||||
#define MAX_CPU 8
|
||||
|
||||
struct boot_info {
|
||||
int is_ap;
|
||||
void (*entry)();
|
||||
};
|
||||
|
||||
struct cpu_local {
|
||||
_AddressSpace *uvm;
|
||||
SegDesc gdt[NR_SEG];
|
||||
TSS tss;
|
||||
uint8_t stack[4096];
|
||||
};
|
||||
|
||||
extern volatile uint32_t *__am_lapic;
|
||||
extern int __am_ncpu;
|
||||
extern struct cpu_local __am_cpuinfo[MAX_CPU];
|
||||
|
||||
#define CPU (&__am_cpuinfo[_cpu()])
|
||||
#define LENGTH(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
#define RANGE(st, ed) (_Area) { .start = (void *)st, .end = (void *)ed }
|
||||
#define IN_RANGE(ptr, area) ((area).start <= (ptr) && (ptr) < (area).end)
|
||||
#define STRINGIFY(s) #s
|
||||
#define TOSTRING(s) STRINGIFY(s)
|
||||
|
||||
#define panic(s) \
|
||||
do { \
|
||||
puts("AM Panic: "); puts(s); \
|
||||
puts(" @ " __FILE__ ":" TOSTRING(__LINE__) " \n"); \
|
||||
_halt(1); \
|
||||
} while(0)
|
||||
|
||||
static inline void puts(const char *s) {
|
||||
for (; *s; s++)
|
||||
_putc(*s);
|
||||
}
|
||||
|
||||
// apic utils
|
||||
void __am_lapic_eoi();
|
||||
void __am_ioapic_init();
|
||||
void __am_lapic_bootap(unsigned int cpu, uint32_t address);
|
||||
void __am_ioapic_enable(int irq, int cpu);
|
||||
|
||||
// per-cpu x86-specific operations
|
||||
void __am_bootcpu_init();
|
||||
void __am_percpu_initirq();
|
||||
void __am_percpu_initgdt();
|
||||
void __am_percpu_initlapic();
|
||||
void __am_percpu_initpg();
|
||||
void __am_thiscpu_setstk0(uintptr_t ss0, uintptr_t esp0);
|
||||
void __am_thiscpu_halt() __attribute__((__noreturn__));
|
||||
void __am_othercpu_halt();
|
||||
|
||||
#endif
|
|
@ -1 +0,0 @@
|
|||
/mbr
|
|
@ -1,10 +0,0 @@
|
|||
$(info # Building bootblock [$(ARCH)])
|
||||
|
||||
mbr: start.S main.c
|
||||
@echo + CC start.S main.c
|
||||
@gcc -nostdlib -I$(AM_HOME)/am/include -Os start.S main.c -m32 -Ttext 0x7C00 -o bootblock.o
|
||||
@objcopy -S -O binary -j .text bootblock.o mbr
|
||||
@perl genboot.pl mbr
|
||||
|
||||
clean:
|
||||
rm mbr -rf bootblock *.o
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";
|
||||
|
||||
$n = sysread(SIG, $buf, 1000);
|
||||
|
||||
if($n > 510){
|
||||
print STDERR "ERROR: boot block too large: $n bytes (max 510)\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print STDERR "OK: boot block is $n bytes (max 510)\n";
|
||||
|
||||
$buf .= "\0" x (510-$n);
|
||||
$buf .= "\x55\xAA";
|
||||
|
||||
open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
|
||||
print SIG $buf;
|
||||
close SIG;
|
|
@ -1,90 +0,0 @@
|
|||
#include <stdint.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/elf-em.h>
|
||||
#include <x86.h>
|
||||
|
||||
#define SECTSIZE 512
|
||||
|
||||
static inline void wait_disk(void) {
|
||||
while ((inb(0x1f7) & 0xc0) != 0x40);
|
||||
}
|
||||
|
||||
static inline void read_disk(void *buf, int sect) {
|
||||
wait_disk();
|
||||
outb(0x1f2, 1);
|
||||
outb(0x1f3, sect);
|
||||
outb(0x1f4, sect >> 8);
|
||||
outb(0x1f5, sect >> 16);
|
||||
outb(0x1f6, (sect >> 24) | 0xE0);
|
||||
outb(0x1f7, 0x20);
|
||||
wait_disk();
|
||||
for (int i = 0; i < SECTSIZE / 4; i ++) {
|
||||
((uint32_t *)buf)[i] = inl(0x1f0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void copy_from_disk(void *buf, int nbytes, int disk_offset) {
|
||||
uint32_t cur = (uint32_t)buf & ~511;
|
||||
uint32_t ed = (uint32_t)buf + nbytes;
|
||||
uint32_t sect = (disk_offset >> 9) + 3;
|
||||
for(; cur < ed; cur += SECTSIZE, sect ++)
|
||||
read_disk((void *)cur, sect);
|
||||
}
|
||||
|
||||
static void load_program(uint32_t filesz, uint32_t memsz, uint32_t paddr, uint32_t offset) {
|
||||
copy_from_disk((void *)paddr, filesz, offset);
|
||||
char *bss = (void *)(paddr + filesz);
|
||||
for (uint32_t i = filesz; i != memsz; i++) {
|
||||
*bss++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_elf64(struct elf64_hdr *elf) {
|
||||
struct elf64_phdr *ph = (struct elf64_phdr *)((char *)elf + elf->e_phoff);
|
||||
for (int i = 0; i < elf->e_phnum; i++, ph++) {
|
||||
load_program(
|
||||
(uint32_t)ph->p_filesz,
|
||||
(uint32_t)ph->p_memsz,
|
||||
(uint32_t)ph->p_paddr,
|
||||
(uint32_t)ph->p_offset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void load_elf32(struct elf32_hdr *elf) {
|
||||
struct elf32_phdr *ph = (struct elf32_phdr *)((char *)elf + elf->e_phoff);
|
||||
for (int i = 0; i < elf->e_phnum; i++, ph++) {
|
||||
load_program(
|
||||
(uint32_t)ph->p_filesz,
|
||||
(uint32_t)ph->p_memsz,
|
||||
(uint32_t)ph->p_paddr,
|
||||
(uint32_t)ph->p_offset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void load_kernel(void) {
|
||||
struct elf32_hdr *elf32 = (void *)0x8000;
|
||||
struct elf64_hdr *elf64 = (void *)0x8000;
|
||||
int is_ap = boot_record()->is_ap;
|
||||
|
||||
if (!is_ap) {
|
||||
// load argument (string) to memory
|
||||
copy_from_disk((void *)MAINARG_ADDR, 1024, -1024);
|
||||
// load elf header to memory
|
||||
copy_from_disk(elf32, 4096, 0);
|
||||
if (elf32->e_machine == EM_X86_64) {
|
||||
load_elf64(elf64);
|
||||
} else {
|
||||
load_elf32(elf32);
|
||||
}
|
||||
} else {
|
||||
// everything should be loaded
|
||||
}
|
||||
|
||||
if (elf32->e_machine == EM_X86_64) {
|
||||
((void(*)())(uint32_t)elf64->e_entry)();
|
||||
} else {
|
||||
((void(*)())(uint32_t)elf32->e_entry)();
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
#define CR0_PE 0x00000001
|
||||
|
||||
#define GDT_ENTRY(n) \
|
||||
((n) << 3)
|
||||
|
||||
#define SEG_NULLASM \
|
||||
.word 0, 0; \
|
||||
.byte 0, 0, 0, 0
|
||||
|
||||
#define SEG_ASM(type, base, lim) \
|
||||
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
||||
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
|
||||
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
||||
|
||||
.code16
|
||||
.globl _start
|
||||
_start:
|
||||
cli
|
||||
|
||||
xorw %ax, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
|
||||
# Set a 640 x 480 x 32 video mode
|
||||
mov $0x4f01, %ax
|
||||
mov $0x0112, %cx
|
||||
mov $0x4000, %di
|
||||
int $0x10
|
||||
|
||||
mov $0x4f02, %ax
|
||||
mov $0x4112, %bx
|
||||
int $0x10
|
||||
|
||||
lgdt gdtdesc
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
ljmp $GDT_ENTRY(1), $start32
|
||||
|
||||
.code32
|
||||
start32:
|
||||
movw $GDT_ENTRY(2), %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
|
||||
movl $0xa000, %esp
|
||||
call load_kernel
|
||||
|
||||
# GDT
|
||||
.p2align 2
|
||||
gdt:
|
||||
SEG_NULLASM
|
||||
SEG_ASM(0xA, 0x0, 0xffffffff)
|
||||
SEG_ASM(0x2, 0x0, 0xffffffff)
|
||||
|
||||
gdtdesc:
|
||||
.word (gdtdesc - gdt - 1)
|
||||
.long gdt
|
|
@ -1,180 +0,0 @@
|
|||
#include "x86_64-qemu.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
static _Context* (*user_handler)(_Event, _Context*) = NULL;
|
||||
#if __x86_64__
|
||||
static GateDesc64 idt[NR_IRQ];
|
||||
#define GATE GATE64
|
||||
#else
|
||||
static GateDesc32 idt[NR_IRQ];
|
||||
#define GATE GATE32
|
||||
#endif
|
||||
|
||||
#define IRQHANDLE_DECL(id, dpl, err) \
|
||||
void __am_irq##id();
|
||||
|
||||
IRQS(IRQHANDLE_DECL)
|
||||
void __am_irqall();
|
||||
|
||||
static void __am_irq_handle_internal(struct trap_frame *tf) {
|
||||
_Context saved_ctx;
|
||||
_Event ev = {
|
||||
.event = _EVENT_NULL,
|
||||
.cause = 0, .ref = 0,
|
||||
.msg = "(no message)",
|
||||
};
|
||||
|
||||
#if __x86_64
|
||||
saved_ctx = tf->saved_context;
|
||||
saved_ctx.rip = tf->rip;
|
||||
saved_ctx.cs = tf->cs;
|
||||
saved_ctx.rflags = tf->rflags;
|
||||
saved_ctx.rsp = tf->rsp;
|
||||
saved_ctx.rsp0 = CPU->tss.rsp0;
|
||||
saved_ctx.ss = tf->ss;
|
||||
saved_ctx.uvm = (void *)get_cr3();
|
||||
#else
|
||||
saved_ctx = tf->saved_context;
|
||||
saved_ctx.eip = tf->eip;
|
||||
saved_ctx.cs = tf->cs;
|
||||
saved_ctx.eflags = tf->eflags;
|
||||
saved_ctx.esp0 = CPU->tss.esp0;
|
||||
saved_ctx.ss3 = USEL(SEG_UDATA);
|
||||
saved_ctx.uvm = (void *)get_cr3();
|
||||
if (!(tf->cs & DPL_USER)) {
|
||||
saved_ctx.esp = (uint32_t)(tf + 1) - 8; // no ss/esp saved
|
||||
}
|
||||
#endif
|
||||
|
||||
#define IRQ T_IRQ0 +
|
||||
#define MSG(m) ev.msg = m;
|
||||
|
||||
if (IRQ 0 <= tf->irq && tf->irq < IRQ 32) {
|
||||
__am_lapic_eoi();
|
||||
}
|
||||
|
||||
switch (tf->irq) {
|
||||
case IRQ 0: MSG("timer interrupt (lapic)")
|
||||
ev.event = _EVENT_IRQ_TIMER; break;
|
||||
case IRQ 1: MSG("I/O device IRQ1 (keyboard)")
|
||||
ev.event = _EVENT_IRQ_IODEV; break;
|
||||
case EX_SYSCALL: MSG("int $0x80 trap: _yield() or system call")
|
||||
if ((int32_t)saved_ctx.GPR1 == -1) {
|
||||
ev.event = _EVENT_YIELD;
|
||||
} else {
|
||||
ev.event = _EVENT_SYSCALL;
|
||||
}
|
||||
break;
|
||||
case EX_DE: MSG("DE #0 divide by zero")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_UD: MSG("UD #6 invalid opcode")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_NM: MSG("NM #7 coprocessor error")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_DF: MSG("DF #8 double fault")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_TS: MSG("TS #10 invalid TSS")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_NP: MSG("NP #11 segment/gate not present")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_SS: MSG("SS #12 stack fault")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_GP: MSG("GP #13, general protection fault")
|
||||
ev.event = _EVENT_ERROR; break;
|
||||
case EX_PF: MSG("PF #14, page fault, @cause: _PROT_XXX")
|
||||
ev.event = _EVENT_PAGEFAULT;
|
||||
if (tf->errcode & 0x1) ev.cause |= _PROT_NONE;
|
||||
if (tf->errcode & 0x2) ev.cause |= _PROT_WRITE;
|
||||
else ev.cause |= _PROT_READ;
|
||||
ev.ref = get_cr2();
|
||||
break;
|
||||
default: MSG("unrecognized interrupt/exception")
|
||||
ev.event = _EVENT_ERROR;
|
||||
ev.cause = tf->errcode;
|
||||
break;
|
||||
}
|
||||
|
||||
_Context *ret_ctx = user_handler(ev, &saved_ctx);
|
||||
|
||||
if (ret_ctx->uvm) {
|
||||
set_cr3(ret_ctx->uvm);
|
||||
#if __x86_64__
|
||||
CPU->tss.rsp0 = ret_ctx->rsp0;
|
||||
#else
|
||||
CPU->tss.ss0 = KSEL(SEG_KDATA);
|
||||
CPU->tss.esp0 = ret_ctx->esp0;
|
||||
#endif
|
||||
}
|
||||
|
||||
__am_iret(ret_ctx ? ret_ctx : &saved_ctx);
|
||||
}
|
||||
|
||||
void __am_irq_handle(struct trap_frame *tf) {
|
||||
stack_switch_call(stack_top(&CPU->irq_stack), __am_irq_handle_internal, (uintptr_t)tf);
|
||||
}
|
||||
|
||||
int _cte_init(_Context *(*handler)(_Event, _Context *)) {
|
||||
panic_on(_cpu() != 0, "init CTE in non-bootstrap CPU");
|
||||
panic_on(!handler, "no interrupt handler");
|
||||
|
||||
for (int i = 0; i < NR_IRQ; i ++) {
|
||||
idt[i] = GATE(STS_TG, KSEL(SEG_KCODE), __am_irqall, DPL_KERN);
|
||||
}
|
||||
#define IDT_ENTRY(id, dpl, err) \
|
||||
idt[id] = GATE(STS_TG, KSEL(SEG_KCODE), __am_irq##id, DPL_##dpl);
|
||||
IRQS(IDT_ENTRY)
|
||||
|
||||
user_handler = handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _yield() {
|
||||
interrupt(0x80, -1);
|
||||
}
|
||||
|
||||
int _intr_read() {
|
||||
return (get_efl() & FL_IF) != 0;
|
||||
}
|
||||
|
||||
void _intr_write(int enable) {
|
||||
if (enable) {
|
||||
sti();
|
||||
} else {
|
||||
cli();
|
||||
}
|
||||
}
|
||||
|
||||
static void panic_on_return() { panic("kernel context returns"); }
|
||||
|
||||
_Context *_kcontext(_Area stack, void (*entry)(void *), void *arg) {
|
||||
_Context *ctx = (_Context *)stack.start;
|
||||
*ctx = (_Context) { 0 };
|
||||
|
||||
#if __x86_64__
|
||||
#define sp rsp
|
||||
ctx->rsp = (uintptr_t)stack.end;
|
||||
ctx->cs = KSEL(SEG_KCODE);
|
||||
ctx->rip = (uintptr_t)entry;
|
||||
ctx->rflags = FL_IF;
|
||||
ctx->rdi = (uintptr_t)arg;
|
||||
void *stk[] = { panic_on_return };
|
||||
#else
|
||||
#define sp esp
|
||||
ctx->esp = (uintptr_t)stack.end;
|
||||
ctx->ds = KSEL(SEG_KDATA);
|
||||
ctx->cs = KSEL(SEG_KCODE);
|
||||
ctx->eip = (uint32_t)entry;
|
||||
ctx->eflags = FL_IF;
|
||||
void *stk[] = { panic_on_return, arg };
|
||||
#endif
|
||||
ctx->sp -= sizeof(stk);
|
||||
for (int i = 0; i < LENGTH(stk); i++) {
|
||||
((uintptr_t *)ctx->sp)[i] = (uintptr_t)stk[i];
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void __am_percpu_initirq() {
|
||||
__am_ioapic_enable(IRQ_KBD, 0);
|
||||
set_idt(idt, sizeof(idt));
|
||||
}
|
|
@ -1,395 +0,0 @@
|
|||
#include "x86_64-qemu.h"
|
||||
#include <amdev.h>
|
||||
|
||||
void __am_vga_init();
|
||||
void __am_timer_init();
|
||||
|
||||
#define DEF_DEVOP(fn) \
|
||||
size_t fn(uintptr_t reg, void *buf, size_t size);
|
||||
|
||||
DEF_DEVOP(__am_input_read);
|
||||
DEF_DEVOP(__am_timer_read);
|
||||
DEF_DEVOP(__am_video_read);
|
||||
DEF_DEVOP(__am_video_write);
|
||||
|
||||
|
||||
// AM INPUT (keyboard)
|
||||
|
||||
static int scan_code[] = {
|
||||
0, 1, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88,
|
||||
41, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 43,
|
||||
58, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 28,
|
||||
42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
||||
29, 91, 56, 57, 56, 29,
|
||||
72, 80, 75, 77
|
||||
};
|
||||
|
||||
size_t __am_input_read(uintptr_t reg, void *buf, size_t size) {
|
||||
_DEV_INPUT_KBD_t *kbd = (_DEV_INPUT_KBD_t *)buf;
|
||||
|
||||
int status = inb(0x64);
|
||||
kbd->keydown = 0;
|
||||
kbd->keycode = _KEY_NONE;
|
||||
|
||||
if ((status & 0x1) == 0) {
|
||||
} else {
|
||||
if (status & 0x20) { // mouse
|
||||
} else {
|
||||
int code = inb(0x60) & 0xff;
|
||||
|
||||
for (int i = 0; i < LENGTH(scan_code); i ++) {
|
||||
if (scan_code[i] == 0) continue;
|
||||
if (scan_code[i] == code) {
|
||||
kbd->keydown = 1;
|
||||
kbd->keycode = i;
|
||||
break;
|
||||
} else if (scan_code[i] + 128 == code) {
|
||||
kbd->keydown = 0;
|
||||
kbd->keycode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sizeof(*kbd);
|
||||
}
|
||||
|
||||
// AM TIMER (based on rdtsc)
|
||||
|
||||
static _DEV_TIMER_DATE_t boot_date;
|
||||
static uint32_t freq_mhz = 2000;
|
||||
static uint64_t uptsc;
|
||||
|
||||
static inline int read_rtc(int reg) {
|
||||
outb(0x70, reg);
|
||||
int ret = inb(0x71);
|
||||
return (ret & 0xf) + (ret >> 4) * 10;
|
||||
}
|
||||
|
||||
static void read_rtc_async(_DEV_TIMER_DATE_t *rtc) {
|
||||
rtc->second = read_rtc(0);
|
||||
rtc->minute = read_rtc(2);
|
||||
rtc->hour = read_rtc(4);
|
||||
rtc->day = read_rtc(7);
|
||||
rtc->month = read_rtc(8);
|
||||
rtc->year = read_rtc(9) + 2000;
|
||||
}
|
||||
|
||||
static void wait_sec(_DEV_TIMER_DATE_t *t1) {
|
||||
_DEV_TIMER_DATE_t t0;
|
||||
while (1) {
|
||||
read_rtc_async(&t0);
|
||||
for (int volatile i = 0; i < 100000; i++) ;
|
||||
read_rtc_async(t1);
|
||||
if (t0.second != t1->second) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t estimate_freq() {
|
||||
_DEV_TIMER_DATE_t rtc1, rtc2;
|
||||
uint64_t tsc1, tsc2, t1, t2;
|
||||
wait_sec(&rtc1); tsc1 = rdtsc(); t1 = rtc1.minute * 60 + rtc1.second;
|
||||
wait_sec(&rtc2); tsc2 = rdtsc(); t2 = rtc2.minute * 60 + rtc2.second;
|
||||
if (t1 >= t2) return estimate_freq(); // passed an hour; try again
|
||||
return ((tsc2 - tsc1) >> 20) / (t2 - t1);
|
||||
}
|
||||
|
||||
static void get_date(_DEV_TIMER_DATE_t *rtc) {
|
||||
int tmp;
|
||||
do {
|
||||
read_rtc_async(rtc);
|
||||
tmp = read_rtc(0);
|
||||
} while (tmp != rtc->second);
|
||||
}
|
||||
|
||||
void __am_timer_init() {
|
||||
freq_mhz = estimate_freq();
|
||||
get_date(&boot_date);
|
||||
uptsc = rdtsc();
|
||||
}
|
||||
|
||||
size_t __am_timer_read(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_TIMER_UPTIME: {
|
||||
_DEV_TIMER_UPTIME_t *uptime = (_DEV_TIMER_UPTIME_t *)buf;
|
||||
uint64_t tsc = rdtsc() - uptsc;
|
||||
uint32_t mticks = (tsc >> 20);
|
||||
uint32_t ms = mticks * 1000 / freq_mhz;
|
||||
uptime->hi = 0;
|
||||
uptime->lo = ms;
|
||||
return sizeof(_DEV_TIMER_UPTIME_t);
|
||||
}
|
||||
case _DEVREG_TIMER_DATE: {
|
||||
get_date((_DEV_TIMER_DATE_t *)buf);
|
||||
return sizeof(_DEV_TIMER_DATE_t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// AM VIDEO
|
||||
|
||||
struct vbe_info {
|
||||
uint8_t ignore[18];
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t ignore1[18];
|
||||
uint32_t framebuffer;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static inline uint32_t pixel(uint8_t r, uint8_t g, uint8_t b) { return (r << 16) | (g << 8) | b; }
|
||||
static inline uint8_t R(uint32_t p) { return p >> 16; }
|
||||
static inline uint8_t G(uint32_t p) { return p >> 8; }
|
||||
static inline uint8_t B(uint32_t p) { return p; }
|
||||
|
||||
struct pixel {
|
||||
uint8_t b, g, r;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pixel *fb;
|
||||
static int W, H;
|
||||
|
||||
void __am_vga_init() {
|
||||
struct vbe_info *info = (struct vbe_info *)0x00004000;
|
||||
W = info->width;
|
||||
H = info->height;
|
||||
fb = upcast(info->framebuffer);
|
||||
}
|
||||
|
||||
size_t __am_video_read(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_VIDEO_INFO: {
|
||||
_DEV_VIDEO_INFO_t *info = (_DEV_VIDEO_INFO_t *)buf;
|
||||
info->width = W;
|
||||
info->height = H;
|
||||
return sizeof(_DEV_VIDEO_INFO_t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t __am_video_write(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_VIDEO_FBCTRL: {
|
||||
_DEV_VIDEO_FBCTRL_t *ctl = (_DEV_VIDEO_FBCTRL_t *)buf;
|
||||
int x = ctl->x, y = ctl->y, w = ctl->w, h = ctl->h;
|
||||
uint32_t *pixels = ctl->pixels;
|
||||
int len = (x + w >= W) ? W - x : w;
|
||||
for (int j = 0; j < h; j ++, pixels += w) {
|
||||
if (y + j < H) {
|
||||
struct pixel *px = &fb[x + (j + y) * W];
|
||||
for (int i = 0; i < len; i ++, px ++) {
|
||||
uint32_t p = pixels[i];
|
||||
px->r = R(p); px->g = G(p); px->b = B(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sizeof(*ctl);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// AM STORAGE
|
||||
|
||||
size_t __am_storage_read(uintptr_t reg, void *buf, size_t size) {
|
||||
switch (reg) {
|
||||
case _DEVREG_STORAGE_INFO: {
|
||||
_DEV_STORAGE_INFO_t *info = (void *)buf;
|
||||
info->blksz = 512;
|
||||
info->blkcnt = 524288;
|
||||
return sizeof(*info);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void wait_disk(void) {
|
||||
while ((inb(0x1f7) & 0xc0) != 0x40);
|
||||
}
|
||||
|
||||
size_t __am_storage_write(uintptr_t reg, void *buf, size_t size) {
|
||||
_DEV_STORAGE_RDCTRL_t *ctl = (void *)buf;
|
||||
int is_read = 0;
|
||||
switch (reg) {
|
||||
case _DEVREG_STORAGE_RDCTRL: is_read = 1; break;
|
||||
case _DEVREG_STORAGE_WRCTRL: break;
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
uint32_t blkno = ctl->blkno, remain = ctl->blkcnt;
|
||||
uint32_t *ptr = ctl->buf;
|
||||
for (remain = ctl->blkcnt; remain; remain--, blkno++) {
|
||||
wait_disk();
|
||||
outb(0x1f2, 1);
|
||||
outb(0x1f3, blkno);
|
||||
outb(0x1f4, blkno >> 8);
|
||||
outb(0x1f5, blkno >> 16);
|
||||
outb(0x1f6, (blkno >> 24) | 0xe0);
|
||||
outb(0x1f7, is_read ? 0x20 : 0x30);
|
||||
wait_disk();
|
||||
if (is_read) {
|
||||
for (int i = 0; i < 512 / 4; i ++) {
|
||||
*ptr++ = inl(0x1f0);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 512 / 4; i ++) {
|
||||
outl(0x1f0, *ptr++);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sizeof(*ctl);
|
||||
}
|
||||
|
||||
size_t _io_read(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
|
||||
switch (dev) {
|
||||
case _DEV_INPUT: return __am_input_read(reg, buf, size);
|
||||
case _DEV_TIMER: return __am_timer_read(reg, buf, size);
|
||||
case _DEV_VIDEO: return __am_video_read(reg, buf, size);
|
||||
case _DEV_STORAGE: return __am_storage_read(reg, buf, size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t _io_write(uint32_t dev, uintptr_t reg, void *buf, size_t size) {
|
||||
switch (dev) {
|
||||
case _DEV_VIDEO: return __am_video_write(reg, buf, size);
|
||||
case _DEV_STORAGE: return __am_storage_write(reg, buf, size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _ioe_init() {
|
||||
panic_on(_cpu() != 0, "init IOE in non-bootstrap CPU");
|
||||
__am_timer_init();
|
||||
__am_vga_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// LAPIC/IOAPIC (from xv6)
|
||||
|
||||
#define ID (0x0020/4) // ID
|
||||
#define VER (0x0030/4) // Version
|
||||
#define TPR (0x0080/4) // Task Priority
|
||||
#define EOI (0x00B0/4) // EOI
|
||||
#define SVR (0x00F0/4) // Spurious Interrupt Vector
|
||||
#define ENABLE 0x00000100 // Unit Enable
|
||||
#define ESR (0x0280/4) // Error Status
|
||||
#define ICRLO (0x0300/4) // Interrupt Command
|
||||
#define INIT 0x00000500 // INIT/RESET
|
||||
#define STARTUP 0x00000600 // Startup IPI
|
||||
#define DELIVS 0x00001000 // Delivery status
|
||||
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
|
||||
#define DEASSERT 0x00000000
|
||||
#define LEVEL 0x00008000 // Level triggered
|
||||
#define BCAST 0x00080000 // Send to all APICs, including self.
|
||||
#define BUSY 0x00001000
|
||||
#define FIXED 0x00000000
|
||||
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
|
||||
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
|
||||
#define X1 0x0000000B // divide counts by 1
|
||||
#define PERIODIC 0x00020000 // Periodic
|
||||
#define PCINT (0x0340/4) // Performance Counter LVT
|
||||
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
|
||||
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
|
||||
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
|
||||
#define MASKED 0x00010000 // Interrupt masked
|
||||
#define TICR (0x0380/4) // Timer Initial Count
|
||||
#define TCCR (0x0390/4) // Timer Current Count
|
||||
#define TDCR (0x03E0/4) // Timer Divide Configuration
|
||||
|
||||
#define IOAPIC_ADDR 0xFEC00000 // Default physical address of IO APIC
|
||||
#define REG_ID 0x00 // Register index: ID
|
||||
#define REG_VER 0x01 // Register index: version
|
||||
#define REG_TABLE 0x10 // Redirection table base
|
||||
|
||||
#define INT_DISABLED 0x00010000 // Interrupt disabled
|
||||
#define INT_LEVEL 0x00008000 // Level-triggered (vs edge-)
|
||||
#define INT_ACTIVELOW 0x00002000 // Active low (vs high)
|
||||
#define INT_LOGICAL 0x00000800 // Destination is CPU id (vs APIC ID)
|
||||
|
||||
volatile unsigned int *__am_lapic = NULL; // Initialized in mp.c
|
||||
struct IOAPIC {
|
||||
uint32_t reg, pad[3], data;
|
||||
} __attribute__((packed));
|
||||
typedef struct IOAPIC IOAPIC;
|
||||
|
||||
static volatile IOAPIC *ioapic;
|
||||
|
||||
static void lapicw(int index, int value) {
|
||||
__am_lapic[index] = value;
|
||||
__am_lapic[ID];
|
||||
}
|
||||
|
||||
void __am_percpu_initlapic(void) {
|
||||
lapicw(SVR, ENABLE | (T_IRQ0 + IRQ_SPURIOUS));
|
||||
lapicw(TDCR, X1);
|
||||
lapicw(TIMER, PERIODIC | (T_IRQ0 + IRQ_TIMER));
|
||||
lapicw(TICR, 10000000);
|
||||
lapicw(LINT0, MASKED);
|
||||
lapicw(LINT1, MASKED);
|
||||
if (((__am_lapic[VER]>>16) & 0xFF) >= 4)
|
||||
lapicw(PCINT, MASKED);
|
||||
lapicw(ERROR, T_IRQ0 + IRQ_ERROR);
|
||||
lapicw(ESR, 0);
|
||||
lapicw(ESR, 0);
|
||||
lapicw(EOI, 0);
|
||||
lapicw(ICRHI, 0);
|
||||
lapicw(ICRLO, BCAST | INIT | LEVEL);
|
||||
while(__am_lapic[ICRLO] & DELIVS) ;
|
||||
lapicw(TPR, 0);
|
||||
}
|
||||
|
||||
void __am_lapic_eoi(void) {
|
||||
if (__am_lapic)
|
||||
lapicw(EOI, 0);
|
||||
}
|
||||
|
||||
void __am_lapic_bootap(uint32_t apicid, void *addr) {
|
||||
int i;
|
||||
uint16_t *wrv;
|
||||
outb(0x70, 0xF);
|
||||
outb(0x71, 0x0A);
|
||||
wrv = (unsigned short*)((0x40<<4 | 0x67));
|
||||
wrv[0] = 0;
|
||||
wrv[1] = (uintptr_t)addr >> 4;
|
||||
|
||||
lapicw(ICRHI, apicid<<24);
|
||||
lapicw(ICRLO, INIT | LEVEL | ASSERT);
|
||||
lapicw(ICRLO, INIT | LEVEL);
|
||||
|
||||
for (i = 0; i < 2; i++){
|
||||
lapicw(ICRHI, apicid<<24);
|
||||
lapicw(ICRLO, STARTUP | ((uintptr_t)addr>>12));
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int ioapicread(int reg) {
|
||||
ioapic->reg = reg;
|
||||
return ioapic->data;
|
||||
}
|
||||
|
||||
static void ioapicwrite(int reg, unsigned int data) {
|
||||
ioapic->reg = reg;
|
||||
ioapic->data = data;
|
||||
}
|
||||
|
||||
void __am_ioapic_init(void) {
|
||||
int i, maxintr;
|
||||
|
||||
ioapic = (volatile IOAPIC*)IOAPIC_ADDR;
|
||||
maxintr = (ioapicread(REG_VER) >> 16) & 0xFF;
|
||||
|
||||
for (i = 0; i <= maxintr; i++){
|
||||
ioapicwrite(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i));
|
||||
ioapicwrite(REG_TABLE+2*i+1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void __am_ioapic_enable(int irq, int cpunum) {
|
||||
ioapicwrite(REG_TABLE+2*irq, T_IRQ0 + irq);
|
||||
ioapicwrite(REG_TABLE+2*irq+1, cpunum << 24);
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#include "x86_64-qemu.h"
|
||||
|
||||
struct cpu_local __am_cpuinfo[MAX_CPU] = {};
|
||||
static void (* volatile user_entry)();
|
||||
static volatile intptr_t ap_ready = 0;
|
||||
|
||||
static void call_user_entry() {
|
||||
user_entry();
|
||||
panic("MPE entry should not return");
|
||||
}
|
||||
|
||||
int _mpe_init(void (*entry)()) {
|
||||
user_entry = entry;
|
||||
boot_record()->jmp_code = 0x000bfde9; // (16-bit) jmp (0x7c00)
|
||||
for (int cpu = 1; cpu < __am_ncpu; cpu++) {
|
||||
boot_record()->is_ap = 1;
|
||||
__am_lapic_bootap(cpu, (void *)boot_record());
|
||||
while (_atomic_xchg(&ap_ready, 0) != 1) {
|
||||
pause();
|
||||
}
|
||||
}
|
||||
call_user_entry();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void othercpu_entry() {
|
||||
__am_percpu_init();
|
||||
_atomic_xchg(&ap_ready, 1);
|
||||
call_user_entry();
|
||||
}
|
||||
|
||||
void __am_othercpu_entry() {
|
||||
stack_switch_call(stack_top(&CPU->stack), othercpu_entry, 0);
|
||||
}
|
||||
|
||||
int _ncpu() {
|
||||
return __am_ncpu;
|
||||
}
|
||||
|
||||
int _cpu(void) {
|
||||
return __am_lapic[8] >> 24;
|
||||
}
|
||||
|
||||
intptr_t _atomic_xchg(volatile intptr_t *addr, intptr_t newval) {
|
||||
return xchg(addr, newval);
|
||||
}
|
||||
|
||||
void __am_stop_the_world() {
|
||||
boot_record()->jmp_code = 0x0000feeb; // (16-bit) jmp .
|
||||
for (int cpu = 0; cpu < __am_ncpu; cpu++) {
|
||||
if (cpu != _cpu()) {
|
||||
__am_lapic_bootap(cpu, (void *)boot_record());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
#include "x86_64-qemu.h"
|
||||
|
||||
_Area _heap = {};
|
||||
volatile uint32_t *__am_lapic;
|
||||
int __am_ncpu = 0;
|
||||
struct cpu_local __am_cpuinfo[MAX_CPU];
|
||||
|
||||
int main(const char *args);
|
||||
|
||||
static void call_main(const char *args) {
|
||||
_halt(main(args));
|
||||
}
|
||||
|
||||
void _start_c(char *args) {
|
||||
if (boot_record()->is_ap) {
|
||||
__am_othercpu_entry();
|
||||
} else {
|
||||
__am_bootcpu_init();
|
||||
stack_switch_call(stack_top(&CPU->stack), call_main, (uintptr_t)args);
|
||||
}
|
||||
}
|
||||
|
||||
void __am_bootcpu_init() {
|
||||
_heap = __am_heap_init();
|
||||
__am_lapic_init();
|
||||
__am_ioapic_init();
|
||||
__am_percpu_init();
|
||||
}
|
||||
|
||||
void __am_percpu_init() {
|
||||
__am_percpu_initgdt();
|
||||
__am_percpu_initlapic();
|
||||
__am_percpu_initirq();
|
||||
}
|
||||
|
||||
void _putc(char ch) {
|
||||
#define COM1 0x3f8
|
||||
outb(COM1, ch);
|
||||
}
|
||||
|
||||
void _halt(int code) {
|
||||
const char *hex = "0123456789abcdef";
|
||||
const char *fmt = "CPU #$ Halt (40).\n";
|
||||
cli();
|
||||
__am_stop_the_world();
|
||||
for (const char *p = fmt; *p; p++) {
|
||||
char ch = *p;
|
||||
switch (ch) {
|
||||
case '$':
|
||||
_putc(hex[_cpu()]);
|
||||
break;
|
||||
case '0': case '4':
|
||||
_putc(hex[(code >> (ch - '0')) & 0xf]);
|
||||
break;
|
||||
default:
|
||||
_putc(ch);
|
||||
}
|
||||
}
|
||||
outw(0x604, 0x2000); // offer of qemu :)
|
||||
while (1) hlt();
|
||||
|
||||
}
|
||||
|
||||
_Area __am_heap_init() {
|
||||
int32_t magic = 0x5a5aa5a5;
|
||||
int32_t step = 1L << 20;
|
||||
extern char end;
|
||||
uintptr_t st, ed;
|
||||
for (st = ed = ROUNDUP(&end, step); ; ed += step) {
|
||||
volatile uint32_t *ptr = (uint32_t *)ed;
|
||||
if ((*ptr = magic, *ptr) != magic) {
|
||||
break; // read-after-write fail
|
||||
}
|
||||
}
|
||||
return RANGE(st, ed);
|
||||
}
|
||||
|
||||
void __am_lapic_init() {
|
||||
for (char *st = (char *)0xf0000; st != (char *)0xffffff; st ++) {
|
||||
if (*(volatile uint32_t *)st == 0x5f504d5f) {
|
||||
uint32_t mpconf_ptr = ((volatile MPDesc *)st)->conf;
|
||||
MPConf *conf = upcast(mpconf_ptr);
|
||||
__am_lapic = upcast(conf->lapicaddr);
|
||||
for (volatile char *ptr = (char *)(conf + 1);
|
||||
ptr < (char *)conf + conf->length; ptr += 8) {
|
||||
if (*ptr == '\0') {
|
||||
ptr += 12;
|
||||
panic_on(++__am_ncpu > MAX_CPU, "cannot support > MAX_CPU processors");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic("seems not an x86-qemu virtual machine");
|
||||
}
|
||||
|
||||
void __am_percpu_initgdt() {
|
||||
#if __x86_64__
|
||||
SegDesc *gdt = CPU->gdt;
|
||||
TSS64 *tss = &CPU->tss;
|
||||
gdt[SEG_KCODE] = SEG64(STA_X | STA_R, DPL_KERN);
|
||||
gdt[SEG_KDATA] = SEG64(STA_W, DPL_KERN);
|
||||
gdt[SEG_UCODE] = SEG64(STA_X | STA_R, DPL_USER);
|
||||
gdt[SEG_UDATA] = SEG64(STA_W, DPL_USER);
|
||||
gdt[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, DPL_KERN);
|
||||
bug_on((uintptr_t)tss >> 32);
|
||||
set_gdt(gdt, sizeof(gdt[0]) * (NR_SEG + 1));
|
||||
set_tr(KSEL(SEG_TSS));
|
||||
#else
|
||||
SegDesc *gdt = CPU->gdt;
|
||||
TSS32 *tss = &CPU->tss;
|
||||
gdt[SEG_KCODE] = SEG32(STA_X | STA_R, 0, 0xffffffff, DPL_KERN);
|
||||
gdt[SEG_KDATA] = SEG32(STA_W, 0, 0xffffffff, DPL_KERN);
|
||||
gdt[SEG_UCODE] = SEG32(STA_X | STA_R, 0, 0xffffffff, DPL_USER);
|
||||
gdt[SEG_UDATA] = SEG32(STA_W, 0, 0xffffffff, DPL_USER);
|
||||
gdt[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, DPL_KERN);
|
||||
set_gdt(gdt, sizeof(gdt[0]) * NR_SEG);
|
||||
set_tr(KSEL(SEG_TSS));
|
||||
#endif
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
#include "x86_64-qemu.h"
|
||||
|
||||
const struct mmu_config mmu = {
|
||||
.pgsize = 4096,
|
||||
#if __x86_64__
|
||||
.ptlevels = 4,
|
||||
.pgtables = {
|
||||
{ "CR3", 0x000000000000, 0, 0 },
|
||||
{ "PML4", 0xff8000000000, 39, 9 },
|
||||
{ "PDPT", 0x007fc0000000, 30, 9 },
|
||||
{ "PD", 0x00003fe00000, 21, 9 },
|
||||
{ "PT", 0x0000001ff000, 12, 9 },
|
||||
},
|
||||
#else
|
||||
.ptlevels = 2,
|
||||
.pgtables = {
|
||||
{ "CR3", 0x00000000, 0, 0 },
|
||||
{ "PD", 0xffc00000, 22, 10 },
|
||||
{ "PT", 0x003ff000, 12, 10 },
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct vm_area vm_areas[] = {
|
||||
#ifdef __x86_64__
|
||||
{ RANGE(0x100000000000, 0x108000000000), 0 }, // 512 GiB user space
|
||||
{ RANGE(0x000000000000, 0x008000000000), 1 }, // 512 GiB kernel
|
||||
#else
|
||||
{ RANGE( 0x40000000, 0x80000000), 0 }, // 1 GiB user space
|
||||
{ RANGE( 0x00000000, 0x40000000), 1 }, // 1 GiB kernel
|
||||
{ RANGE( 0xfd000000, 0x00000000), 1 }, // memory-mapped I/O
|
||||
#endif
|
||||
};
|
||||
#define uvm_area (vm_areas[0].area)
|
||||
|
||||
static uintptr_t *kpt;
|
||||
static void *(*pgalloc)(size_t size);
|
||||
static void (*pgfree)(void *);
|
||||
|
||||
static void *pgallocz() {
|
||||
uintptr_t *base = pgalloc(mmu.pgsize);
|
||||
panic_on(!base, "cannot allocate page");
|
||||
for (int i = 0; i < mmu.pgsize / sizeof(uintptr_t); i++) {
|
||||
base[i] = 0;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
static int indexof(uintptr_t addr, const struct ptinfo *info) {
|
||||
return ((uintptr_t)addr & info->mask) >> info->shift;
|
||||
}
|
||||
|
||||
static uintptr_t baseof(uintptr_t addr) {
|
||||
return addr & ~(mmu.pgsize - 1);
|
||||
}
|
||||
|
||||
static uintptr_t *ptwalk(_AddressSpace *as, uintptr_t addr, int flags) {
|
||||
uintptr_t cur = (uintptr_t)&as->ptr;
|
||||
|
||||
for (int i = 0; i <= mmu.ptlevels; i++) {
|
||||
const struct ptinfo *ptinfo = &mmu.pgtables[i];
|
||||
uintptr_t *pt = (uintptr_t *)cur, next_page;
|
||||
int index = indexof(addr, ptinfo);
|
||||
if (i == mmu.ptlevels) return &pt[index];
|
||||
|
||||
if (!(pt[index] & PTE_P)) {
|
||||
next_page = (uintptr_t)pgallocz();
|
||||
pt[index] = next_page | PTE_P | flags;
|
||||
} else {
|
||||
next_page = baseof(pt[index]);
|
||||
}
|
||||
cur = next_page;
|
||||
}
|
||||
bug();
|
||||
}
|
||||
|
||||
static void teardown(int level, uintptr_t *pt) {
|
||||
if (level > mmu.ptlevels) return;
|
||||
for (int index = 0; index < (1 << mmu.pgtables[level].bits); index++) {
|
||||
if ((pt[index] & PTE_P) && (pt[index] & PTE_U)) {
|
||||
teardown(level + 1, (void *)baseof(pt[index]));
|
||||
}
|
||||
}
|
||||
if (level >= 1) {
|
||||
pgfree(pt);
|
||||
}
|
||||
}
|
||||
|
||||
int _vme_init(void *(*_pgalloc)(size_t size), void (*_pgfree)(void *)) {
|
||||
panic_on(_cpu() != 0, "init VME in non-bootstrap CPU");
|
||||
pgalloc = _pgalloc;
|
||||
pgfree = _pgfree;
|
||||
|
||||
#if __x86_64__
|
||||
kpt = (void *)PML4_ADDR;
|
||||
#else
|
||||
_AddressSpace as;
|
||||
as.ptr = NULL;
|
||||
for (int i = 0; i < LENGTH(vm_areas); i++) {
|
||||
const struct vm_area *vma = &vm_areas[i];
|
||||
if (vma->kernel) {
|
||||
for (uintptr_t cur = (uintptr_t)vma->area.start;
|
||||
cur != (uintptr_t)vma->area.end;
|
||||
cur += mmu.pgsize) {
|
||||
*ptwalk(&as, cur, PTE_W) = cur | PTE_P | PTE_W;
|
||||
}
|
||||
}
|
||||
}
|
||||
kpt = (void *)baseof((uintptr_t)as.ptr);
|
||||
#endif
|
||||
|
||||
set_cr3(kpt);
|
||||
set_cr0(get_cr0() | CR0_PG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _protect(_AddressSpace *as) {
|
||||
uintptr_t *upt = pgallocz();
|
||||
|
||||
for (int i = 0; i < LENGTH(vm_areas); i++) {
|
||||
const struct vm_area *vma = &vm_areas[i];
|
||||
if (vma->kernel) {
|
||||
const struct ptinfo *info = &mmu.pgtables[1]; // level-1 page table
|
||||
for (uintptr_t cur = (uintptr_t)vma->area.start;
|
||||
cur != (uintptr_t)vma->area.end;
|
||||
cur += (1L << info->shift)) {
|
||||
int index = indexof(cur, info);
|
||||
upt[index] = kpt[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
as->pgsize = mmu.pgsize;
|
||||
as->area = uvm_area;
|
||||
as->ptr = (void *)((uintptr_t)upt | PTE_P | PTE_U);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _unprotect(_AddressSpace *as) {
|
||||
teardown(0, (void *)&as->ptr);
|
||||
}
|
||||
|
||||
int _map(_AddressSpace *as, void *va, void *pa, int prot) {
|
||||
panic_on(!IN_RANGE(va, uvm_area), "mapping an invalid address");
|
||||
panic_on((prot & _PROT_NONE) && (prot != _PROT_NONE), "invalid protection flags");
|
||||
panic_on((uintptr_t)va != ROUNDDOWN(va, mmu.pgsize) ||
|
||||
(uintptr_t)pa != ROUNDDOWN(pa, mmu.pgsize), "non-page-boundary address");
|
||||
|
||||
uintptr_t *ptentry = ptwalk(as, (uintptr_t)va, PTE_W | PTE_U);
|
||||
if (prot & _PROT_NONE) {
|
||||
panic_on(!(*ptentry & PTE_P), "unmapping a non-mapped page");
|
||||
*ptentry = 0;
|
||||
} else {
|
||||
panic_on(*ptentry & PTE_P, "remapping a mapped page");
|
||||
uintptr_t pte = (uintptr_t)pa | PTE_P | PTE_U | ((prot & _PROT_WRITE) ? PTE_W : 0);
|
||||
*ptentry = pte;
|
||||
}
|
||||
ptwalk(as, (uintptr_t)va, PTE_W | PTE_U);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_Context *_ucontext(_AddressSpace *as, _Area kstack, void *entry) {
|
||||
_Context *ctx = (_Context *)kstack.start;
|
||||
*ctx = (_Context) { 0 };
|
||||
|
||||
#if __x86_64__
|
||||
ctx->cs = USEL(SEG_UCODE);
|
||||
ctx->ss = USEL(SEG_UDATA);
|
||||
ctx->rip = (uintptr_t)entry;
|
||||
ctx->rflags = FL_IF;
|
||||
ctx->rsp = (uintptr_t)uvm_area.end;
|
||||
ctx->rsp0 = (uintptr_t)kstack.end;
|
||||
#else
|
||||
ctx->cs = USEL(SEG_UCODE);
|
||||
ctx->ds = USEL(SEG_UDATA);
|
||||
ctx->ss3 = USEL(SEG_UDATA);
|
||||
ctx->eip = (uintptr_t)entry;
|
||||
ctx->eflags = FL_IF;
|
||||
ctx->esp = (uintptr_t)uvm_area.end;
|
||||
ctx->esp0 = (uintptr_t)kstack.end;
|
||||
#endif
|
||||
ctx->uvm = as->ptr;
|
||||
return ctx;
|
||||
}
|
Loading…
Reference in New Issue