NJU-ProjectN/abstract-machine ics2023 initialized

NJU-ProjectN/abstract-machine 3348db971fd860be5cb28e21c18f9d0e65d0c96a Merge pull request #8 from Jasonyanyusong/master
This commit is contained in:
xinyangli 2023-12-21 00:20:42 +08:00
parent 2824efad33
commit 8e4feb4010
129 changed files with 9017 additions and 0 deletions

View file

@ -0,0 +1,67 @@
#include <am.h>
#include <x86/x86.h>
#include <klib.h>
#define NR_IRQ 256 // IDT size
#define SEG_KCODE 1
#define SEG_KDATA 2
static Context* (*user_handler)(Event, Context*) = NULL;
void __am_irq0();
void __am_vecsys();
void __am_vectrap();
void __am_vecnull();
Context* __am_irq_handle(Context *c) {
if (user_handler) {
Event ev = {0};
switch (c->irq) {
default: ev.event = EVENT_ERROR; break;
}
c = user_handler(ev, c);
assert(c != NULL);
}
return c;
}
bool cte_init(Context*(*handler)(Event, Context*)) {
static GateDesc32 idt[NR_IRQ];
// initialize IDT
for (unsigned int i = 0; i < NR_IRQ; i ++) {
idt[i] = GATE32(STS_TG, KSEL(SEG_KCODE), __am_vecnull, DPL_KERN);
}
// ----------------------- interrupts ----------------------------
idt[32] = GATE32(STS_IG, KSEL(SEG_KCODE), __am_irq0, DPL_KERN);
// ---------------------- system call ----------------------------
idt[0x80] = GATE32(STS_TG, KSEL(SEG_KCODE), __am_vecsys, DPL_USER);
idt[0x81] = GATE32(STS_TG, KSEL(SEG_KCODE), __am_vectrap, DPL_KERN);
set_idt(idt, sizeof(idt));
// register event handler
user_handler = handler;
return true;
}
Context* kcontext(Area kstack, void (*entry)(void *), void *arg) {
return NULL;
}
void yield() {
asm volatile("int $0x81");
}
bool ienabled() {
return false;
}
void iset(bool enable) {
}

View file

@ -0,0 +1,8 @@
.section entry, "ax"
.globl _start
.type _start, @function
_start:
mov $0, %ebp
mov $_stack_pointer, %esp
call _trm_init # never return

View file

@ -0,0 +1,22 @@
#----|------------entry------------|---irq id---|-----handler-----|
.globl __am_vecsys; __am_vecsys: pushl $0x80; jmp __am_asm_trap
.globl __am_vectrap; __am_vectrap: pushl $0x81; jmp __am_asm_trap
.globl __am_irq0; __am_irq0: pushl $32; jmp __am_asm_trap
.globl __am_vecnull; __am_vecnull: pushl $-1; jmp __am_asm_trap
__am_asm_trap:
pushal
pushl $0
pushl %esp
call __am_irq_handle
addl $4, %esp
addl $4, %esp
popal
addl $4, %esp
iret

View file

@ -0,0 +1,64 @@
#include <am.h>
#include <nemu.h>
#include <klib.h>
static AddrSpace kas = {};
static void* (*pgalloc_usr)(int) = NULL;
static void (*pgfree_usr)(void*) = NULL;
static int vme_enable = 0;
static Area segments[] = { // Kernel memory mappings
NEMU_PADDR_SPACE
};
#define USER_SPACE RANGE(0x40000000, 0xc0000000)
bool vme_init(void* (*pgalloc_f)(int), void (*pgfree_f)(void*)) {
pgalloc_usr = pgalloc_f;
pgfree_usr = pgfree_f;
kas.ptr = pgalloc_f(PGSIZE);
int i;
for (i = 0; i < LENGTH(segments); i ++) {
void *va = segments[i].start;
for (; va < segments[i].end; va += PGSIZE) {
map(&kas, va, va, 0);
}
}
set_cr3(kas.ptr);
set_cr0(get_cr0() | CR0_PG);
vme_enable = 1;
return true;
}
void protect(AddrSpace *as) {
PTE *updir = (PTE*)(pgalloc_usr(PGSIZE));
as->ptr = updir;
as->area = USER_SPACE;
as->pgsize = PGSIZE;
// map kernel space
memcpy(updir, kas.ptr, PGSIZE);
}
void unprotect(AddrSpace *as) {
}
void __am_get_cur_as(Context *c) {
c->cr3 = (vme_enable ? (void *)get_cr3() : NULL);
}
void __am_switch(Context *c) {
if (vme_enable && c->cr3 != NULL) {
set_cr3(c->cr3);
}
}
void map(AddrSpace *as, void *va, void *pa, int prot) {
}
Context* ucontext(AddrSpace *as, Area kstack, void *entry) {
return NULL;
}

View file

@ -0,0 +1,8 @@
SRCS := start.S main.c
bootblock.o: $(SRCS) Makefile
@echo + CC $(SRCS)
@$(CROSS_COMPILE)gcc -static -m32 -fno-pic -Os -nostdlib -Ttext 0x7c00 -I$(AM_HOME)/am/src -o bootblock.o $(SRCS)
@python3 genboot.py bootblock.o
clean:
rm -rf *.o

View file

@ -0,0 +1,14 @@
import os, sys, pathlib, subprocess
f = pathlib.Path(sys.argv[1])
try:
objcopy = os.getenv('CROSS_COMPILE', '') + 'objcopy'
data = subprocess.run(
[objcopy, '-S', '-O', 'binary', '-j', '.text', f, '/dev/stdout'],
capture_output=True).stdout
assert len(data) <= 510
data += b'\0' * (510 - len(data)) + b'\x55\xaa'
f.write_bytes(data)
except:
f.unlink()
raise

View file

@ -0,0 +1,90 @@
#include <stdint.h>
#include <elf.h>
#include <x86/x86.h>
#define SECTSIZE 512
#define ARGSIZE 1024
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 & ~(SECTSIZE - 1);
uint32_t ed = (uint32_t)buf + nbytes;
uint32_t sect = (disk_offset / SECTSIZE) + (ARGSIZE / SECTSIZE) + 1;
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(Elf64_Ehdr *elf) {
Elf64_Phdr *ph = (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(Elf32_Ehdr *elf) {
Elf32_Phdr *ph = (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) {
Elf32_Ehdr *elf32 = (void *)0x8000;
Elf64_Ehdr *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

@ -0,0 +1,60 @@
#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

@ -0,0 +1,165 @@
#include "x86-qemu.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();
void __am_kcontext_start();
void __am_irq_handle(struct trap_frame *tf) {
Context *saved_ctx = &tf->saved_context;
Event ev = {
.event = EVENT_NULL,
.cause = 0, .ref = 0,
.msg = "(no message)",
};
#if __x86_64
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;
#else
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);
// no ss/esp saved for DPL_KERNEL
saved_ctx->esp = (tf->cs & DPL_USER ? tf->esp : (uint32_t)(tf + 1) - 8);
#endif
saved_ctx->cr3 = (void *)get_cr3();
#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 IRQ 4: MSG("I/O device IRQ4 (COM1)")
ev.event = EVENT_IRQ_IODEV; break;
case EX_SYSCALL: MSG("int $0x80 system call")
ev.event = EVENT_SYSCALL; break;
case EX_YIELD: MSG("int $0x81 yield")
ev.event = EVENT_YIELD; 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 |= MMAP_NONE;
if (tf->errcode & 0x2) ev.cause |= MMAP_WRITE;
else ev.cause |= MMAP_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);
panic_on(!ret_ctx, "returning to NULL context");
if (ret_ctx->cr3) {
set_cr3(ret_ctx->cr3);
#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);
}
bool cte_init(Context *(*handler)(Event, Context *)) {
panic_on(cpu_current() != 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 true;
}
void yield() {
interrupt(0x81);
}
bool ienabled() {
return (get_efl() & FL_IF) != 0;
}
void iset(bool enable) {
if (enable) sti();
else cli();
}
void __am_panic_on_return() { panic("kernel context returns"); }
Context* kcontext(Area kstack, void (*entry)(void *), void *arg) {
Context *ctx = kstack.end - sizeof(Context);
*ctx = (Context) { 0 };
#if __x86_64__
ctx->cs = KSEL(SEG_KCODE);
ctx->rip = (uintptr_t)__am_kcontext_start;
ctx->rflags = FL_IF;
ctx->rsp = (uintptr_t)kstack.end;
#else
ctx->ds = KSEL(SEG_KDATA);
ctx->cs = KSEL(SEG_KCODE);
ctx->eip = (uintptr_t)__am_kcontext_start;
ctx->eflags = FL_IF;
ctx->esp = (uintptr_t)kstack.end;
#endif
ctx->GPR1 = (uintptr_t)arg;
ctx->GPR2 = (uintptr_t)entry;
return ctx;
}
void __am_percpu_initirq() {
__am_ioapic_enable(IRQ_KBD, 0);
__am_ioapic_enable(IRQ_COM1, 0);
set_idt(idt, sizeof(idt));
}

View file

@ -0,0 +1,471 @@
#include "x86-qemu.h"
#include <klib.h> // TODO: delete
// UART
// ====================================================
#define COM1 0x3f8
static int uart_init() {
outb(COM1 + 2, 0);
outb(COM1 + 3, 0x80);
outb(COM1 + 0, 115200 / 9600);
outb(COM1 + 1, 0);
outb(COM1 + 3, 0x03);
outb(COM1 + 4, 0);
outb(COM1 + 1, 0x01);
inb (COM1 + 2);
inb (COM1 + 0);
return 0;
}
static void uart_config(AM_UART_CONFIG_T *cfg) {
cfg->present = true;
}
static void uart_tx(AM_UART_TX_T *send) {
outb(COM1, send->data);
}
static void uart_rx(AM_UART_RX_T *recv) {
recv->data = (inb(COM1 + 5) & 0x1) ? inb(COM1) : -1;
}
// Timer
// ====================================================
static AM_TIMER_RTC_T boot_date;
static uint32_t freq_mhz = 2000;
static uint64_t uptsc;
static void timer_rtc(AM_TIMER_RTC_T *rtc);
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(AM_TIMER_RTC_T *rtc) {
*rtc = (AM_TIMER_RTC_T) {
.second = read_rtc(0),
.minute = read_rtc(2),
.hour = read_rtc(4),
.day = read_rtc(7),
.month = read_rtc(8),
.year = read_rtc(9) + 2000,
};
}
static void wait_sec(AM_TIMER_RTC_T *t1) {
AM_TIMER_RTC_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() {
AM_TIMER_RTC_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 timer_init() {
freq_mhz = estimate_freq();
timer_rtc(&boot_date);
uptsc = rdtsc();
}
static void timer_config(AM_TIMER_CONFIG_T *cfg) {
cfg->present = cfg->has_rtc = true;
}
static void timer_rtc(AM_TIMER_RTC_T *rtc) {
int tmp;
do {
read_rtc_async(rtc);
tmp = read_rtc(0);
} while (tmp != rtc->second);
}
static void timer_uptime(AM_TIMER_UPTIME_T *upt) {
upt->us = (rdtsc() - uptsc) / freq_mhz;
}
// Input
// ====================================================
static int keylut[128] = {
[0x01] = AM_KEY_ESCAPE, [0x02] = AM_KEY_1, [0x03] = AM_KEY_2,
[0x04] = AM_KEY_3, [0x05] = AM_KEY_4, [0x06] = AM_KEY_5, [0x07] = AM_KEY_6,
[0x08] = AM_KEY_7, [0x09] = AM_KEY_8, [0x0a] = AM_KEY_9, [0x0b] = AM_KEY_0,
[0x0c] = AM_KEY_MINUS, [0x0d] = AM_KEY_EQUALS,
[0x0e] = AM_KEY_BACKSPACE, [0x0f] = AM_KEY_TAB,
[0x10] = AM_KEY_Q, [0x11] = AM_KEY_W, [0x12] = AM_KEY_E, [0x13] = AM_KEY_R,
[0x14] = AM_KEY_T, [0x15] = AM_KEY_Y, [0x16] = AM_KEY_U, [0x17] = AM_KEY_I,
[0x18] = AM_KEY_O, [0x19] = AM_KEY_P, [0x1a] = AM_KEY_LEFTBRACKET,
[0x1b] = AM_KEY_RIGHTBRACKET, [0x1c] = AM_KEY_RETURN,
[0x1d] = AM_KEY_LCTRL, [0x1e] = AM_KEY_A, [0x1f] = AM_KEY_S,
[0x20] = AM_KEY_D, [0x21] = AM_KEY_F, [0x22] = AM_KEY_G, [0x23] = AM_KEY_H,
[0x24] = AM_KEY_J, [0x25] = AM_KEY_K, [0x26] = AM_KEY_L,
[0x27] = AM_KEY_SEMICOLON, [0x28] = AM_KEY_APOSTROPHE,
[0x29] = AM_KEY_GRAVE, [0x2a] = AM_KEY_LSHIFT,
[0x2b] = AM_KEY_BACKSLASH, [0x2c] = AM_KEY_Z, [0x2d] = AM_KEY_X,
[0x2e] = AM_KEY_C, [0x2f] = AM_KEY_V, [0x30] = AM_KEY_B, [0x31] = AM_KEY_N,
[0x32] = AM_KEY_M, [0x33] = AM_KEY_COMMA, [0x34] = AM_KEY_PERIOD,
[0x35] = AM_KEY_SLASH, [0x36] = AM_KEY_RSHIFT, [0x38] = AM_KEY_LALT,
[0x38] = AM_KEY_RALT, [0x39] = AM_KEY_SPACE, [0x3a] = AM_KEY_CAPSLOCK,
[0x3b] = AM_KEY_F1, [0x3c] = AM_KEY_F2, [0x3d] = AM_KEY_F3,
[0x3e] = AM_KEY_F4, [0x3f] = AM_KEY_F5, [0x40] = AM_KEY_F6,
[0x41] = AM_KEY_F7, [0x42] = AM_KEY_F8, [0x43] = AM_KEY_F9,
[0x44] = AM_KEY_F10, [0x48] = AM_KEY_INSERT,
[0x4b] = AM_KEY_HOME, [0x4d] = AM_KEY_END, [0x50] = AM_KEY_DELETE,
[0x57] = AM_KEY_F11, [0x58] = AM_KEY_F12, [0x5b] = AM_KEY_APPLICATION,
};
static void input_config(AM_INPUT_CONFIG_T *cfg) {
cfg->present = true;
}
static void input_keybrd(AM_INPUT_KEYBRD_T *ev) {
if (inb(0x64) & 0x1) {
int code = inb(0x60) & 0xff;
ev->keydown = code < 128;
ev->keycode = keylut[code & 0x7f];
} else {
ev->keydown = false;
ev->keycode = AM_KEY_NONE;
}
}
// GPU (Frame Buffer and 2D Accelerated Graphics)
// ====================================================
#define VMEM_SIZE (512 << 10)
struct vbe_info {
uint8_t ignore[18];
uint16_t width;
uint16_t height;
uint8_t ignore1[18];
uint32_t framebuffer;
} __attribute__ ((packed));
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));
static struct pixel *fb;
static uint8_t vmem[VMEM_SIZE], vbuf[VMEM_SIZE], *vbuf_head;
static struct gpu_canvas display;
static inline void *to_host(gpuptr_t ptr) { return ptr == AM_GPU_NULL ? NULL : vmem + ptr; }
static void gpu_init() {
struct vbe_info *info = (struct vbe_info *)0x00004000;
display.w = info->width;
display.h = info->height;
fb = (void *)((intptr_t)(info->framebuffer));
}
static void gpu_config(AM_GPU_CONFIG_T *cfg) {
*cfg = (AM_GPU_CONFIG_T) {
.present = true,
.width = display.w, .height = display.h,
.vmemsz = sizeof(vmem),
};
}
static void gpu_fbdraw(AM_GPU_FBDRAW_T *draw) {
int x = draw->x, y = draw->y, w = draw->w, h = draw->h;
int W = display.w, H = display.h;
uint32_t *pixels = draw->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 = (struct pixel) { .r = R(p), .g = G(p), .b = B(p) };
}
}
}
}
static void gpu_status(AM_GPU_STATUS_T *stat) {
stat->ready = true;
}
static void gpu_memcpy(AM_GPU_MEMCPY_T *params) {
char *src = params->src, *dst = to_host(params->dest);
for (int i = 0; i < params->size; i++)
dst[i] = src[i];
}
static void *vbuf_alloc(int size) {
void *ret = vbuf_head;
vbuf_head += size;
panic_on(vbuf_head > vbuf + sizeof(vbuf), "no memory");
for (int i = 0; i < size; i++)
((char *)ret)[i] = 0;
return ret;
}
static struct pixel *render(struct gpu_canvas *cv, struct gpu_canvas *parent, struct pixel *px) {
struct pixel *px_local;
int W = parent->w, w, h;
switch (cv->type) {
case AM_GPU_TEXTURE: {
w = cv->texture.w; h = cv->texture.h;
px_local = to_host(cv->texture.pixels);
break;
}
case AM_GPU_SUBTREE: {
w = cv->w; h = cv->h;
px_local = vbuf_alloc(w * h * sizeof(struct pixel));
for (struct gpu_canvas *ch = to_host(cv->child); ch; ch = to_host(ch->sibling)) {
render(ch, cv, px_local);
}
break;
}
default:
panic("invalid node");
}
// draw local canvas (w * h) -> px (x1, y1) - (x1 + w1, y1 + h1)
for (int i = 0; i < cv->w1; i++)
for (int j = 0; j < cv->h1; j++) {
int x = cv->x1 + i, y = cv->y1 + j;
px[W * y + x] = px_local[w * (j * h / cv->h1) + (i * w / cv->w1)];
}
return 0;
}
static void gpu_render(AM_GPU_RENDER_T *ren) {
vbuf_head = vbuf;
render(to_host(ren->root), &display, fb);
}
// Disk (ATA0)
// ====================================================
#define BLKSZ 512
#define DISKSZ (64 << 20)
static void disk_config(AM_DISK_CONFIG_T *cfg) {
cfg->present = true;
cfg->blksz = BLKSZ;
cfg->blkcnt = DISKSZ / BLKSZ;
}
static void disk_status(AM_DISK_STATUS_T *status) {
status->ready = true;
}
static inline void wait_disk(void) {
while ((inb(0x1f7) & 0xc0) != 0x40);
}
static void disk_blkio(AM_DISK_BLKIO_T *bio) {
uint32_t blkno = bio->blkno, remain = bio->blkcnt;
uint32_t *ptr = bio->buf;
for (remain = bio->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, bio->write? 0x30 : 0x20);
wait_disk();
if (bio->write) {
for (int i = 0; i < BLKSZ / 4; i ++)
outl(0x1f0, *ptr++);
} else {
for (int i = 0; i < BLKSZ / 4; i ++)
*ptr++ = inl(0x1f0);
}
}
}
// ====================================================
static void audio_config(AM_AUDIO_CONFIG_T *cfg) { cfg->present = false; }
static void net_config(AM_NET_CONFIG_T *cfg) { cfg->present = false; }
static void fail(void *buf) { panic("access nonexist register"); }
typedef void (*handler_t)(void *buf);
static void *lut[128] = {
[AM_UART_CONFIG ] = uart_config,
[AM_UART_TX ] = uart_tx,
[AM_UART_RX ] = uart_rx,
[AM_TIMER_CONFIG] = timer_config,
[AM_TIMER_RTC ] = timer_rtc,
[AM_TIMER_UPTIME] = timer_uptime,
[AM_INPUT_CONFIG] = input_config,
[AM_INPUT_KEYBRD] = input_keybrd,
[AM_GPU_CONFIG ] = gpu_config,
[AM_GPU_FBDRAW ] = gpu_fbdraw,
[AM_GPU_STATUS ] = gpu_status,
[AM_GPU_MEMCPY ] = gpu_memcpy,
[AM_GPU_RENDER ] = gpu_render,
[AM_AUDIO_CONFIG] = audio_config,
[AM_DISK_CONFIG ] = disk_config,
[AM_DISK_STATUS ] = disk_status,
[AM_DISK_BLKIO ] = disk_blkio,
[AM_NET_CONFIG ] = net_config,
};
bool ioe_init() {
panic_on(cpu_current() != 0, "init IOE in non-bootstrap CPU");
for (int i = 0; i < LENGTH(lut); i++)
if (!lut[i]) lut[i] = fail;
uart_init();
timer_init();
gpu_init();
return true;
}
void ioe_read (int reg, void *buf) { ((handler_t)lut[reg])(buf); }
void ioe_write(int reg, void *buf) { ((handler_t)lut[reg])(buf); }
// 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

@ -0,0 +1,55 @@
#include "x86-qemu.h"
struct cpu_local __am_cpuinfo[MAX_CPU] = {};
static void (* volatile user_entry)();
static int ap_ready = 0;
static void call_user_entry() {
user_entry();
panic("MPE entry should not return");
}
bool 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 (xchg(&ap_ready, 0) != 1) {
pause();
}
}
call_user_entry();
return true;
}
static void othercpu_entry() {
__am_percpu_init();
xchg(&ap_ready, 1);
call_user_entry();
}
void __am_othercpu_entry() {
stack_switch_call(stack_top(&CPU->stack), othercpu_entry, 0);
}
int cpu_count() {
return __am_ncpu;
}
int cpu_current(void) {
return __am_lapic[8] >> 24;
}
int atomic_xchg(int *addr, int 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_current()) {
__am_lapic_bootap(cpu_, (void *)boot_record());
}
}
}

View file

@ -0,0 +1,7 @@
#include "x86-qemu.h"
.globl _start
_start:
pushl $MAINARG_ADDR
pushl $0
jmp _start_c

View file

@ -0,0 +1,69 @@
#include <x86/x86.h>
#include "x86-qemu.h"
.code32
.globl _start
_start:
movl $(PDPT_ADDR | PTE_P | PTE_W), %eax
cmpl (PML4_ADDR), %eax
je .long_mode_init
movl $(PDPT_ADDR | PTE_P | PTE_W), %eax
movl %eax, (PML4_ADDR)
movl $0, %ecx
movl $512, %esi // 512 pages
// |
.loop: // x
movl %ecx, %eax // |
shll $30, %eax // |
orl $(PTE_P | PTE_W | PTE_PS), %eax // 1 GiB page
movl %eax, PDPT_ADDR(, %ecx, 8)
movl %ecx, %eax
shrl $2, %eax
movl %eax, PDPT_ADDR + 4(, %ecx, 8)
inc %ecx
cmp %esi, %ecx
jne .loop
.long_mode_init:
movl $PML4_ADDR, %eax
movl %eax, %cr3 // %cr3 = PML4 base
movl $CR4_PAE, %eax
movl %eax, %cr4 // %cr4.PAE = 1
movl $0xc0000080, %ecx
rdmsr
orl $0x100, %eax
wrmsr // %EFER.LME = 1
movl %cr0, %eax
orl $CR0_PG, %eax
movl %eax, %cr0 // %cr0.PG = 1
lgdt gdt_ptr // bootstrap GDT
ljmp $8, $_start64 // should not return
.code64
_start64:
movw $0, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw %ax, %fs
movw %ax, %gs
movq $MAINARG_ADDR, %rdi
pushq $0
jmp _start_c
.align 16
gdt_ptr:
.word gdt64_end - gdt64_begin - 1
.quad gdt64_begin
gdt64_begin:
.long 0x00000000 // 0: null desc
.long 0x00000000
.long 0x00000000 // 1: code
.long 0x00209800
gdt64_end:

View file

@ -0,0 +1,99 @@
#include "x86-qemu.h"
.globl __am_kcontext_start
__am_kcontext_start:
// eax = arg, ebx = entry
pushl %eax
pushl $__am_panic_on_return
jmpl *%ebx
trap:
cli
subl $20, %esp
pushl %ebp
pushl %edi
pushl %esi
pushl $0
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movw %ds, %ax
pushl %eax
pushl $0
movw $KSEL(SEG_KDATA), %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
pushl %esp
call __am_irq_handle
.globl __am_iret
__am_iret:
addl $4, %esp
popl %eax
movl %eax, %esp
addl $4, %esp
popl %eax
movw %ax, %ds
movw %ax, %es
cmpw $KSEL(SEG_KCODE), 36(%esp)
je .kernel_iret
.user_iret:
popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp
popl %esi
popl %edi
popl %ebp
iret
.kernel_iret:
popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp
/* stack frame:
28 ss
24 esp (not popped by iret when returning to ring0)
20 eflags ---> move to new-esp
16 cs
12 eip
8 ebp
4 edi
0 esi <--- %esp
*/
movl %esp, %ebp
movl 24(%ebp), %edi // %edi is new-esp
movl 20(%ebp), %esi; movl %esi, -4(%edi)
movl 16(%ebp), %esi; movl %esi, -8(%edi)
movl 12(%ebp), %esi; movl %esi, -12(%edi)
movl 8(%ebp), %esi; movl %esi, -16(%edi)
movl 4(%ebp), %esi; movl %esi, -20(%edi)
movl 0(%ebp), %esi; movl %esi, -24(%edi)
leal -24(%edi), %esp
popl %esi
popl %edi
popl %ebp
iret
#define NOERR push $0
#define ERR
#define IRQ_DEF(id, dpl, err) \
.globl __am_irq##id; __am_irq##id: cli; err; push $id; jmp trap;
IRQS(IRQ_DEF)
.globl __am_irqall; __am_irqall: cli; push $0; push $-1; jmp trap;

View file

@ -0,0 +1,61 @@
#include "x86-qemu.h"
.globl __am_kcontext_start
__am_kcontext_start:
// rdi = arg, rsi = entry
pushq $__am_panic_on_return
jmpq *%rsi
trap:
cli
subq $48, %rsp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %r11
pushq %r10
pushq %r9
pushq %r8
pushq %rdi
pushq %rsi
pushq %rbp
pushq %rdx
pushq %rcx
pushq %rbx
pushq %rax
pushq $0 // cr3, saved in __am_irq_handle
movq %rsp, %rdi
call __am_irq_handle
.globl __am_iret
__am_iret:
movq %rdi, %rsp
movq 160(%rsp), %rax
movw %ax, %ds
movw %ax, %es
addq $8, %rsp
popq %rax
popq %rbx
popq %rcx
popq %rdx
popq %rbp
popq %rsi
popq %rdi
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
popq %r15
iretq
#define NOERR push $0
#define ERR
#define IRQ_DEF(id, dpl, err) \
.globl __am_irq##id; __am_irq##id: cli; err; push $id; jmp trap;
IRQS(IRQ_DEF)
.globl __am_irqall; __am_irqall: cli; push $0; push $-1; jmp trap;

View file

@ -0,0 +1,112 @@
#include "x86-qemu.h"
Area heap = {};
int __am_ncpu = 0;
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 putch(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 '$':
putch(hex[cpu_current()]);
break;
case '0': case '4':
putch(hex[(code >> (ch - '0')) & 0xf]);
break;
default:
putch(ch);
}
}
outw(0x604, 0x2000); // offer of qemu :)
while (1) hlt();
}
Area __am_heap_init() {
extern char end;
outb(0x70, 0x34);
uint32_t lo = inb(0x71);
outb(0x70, 0x35);
uint32_t hi = inb(0x71) + 1;
return RANGE(ROUNDUP(&end, 1 << 20), (uintptr_t)((lo | hi << 8) << 16));
}
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 = (void *)((uintptr_t)(mpconf_ptr));
__am_lapic = (void *)((uintptr_t)(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

@ -0,0 +1,181 @@
#include "x86-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)(int 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(AddrSpace *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);
}
}
bool vme_init(void *(*_pgalloc)(int size), void (*_pgfree)(void *)) {
panic_on(cpu_current() != 0, "init VME in non-bootstrap CPU");
pgalloc = _pgalloc;
pgfree = _pgfree;
#if __x86_64__
kpt = (void *)PML4_ADDR;
#else
AddrSpace 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 true;
}
void protect(AddrSpace *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);
}
void unprotect(AddrSpace *as) {
teardown(0, (void *)&as->ptr);
}
void map(AddrSpace *as, void *va, void *pa, int prot) {
panic_on(!IN_RANGE(va, uvm_area), "mapping an invalid address");
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 == MMAP_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 & MMAP_WRITE) ? PTE_W : 0);
*ptentry = pte;
}
ptwalk(as, (uintptr_t)va, PTE_W | PTE_U);
}
Context *ucontext(AddrSpace *as, Area kstack, void *entry) {
Context *ctx = kstack.end - sizeof(Context);
*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->cr3 = as->ptr;
return ctx;
}

View file

@ -0,0 +1,100 @@
#include <x86/x86.h>
#define PML4_ADDR 0x1000
#define PDPT_ADDR 0x2000
#define NR_SEG 6 // GDT size
#define SEG_KCODE 1 // Kernel code
#define SEG_KDATA 2 // Kernel data/stack
#define SEG_UCODE 3 // User code
#define SEG_UDATA 4 // User data/stack
#define SEG_TSS 5 // Global unique task state segement
#define NR_IRQ 256 // IDT size
#ifndef __ASSEMBLER__
#include <am.h>
#include <klib-macros.h>
struct kernel_stack {
uint8_t stack[8192];
};
static inline void *stack_top(struct kernel_stack *stk) {
return stk->stack + sizeof(stk->stack);
}
struct mmu_config {
int ptlevels, pgsize;
struct ptinfo {
const char *name;
uintptr_t mask;
int shift, bits;
} pgtables[];
};
struct vm_area {
Area area;
int kernel;
};
void __am_iret(Context *ctx);
struct cpu_local {
AddrSpace *uvm;
#if __x86_64__
SegDesc gdt[NR_SEG + 1];
TSS64 tss;
#else
SegDesc gdt[NR_SEG];
TSS32 tss;
#endif
struct kernel_stack stack;
};
#if __x86_64__
struct trap_frame {
Context saved_context;
uint64_t irq, errcode;
uint64_t rip, cs, rflags, rsp, ss;
};
#else
struct trap_frame {
Context saved_context;
uint32_t irq, errcode;
uint32_t eip, cs, eflags, esp, ss;
};
#endif
extern volatile uint32_t *__am_lapic;
extern int __am_ncpu;
extern struct cpu_local __am_cpuinfo[MAX_CPU];
#define CPU (&__am_cpuinfo[cpu_current()])
#define bug_on(cond) \
do { \
if (cond) panic("internal error (likely a bug in AM)"); \
} while (0)
#define bug() bug_on(1)
// apic utils
void __am_lapic_eoi();
void __am_ioapic_init();
void __am_lapic_bootap(uint32_t cpu, void *address);
void __am_ioapic_enable(int irq, int cpu);
// x86-specific operations
void __am_bootcpu_init();
void __am_percpu_init();
Area __am_heap_init();
void __am_lapic_init();
void __am_othercpu_entry();
void __am_percpu_initirq();
void __am_percpu_initgdt();
void __am_percpu_initlapic();
void __am_stop_the_world();
#endif

View file

@ -0,0 +1,353 @@
// CPU rings
#define DPL_KERN 0x0 // Kernel (ring 0)
#define DPL_USER 0x3 // User (ring 3)
// Application Segment type bits
#define STA_X 0x8 // Executable segment
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
// System Segment type bits
#define STS_T32A 0x9 // Available 32-bit TSS
#define STS_IG 0xe // 32/64-bit Interrupt Gate
#define STS_TG 0xf // 32/64-bit Trap Gate
// EFLAGS register
#define FL_IF 0x00000200 // Interrupt Enable
// Control Register flags
#define CR0_PE 0x00000001 // Protection Enable
#define CR0_PG 0x80000000 // Paging
#define CR4_PAE 0x00000020 // Physical Address Extension
// Page table/directory entry flags
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PS 0x080 // Large Page (1 GiB or 2 MiB)
// GDT selectors
#define KSEL(seg) (((seg) << 3) | DPL_KERN)
#define USEL(seg) (((seg) << 3) | DPL_USER)
// Interrupts and exceptions
#define T_IRQ0 32
#define IRQ_TIMER 0
#define IRQ_KBD 1
#define IRQ_COM1 4
#define IRQ_ERROR 19
#define IRQ_SPURIOUS 31
#define EX_DE 0
#define EX_UD 6
#define EX_NM 7
#define EX_DF 8
#define EX_TS 10
#define EX_NP 11
#define EX_SS 12
#define EX_GP 13
#define EX_PF 14
#define EX_MF 15
#define EX_SYSCALL 0x80
#define EX_YIELD 0x81
// List of interrupts and exceptions (#irq, DPL, hardware errorcode)
#define IRQS(_) \
_( 0, KERN, NOERR) \
_( 1, KERN, NOERR) \
_( 2, KERN, NOERR) \
_( 3, KERN, NOERR) \
_( 4, KERN, NOERR) \
_( 5, KERN, NOERR) \
_( 6, KERN, NOERR) \
_( 7, KERN, NOERR) \
_( 8, KERN, ERR) \
_( 9, KERN, NOERR) \
_( 10, KERN, ERR) \
_( 11, KERN, ERR) \
_( 12, KERN, ERR) \
_( 13, KERN, ERR) \
_( 14, KERN, ERR) \
_( 15, KERN, NOERR) \
_( 16, KERN, NOERR) \
_( 19, KERN, NOERR) \
_( 31, KERN, NOERR) \
_( 32, KERN, NOERR) \
_( 33, KERN, NOERR) \
_( 34, KERN, NOERR) \
_( 35, KERN, NOERR) \
_( 36, KERN, NOERR) \
_( 37, KERN, NOERR) \
_( 38, KERN, NOERR) \
_( 39, KERN, NOERR) \
_( 40, KERN, NOERR) \
_( 41, KERN, NOERR) \
_( 42, KERN, NOERR) \
_( 43, KERN, NOERR) \
_( 44, KERN, NOERR) \
_( 45, KERN, NOERR) \
_( 46, KERN, NOERR) \
_( 47, KERN, NOERR) \
_(128, USER, NOERR) \
_(129, USER, NOERR)
// AM-specific configurations
#define MAX_CPU 8
#define BOOTREC_ADDR 0x07000
#define MAINARG_ADDR 0x10000
// Below are only visible to c/c++ files
#ifndef __ASSEMBLER__
#include <stdint.h>
// Segment Descriptor
typedef struct {
uint32_t lim_15_0 : 16; // Low bits of segment limit
uint32_t base_15_0 : 16; // Low bits of segment base address
uint32_t base_23_16 : 8; // Middle bits of segment base address
uint32_t type : 4; // Segment type (see STS_ constants)
uint32_t s : 1; // 0 = system, 1 = application
uint32_t dpl : 2; // Descriptor Privilege Level
uint32_t p : 1; // Present
uint32_t lim_19_16 : 4; // High bits of segment limit
uint32_t avl : 1; // Unused (available for software use)
uint32_t l : 1; // 64-bit segment
uint32_t db : 1; // 32-bit segment
uint32_t g : 1; // Granularity: limit scaled by 4K when set
uint32_t base_31_24 : 8; // High bits of segment base address
} SegDesc;
// Gate descriptors for interrupts and traps
typedef struct {
uint32_t off_15_0 : 16; // Low 16 bits of offset in segment
uint32_t cs : 16; // Code segment selector
uint32_t args : 5; // # args, 0 for interrupt/trap gates
uint32_t rsv1 : 3; // Reserved(should be zero I guess)
uint32_t type : 4; // Type(STS_{TG,IG32,TG32})
uint32_t s : 1; // Must be 0 (system)
uint32_t dpl : 2; // Descriptor(meaning new) privilege level
uint32_t p : 1; // Present
uint32_t off_31_16 : 16; // High bits of offset in segment
} GateDesc32;
typedef struct {
uint32_t off_15_0 : 16;
uint32_t cs : 16;
uint32_t isv : 3;
uint32_t zero1 : 5;
uint32_t type : 4;
uint32_t zero2 : 1;
uint32_t dpl : 2;
uint32_t p : 1;
uint32_t off_31_16 : 16;
uint32_t off_63_32 : 32;
uint32_t rsv : 32;
} GateDesc64;
// Task State Segment (TSS)
typedef struct {
uint32_t link; // Unused
uint32_t esp0; // Stack pointers and segment selectors
uint32_t ss0; // after an increase in privilege level
uint32_t padding[23];
} __attribute__((packed)) TSS32;
typedef struct {
uint32_t rsv;
uint64_t rsp0, rsp1, rsp2;
uint32_t padding[19];
} __attribute__((packed)) TSS64;
// Multiprocesor configuration
typedef struct { // configuration table header
uint8_t signature[4]; // "PCMP"
uint16_t length; // total table length
uint8_t version; // [14]
uint8_t checksum; // all bytes must add up to 0
uint8_t product[20]; // product id
uint32_t oemtable; // OEM table pointer
uint16_t oemlength; // OEM table length
uint16_t entry; // entry count
uint32_t lapicaddr; // address of local APIC
uint16_t xlength; // extended table length
uint8_t xchecksum; // extended table checksum
uint8_t reserved;
} MPConf;
typedef struct {
int magic;
uint32_t conf; // MP config table addr
uint8_t length; // 1
uint8_t specrev; // [14]
uint8_t checksum; // all bytes add to 0
uint8_t type; // config type
uint8_t imcrp;
uint8_t reserved[3];
} MPDesc;
typedef struct {
uint32_t jmp_code;
int32_t is_ap;
} BootRecord;
#define SEG16(type, base, lim, dpl) (SegDesc) \
{ (lim) & 0xffff, (uintptr_t)(base) & 0xffff, \
((uintptr_t)(base) >> 16) & 0xff, type, 0, dpl, 1, \
(uintptr_t)(lim) >> 16, 0, 0, 1, 0, (uintptr_t)(base) >> 24 }
#define SEG32(type, base, lim, dpl) (SegDesc) \
{ ((lim) >> 12) & 0xffff, (uintptr_t)(base) & 0xffff, \
((uintptr_t)(base) >> 16) & 0xff, type, 1, dpl, 1, \
(uintptr_t)(lim) >> 28, 0, 0, 1, 1, (uintptr_t)(base) >> 24 }
#define SEG64(type, dpl) (SegDesc) \
{ 0, 0, 0, type, 1, dpl, 1, 0, 0, 1, 0, 0 }
#define SEGTSS64(type, base, lim, dpl) (SegDesc) \
{ (lim) & 0xffff, (uint32_t)(base) & 0xffff, \
((uint32_t)(base) >> 16) & 0xff, type, 0, dpl, 1, \
(uint32_t)(lim) >> 16, 0, 0, 0, 0, (uint32_t)(base) >> 24 }
#define GATE32(type, cs, entry, dpl) (GateDesc32) \
{ (uint32_t)(entry) & 0xffff, (cs), 0, 0, (type), 0, (dpl), \
1, (uint32_t)(entry) >> 16 }
#define GATE64(type, cs, entry, dpl) (GateDesc64) \
{ (uint64_t)(entry) & 0xffff, (cs), 0, 0, (type), 0, (dpl), \
1, ((uint64_t)(entry) >> 16) & 0xffff, (uint64_t)(entry) >> 32, 0 }
// Instruction wrappers
static inline uint8_t inb(int port) {
uint8_t data;
asm volatile ("inb %1, %0" : "=a"(data) : "d"((uint16_t)port));
return data;
}
static inline uint16_t inw(int port) {
uint16_t data;
asm volatile ("inw %1, %0" : "=a"(data) : "d"((uint16_t)port));
return data;
}
static inline uint32_t inl(int port) {
uint32_t data;
asm volatile ("inl %1, %0" : "=a"(data) : "d"((uint16_t)port));
return data;
}
static inline void outb(int port, uint8_t data) {
asm volatile ("outb %%al, %%dx" : : "a"(data), "d"((uint16_t)port));
}
static inline void outw(int port, uint16_t data) {
asm volatile ("outw %%ax, %%dx" : : "a"(data), "d"((uint16_t)port));
}
static inline void outl(int port, uint32_t data) {
asm volatile ("outl %%eax, %%dx" : : "a"(data), "d"((uint16_t)port));
}
static inline void cli() {
asm volatile ("cli");
}
static inline void sti() {
asm volatile ("sti");
}
static inline void hlt() {
asm volatile ("hlt");
}
static inline void pause() {
asm volatile ("pause");
}
static inline uint32_t get_efl() {
volatile uintptr_t efl;
asm volatile ("pushf; pop %0": "=r"(efl));
return efl;
}
static inline uintptr_t get_cr0(void) {
volatile uintptr_t val;
asm volatile ("mov %%cr0, %0" : "=r"(val));
return val;
}
static inline void set_cr0(uintptr_t cr0) {
asm volatile ("mov %0, %%cr0" : : "r"(cr0));
}
static inline void set_idt(void *idt, int size) {
static volatile struct {
int16_t size;
void *idt;
} __attribute__((packed)) data;
data.size = size;
data.idt = idt;
asm volatile ("lidt (%0)" : : "r"(&data));
}
static inline void set_gdt(void *gdt, int size) {
static volatile struct {
int16_t size;
void *gdt;
} __attribute__((packed)) data;
data.size = size;
data.gdt = gdt;
asm volatile ("lgdt (%0)" : : "r"(&data));
}
static inline void set_tr(int selector) {
asm volatile ("ltr %0" : : "r"((uint16_t)selector));
}
static inline uintptr_t get_cr2() {
volatile uintptr_t val;
asm volatile ("mov %%cr2, %0" : "=r"(val));
return val;
}
static inline uintptr_t get_cr3() {
volatile uintptr_t val;
asm volatile ("mov %%cr3, %0" : "=r"(val));
return val;
}
static inline void set_cr3(void *pdir) {
asm volatile ("mov %0, %%cr3" : : "r"(pdir));
}
static inline int xchg(int *addr, int newval) {
int result;
asm volatile ("lock xchg %0, %1":
"+m"(*addr), "=a"(result) : "1"(newval) : "cc", "memory");
return result;
}
static inline uint64_t rdtsc() {
uint32_t lo, hi;
asm volatile ("rdtsc": "=a"(lo), "=d"(hi));
return ((uint64_t)hi << 32) | lo;
}
#define interrupt(id) \
asm volatile ("int $" #id);
static inline void stack_switch_call(void *sp, void *entry, uintptr_t arg) {
asm volatile (
#if __x86_64__
"movq %0, %%rsp; movq %2, %%rdi; jmp *%1" : : "b"((uintptr_t)sp), "d"(entry), "a"(arg)
#else
"movl %0, %%esp; movl %2, 4(%0); jmp *%1" : : "b"((uintptr_t)sp - 8), "d"(entry), "a"(arg)
#endif
);
}
static inline volatile BootRecord *boot_record() {
return (BootRecord *)BOOTREC_ADDR;
}
#endif // __ASSEMBLER__