take over old x86-qemu

This commit is contained in:
Yanyan Jiang 2020-02-02 05:49:40 +00:00
parent e6aa03d69c
commit 05f7e8b088
36 changed files with 885 additions and 2145 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)();
}
}

View File

@ -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

View File

@ -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));
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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));
}

View File

@ -1,5 +1,4 @@
#include <x86.h>
#include "x86_64-qemu.h"
#include "x86-qemu.h"
.globl _start
_start:

View File

@ -1,5 +1,5 @@
#include <x86.h>
#include "x86_64-qemu.h"
#include "x86-qemu.h"
.code32
.globl _start

View File

@ -1,4 +1,4 @@
#include "x86_64-qemu.h"
#include "x86-qemu.h"
trap:
cli

View File

@ -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

View File

@ -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
}

View File

@ -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);
}

View File

@ -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

View File

@ -1 +0,0 @@
/mbr

View File

@ -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

View File

@ -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;

View File

@ -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)();
}
}

View File

@ -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

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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());
}
}
}

View File

@ -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
}

View File

@ -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;
}