NJU-ProjectN/abstract-machine ics2023 initialized
NJU-ProjectN/abstract-machine 3348db971fd860be5cb28e21c18f9d0e65d0c96a Merge pull request #8 from Jasonyanyusong/master
This commit is contained in:
parent
2824efad33
commit
8e4feb4010
129 changed files with 9017 additions and 0 deletions
67
abstract-machine/am/src/x86/nemu/cte.c
Normal file
67
abstract-machine/am/src/x86/nemu/cte.c
Normal 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) {
|
||||
}
|
8
abstract-machine/am/src/x86/nemu/start.S
Normal file
8
abstract-machine/am/src/x86/nemu/start.S
Normal 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
|
22
abstract-machine/am/src/x86/nemu/trap.S
Normal file
22
abstract-machine/am/src/x86/nemu/trap.S
Normal 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
|
64
abstract-machine/am/src/x86/nemu/vme.c
Normal file
64
abstract-machine/am/src/x86/nemu/vme.c
Normal 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;
|
||||
}
|
8
abstract-machine/am/src/x86/qemu/boot/Makefile
Normal file
8
abstract-machine/am/src/x86/qemu/boot/Makefile
Normal 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
|
14
abstract-machine/am/src/x86/qemu/boot/genboot.py
Normal file
14
abstract-machine/am/src/x86/qemu/boot/genboot.py
Normal 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
|
90
abstract-machine/am/src/x86/qemu/boot/main.c
Normal file
90
abstract-machine/am/src/x86/qemu/boot/main.c
Normal 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)();
|
||||
}
|
||||
}
|
60
abstract-machine/am/src/x86/qemu/boot/start.S
Normal file
60
abstract-machine/am/src/x86/qemu/boot/start.S
Normal 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
|
165
abstract-machine/am/src/x86/qemu/cte.c
Normal file
165
abstract-machine/am/src/x86/qemu/cte.c
Normal 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));
|
||||
}
|
471
abstract-machine/am/src/x86/qemu/ioe.c
Normal file
471
abstract-machine/am/src/x86/qemu/ioe.c
Normal 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);
|
||||
}
|
55
abstract-machine/am/src/x86/qemu/mpe.c
Normal file
55
abstract-machine/am/src/x86/qemu/mpe.c
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
7
abstract-machine/am/src/x86/qemu/start32.S
Normal file
7
abstract-machine/am/src/x86/qemu/start32.S
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "x86-qemu.h"
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
pushl $MAINARG_ADDR
|
||||
pushl $0
|
||||
jmp _start_c
|
69
abstract-machine/am/src/x86/qemu/start64.S
Normal file
69
abstract-machine/am/src/x86/qemu/start64.S
Normal 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:
|
99
abstract-machine/am/src/x86/qemu/trap32.S
Normal file
99
abstract-machine/am/src/x86/qemu/trap32.S
Normal 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;
|
61
abstract-machine/am/src/x86/qemu/trap64.S
Normal file
61
abstract-machine/am/src/x86/qemu/trap64.S
Normal 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;
|
112
abstract-machine/am/src/x86/qemu/trm.c
Normal file
112
abstract-machine/am/src/x86/qemu/trm.c
Normal 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
|
||||
}
|
181
abstract-machine/am/src/x86/qemu/vme.c
Normal file
181
abstract-machine/am/src/x86/qemu/vme.c
Normal 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;
|
||||
}
|
100
abstract-machine/am/src/x86/qemu/x86-qemu.h
Normal file
100
abstract-machine/am/src/x86/qemu/x86-qemu.h
Normal 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
|
353
abstract-machine/am/src/x86/x86.h
Normal file
353
abstract-machine/am/src/x86/x86.h
Normal 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__
|
Loading…
Add table
Add a link
Reference in a new issue