Lecture 3: running binaries in userspace.
This commit is contained in:
parent
7d1b88ec00
commit
1679518a28
7
Makefile
7
Makefile
@ -24,7 +24,8 @@ LDKERNELFLAGS = --script=script.ld
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
OBJECTS = kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \
|
OBJECTS = kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \
|
||||||
cpu/idt.o cpu/vectors.o lib/mem.o
|
cpu/idt.o cpu/gdt.o cpu/swtch.o cpu/vectors.o lib/mem.o proc.o lib/string.o \
|
||||||
|
fs/fs.o
|
||||||
|
|
||||||
run: image.bin
|
run: image.bin
|
||||||
qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio
|
qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio
|
||||||
@ -76,8 +77,8 @@ debug-nox: image.bin
|
|||||||
-ex "break _start" \
|
-ex "break _start" \
|
||||||
-ex "continue"
|
-ex "continue"
|
||||||
|
|
||||||
fs.img: kernel.bin tools/mkfs
|
fs.img: kernel.bin tools/mkfs user/false user/greet user/div0
|
||||||
tools/mkfs $@ $<
|
tools/mkfs $@ $< user/false user/greet user/div0
|
||||||
|
|
||||||
LDFLAGS=-m elf_i386
|
LDFLAGS=-m elf_i386
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void printk(const char* msg);
|
void printk(const char* msg);
|
||||||
void panic(const char* msg);
|
_Noreturn void panic(const char* msg);
|
||||||
|
|||||||
65
cpu/gdt.c
Normal file
65
cpu/gdt.c
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#include "gdt.h"
|
||||||
|
#include "../lib/string.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct seg_desc_t {
|
||||||
|
uint16_t lim_15_0; // Low bits of segment limit
|
||||||
|
uint16_t base_15_0; // Low bits of segment base address
|
||||||
|
uint8_t base_23_16; // Middle bits of segment base address
|
||||||
|
uint8_t type : 4; // Segment type (see STA_ constants)
|
||||||
|
uint8_t s : 1; // 0 = system, 1 = application
|
||||||
|
uint8_t dpl : 2; // Descriptor Privilege Level
|
||||||
|
uint8_t p : 1; // Present
|
||||||
|
uint8_t lim_19_16 : 4; // High bits of segment limit
|
||||||
|
uint8_t avl : 1; // Unused (available for software use)
|
||||||
|
uint8_t rsv1 : 1; // Reserved
|
||||||
|
uint8_t db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
|
||||||
|
uint8_t g : 1; // Granularity: limit scaled by 4K when set
|
||||||
|
uint8_t base_31_24; // High bits of segment base address
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
#define SEG(type, base, lim, dpl) (struct seg_desc_t) \
|
||||||
|
{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \
|
||||||
|
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||||
|
(uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 }
|
||||||
|
#define SEG16(type, base, lim, dpl) (struct seg_desc_t) \
|
||||||
|
{ (lim) & 0xffff, (uint)(base) & 0xffff, \
|
||||||
|
((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||||
|
(uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 }
|
||||||
|
|
||||||
|
struct seg_desc_t seg_desc[6];
|
||||||
|
|
||||||
|
void init_seg_desc() {
|
||||||
|
seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
|
||||||
|
seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
|
||||||
|
seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, USER_BASE, 0xffffffff - USER_BASE, DPL_USER);
|
||||||
|
seg_desc[SEG_UDATA] = SEG(STA_W, USER_BASE, 0xffffffff - USER_BASE, DPL_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gdt_desc_t {
|
||||||
|
uint16_t size;
|
||||||
|
void* ptr;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
void load_gdt() {
|
||||||
|
init_seg_desc();
|
||||||
|
|
||||||
|
struct gdt_desc_t gdt_desc;
|
||||||
|
gdt_desc.size = sizeof(seg_desc) - 1;
|
||||||
|
gdt_desc.ptr = seg_desc;
|
||||||
|
asm("lgdt %0": : "m"(gdt_desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
void switchuvm(struct taskstate *tss, void* esp) {
|
||||||
|
memset(tss, 0, sizeof(*tss));
|
||||||
|
seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, 0);
|
||||||
|
seg_desc[SEG_TSS].s = 0;
|
||||||
|
tss->ss0 = SEG_KDATA << 3;
|
||||||
|
tss->esp0 = (uint)esp;
|
||||||
|
// setting IOPL=0 in eflags *and* iomb beyond the tss segment limit
|
||||||
|
// forbids I/O instructions (e.g., inb and outb) from user space
|
||||||
|
tss->iomb = (ushort) 0xFFFF;
|
||||||
|
|
||||||
|
asm("ltr %0": : "r"((ushort)(SEG_TSS << 3)));
|
||||||
|
}
|
||||||
24
cpu/idt.c
24
cpu/idt.c
@ -1,5 +1,7 @@
|
|||||||
#include "isr.h"
|
#include "isr.h"
|
||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
|
#include "../syscall.h"
|
||||||
|
#include "../proc.h"
|
||||||
#include "../drivers/port.h"
|
#include "../drivers/port.h"
|
||||||
#include "../console.h"
|
#include "../console.h"
|
||||||
|
|
||||||
@ -47,6 +49,7 @@ void init_idt() {
|
|||||||
for (int i = 0; i < IDT_HANDLERS; i++) {
|
for (int i = 0; i < IDT_HANDLERS; i++) {
|
||||||
set_idt_gate(i, 0, default_handlers[i], 0);
|
set_idt_gate(i, 0, default_handlers[i], 0);
|
||||||
}
|
}
|
||||||
|
set_idt_gate(T_SYSCALL, 1, default_handlers[T_SYSCALL], DPL_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * const exception_messages[] = {
|
const char * const exception_messages[] = {
|
||||||
@ -82,6 +85,26 @@ void register_interrupt_handler(uint8_t i, isr_t handler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void trap(registers_t *r) {
|
void trap(registers_t *r) {
|
||||||
|
if (r->int_no == T_SYSCALL) {
|
||||||
|
switch (r->eax) {
|
||||||
|
case SYS_exit:
|
||||||
|
if (r->ebx == 0) {
|
||||||
|
printk("* success\n");
|
||||||
|
} else {
|
||||||
|
printk("* failure\n");
|
||||||
|
}
|
||||||
|
killproc();
|
||||||
|
case SYS_greet:
|
||||||
|
printk("Hello world!\n");
|
||||||
|
r->eax = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printk("Unknown syscall\n");
|
||||||
|
r->eax = -1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r->int_no < 32) {
|
if (r->int_no < 32) {
|
||||||
const char* msg = "Reserved";
|
const char* msg = "Reserved";
|
||||||
if (r->int_no < ARRLEN(exception_messages)) {
|
if (r->int_no < ARRLEN(exception_messages)) {
|
||||||
@ -90,6 +113,7 @@ void trap(registers_t *r) {
|
|||||||
panic(msg);
|
panic(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle the interrupt in a more modular way */
|
||||||
if (interrupt_handlers[r->int_no] != 0) {
|
if (interrupt_handlers[r->int_no] != 0) {
|
||||||
isr_t handler = interrupt_handlers[r->int_no];
|
isr_t handler = interrupt_handlers[r->int_no];
|
||||||
handler(r);
|
handler(r);
|
||||||
|
|||||||
20
cpu/swtch.S
Normal file
20
cpu/swtch.S
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// swtch(void** oldstack, void* newstack)
|
||||||
|
.global swtch
|
||||||
|
swtch:
|
||||||
|
mov 4(%esp), %eax // eax holds "oldstack"
|
||||||
|
mov 8(%esp), %ecx
|
||||||
|
|
||||||
|
push %ebx
|
||||||
|
push %ebp
|
||||||
|
push %esi
|
||||||
|
push %edi
|
||||||
|
|
||||||
|
mov %esp, (%eax) // save stack ptr to "oldstack"
|
||||||
|
mov %ecx, %esp // use "newstack" as stack ptr
|
||||||
|
|
||||||
|
pop %edi
|
||||||
|
pop %esi
|
||||||
|
pop %ebp
|
||||||
|
pop %ebx
|
||||||
|
|
||||||
|
ret
|
||||||
12
fs/fs.c
Normal file
12
fs/fs.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "fs.h"
|
||||||
|
#include "../lib/string.h"
|
||||||
|
#include "../drivers/ata.h"
|
||||||
|
#include "../console.h"
|
||||||
|
|
||||||
|
int stat(const char* name, struct stat *buf) {
|
||||||
|
panic("stat not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_file(const char* name, void* buf, uint32_t bufsize) {
|
||||||
|
panic("read_file not implemented");
|
||||||
|
}
|
||||||
34
kernel.c
34
kernel.c
@ -1,19 +1,43 @@
|
|||||||
|
asm(".asciz \"kernel start\\n\"");
|
||||||
|
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "drivers/vga.h"
|
|
||||||
#include "drivers/uart.h"
|
|
||||||
#include "drivers/keyboard.h"
|
|
||||||
#include "cpu/isr.h"
|
#include "cpu/isr.h"
|
||||||
|
#include "cpu/gdt.h"
|
||||||
|
#include "drivers/keyboard.h"
|
||||||
|
#include "drivers/vga.h"
|
||||||
|
#include "drivers/ata.h"
|
||||||
|
#include "drivers/misc.h"
|
||||||
|
#include "drivers/uart.h"
|
||||||
|
#include "fs/fs.h"
|
||||||
|
#include "lib/string.h"
|
||||||
|
#include "proc.h"
|
||||||
|
|
||||||
|
|
||||||
void _start() {
|
void _start() {
|
||||||
uartinit();
|
load_gdt();
|
||||||
init_keyboard();
|
init_keyboard();
|
||||||
|
uartinit();
|
||||||
load_idt();
|
load_idt();
|
||||||
sti();
|
sti();
|
||||||
|
|
||||||
vga_clear_screen();
|
vga_clear_screen();
|
||||||
printk("\nYABLOKO\n> ");
|
printk("YABLOKO\n");
|
||||||
|
|
||||||
|
printk("\n> ");
|
||||||
while (1) {
|
while (1) {
|
||||||
|
if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') {
|
||||||
|
if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) {
|
||||||
|
qemu_shutdown();
|
||||||
|
} else if (!strncmp("run ", kbd_buf, 4)) {
|
||||||
|
kbd_buf[kbd_buf_size-1] = '\0';
|
||||||
|
const char* cmd = kbd_buf + 4;
|
||||||
|
run_elf(cmd);
|
||||||
|
} else {
|
||||||
|
printk("unknown command, try: halt | run CMD");
|
||||||
|
}
|
||||||
|
kbd_buf_size = 0;
|
||||||
|
printk("\n> ");
|
||||||
|
}
|
||||||
asm("hlt");
|
asm("hlt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
lib/string.c
Normal file
20
lib/string.c
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
int strncmp(const char* s1, const char* s2, size_t size) {
|
||||||
|
while (size && *s1 && *s2 && *s1 == *s2) {
|
||||||
|
size--;
|
||||||
|
s1++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
if (!size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (unsigned char)(*s1) - (unsigned char)(*s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void memset(void* b, char c, size_t len) {
|
||||||
|
char* p = b;
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
p[i] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lib/string.h
Normal file
7
lib/string.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef unsigned size_t;
|
||||||
|
|
||||||
|
void kmemmove(char* dst, char* src, size_t size);
|
||||||
|
int strncmp(const char* s1, const char* s2, size_t size);
|
||||||
|
void memset(void* b, char c, size_t len);
|
||||||
75
proc.c
Normal file
75
proc.c
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include "elf.h"
|
||||||
|
#include "proc.h"
|
||||||
|
#include "fs/fs.h"
|
||||||
|
#include "cpu/gdt.h"
|
||||||
|
#include "cpu/isr.h"
|
||||||
|
#include "lib/mem.h"
|
||||||
|
#include "lib/string.h"
|
||||||
|
#include "console.h"
|
||||||
|
|
||||||
|
struct context {
|
||||||
|
// matches the behavior of swtch()
|
||||||
|
uint32_t edi, esi, ebp, ebx;
|
||||||
|
uint32_t eip; // return address for swtch()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kstack {
|
||||||
|
uint32_t space[400];
|
||||||
|
struct context context;
|
||||||
|
registers_t trapframe;
|
||||||
|
char bottom[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct task {
|
||||||
|
struct taskstate tss;
|
||||||
|
struct kstack stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vm {
|
||||||
|
void *kernel_thread;
|
||||||
|
void *user_thread;
|
||||||
|
struct task *user_task;
|
||||||
|
} *vm;
|
||||||
|
|
||||||
|
void trapret();
|
||||||
|
void swtch(void** oldstack, void* newstack);
|
||||||
|
|
||||||
|
void run_elf(const char* name) {
|
||||||
|
if (!vm) {
|
||||||
|
vm = kmalloc(sizeof(struct vm));
|
||||||
|
vm->user_task = kmalloc(sizeof(struct task));
|
||||||
|
switchuvm(&vm->user_task->tss, vm->user_task->stack.bottom);
|
||||||
|
}
|
||||||
|
if (read_file(name, (void*)USER_BASE, 100 << 20) <= 0) {
|
||||||
|
printk(name);
|
||||||
|
printk(": file not found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Elf32_Ehdr *hdr = (void*)USER_BASE;
|
||||||
|
|
||||||
|
struct kstack *u = &vm->user_task->stack;
|
||||||
|
memset(u, 0, sizeof(*u));
|
||||||
|
u->context.eip = (uint32_t)trapret;
|
||||||
|
|
||||||
|
registers_t *tf = &u->trapframe;
|
||||||
|
tf->eip = hdr->e_entry;
|
||||||
|
tf->cs = (SEG_UCODE << 3) | DPL_USER;
|
||||||
|
tf->ds = (SEG_UDATA << 3) | DPL_USER;
|
||||||
|
tf->es = tf->ds;
|
||||||
|
tf->fs = tf->ds;
|
||||||
|
tf->gs = tf->ds;
|
||||||
|
tf->ss = tf->ds;
|
||||||
|
tf->eflags = FL_IF;
|
||||||
|
tf->useresp = USER_STACK_BASE;
|
||||||
|
|
||||||
|
// initialization done, now switch to the process
|
||||||
|
swtch(&vm->kernel_thread, &u->context);
|
||||||
|
|
||||||
|
// process has finished
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn void killproc() {
|
||||||
|
void* task_stack;
|
||||||
|
swtch(&task_stack, vm->kernel_thread);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
4
proc.h
Normal file
4
proc.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void run_elf(const char* name);
|
||||||
|
_Noreturn void killproc();
|
||||||
9
syscall.h
Normal file
9
syscall.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
enum {
|
||||||
|
T_SYSCALL = 0x84,
|
||||||
|
SYS_exit = 0,
|
||||||
|
SYS_greet = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
int syscall(int call, int arg);
|
||||||
18
user/crt.c
Normal file
18
user/crt.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "../syscall.h"
|
||||||
|
|
||||||
|
int main();
|
||||||
|
|
||||||
|
int syscall(int call, int arg) {
|
||||||
|
asm("int $0x84": "+a"(call) : "b"(arg));
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn
|
||||||
|
void _exit(int exit_status) {
|
||||||
|
syscall(SYS_exit, exit_status);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _start() {
|
||||||
|
_exit(main());
|
||||||
|
}
|
||||||
5
user/div0.c
Normal file
5
user/div0.c
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
int main(void) {
|
||||||
|
volatile int x = 1, y = 0;
|
||||||
|
x /= y;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
3
user/false.c
Normal file
3
user/false.c
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
int main() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
7
user/greet.c
Normal file
7
user/greet.c
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "../syscall.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
syscall(SYS_greet, 0);
|
||||||
|
syscall(SYS_greet, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user