Brendan's Tutorial Step 1: done
This commit is contained in:
parent
8d7e7591e9
commit
6af31b5b89
13 changed files with 163 additions and 127 deletions
5
.gdbinit
Normal file
5
.gdbinit
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
file build/kernel
|
||||
target remote localhost:4242
|
||||
set arch i386
|
||||
set step-mode on
|
||||
directory src
|
||||
|
|
@ -11,7 +11,7 @@ pub fn build(b: *Builder) void {
|
|||
kernel.addAssemblyFile("src/arch/x86/gdt.s");
|
||||
kernel.addAssemblyFile("src/arch/x86/isr.s");
|
||||
kernel.addAssemblyFile("src/arch/x86/paging.s");
|
||||
// kernel.addAssemblyFile("src/arch/x86/switch_tasks.s");
|
||||
kernel.addAssemblyFile("src/arch/x86/switch_tasks.s");
|
||||
|
||||
kernel.setBuildMode(b.standardReleaseOptions());
|
||||
kernel.setTarget(builtin.Arch.i386, builtin.Os.freestanding, builtin.Abi.none);
|
||||
|
|
|
|||
30
qemu.sh
30
qemu.sh
|
|
@ -1,5 +1,5 @@
|
|||
QEMU_SOCKET=/tmp/qemu.sock
|
||||
QEMU_MONITOR="socat - UNIX-CONNECT:${QEMU_SOCKET}"
|
||||
QEMU_MONITOR="sudo socat - UNIX-CONNECT:${QEMU_SOCKET}"
|
||||
QEMU_GDB_PORT=4242
|
||||
KERNEL=build/kernel
|
||||
|
||||
|
|
@ -11,11 +11,11 @@ start() {
|
|||
-enable-kvm \
|
||||
-m 1337M \
|
||||
-curses \
|
||||
-append "Hello" \
|
||||
-drive file=disk.img,if=virtio\
|
||||
-kernel ${KERNEL}
|
||||
# -drive file=disk.img,if=virtio\
|
||||
# -no-reboot \
|
||||
# -device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \
|
||||
# -S \
|
||||
# build/kernel.iso
|
||||
|
||||
# this allows to monitor with ^a-c, but doesn't
|
||||
|
|
@ -23,26 +23,16 @@ start() {
|
|||
# -serial mon:stdio \
|
||||
}
|
||||
|
||||
monitor() {
|
||||
if [ "$1" == "" ]; then
|
||||
sudo ${QEMU_MONITOR}
|
||||
else
|
||||
echo "$1" | sudo ${QEMU_MONITOR} >/dev/null
|
||||
fi
|
||||
}
|
||||
monitor() { sudo ${QEMU_MONITOR}; }
|
||||
monitor-exec() { echo "$1" | sudo ${QEMU_MONITOR} >/dev/null; }
|
||||
|
||||
quit() { monitor-exec quit; }
|
||||
|
||||
reload() {
|
||||
monitor stop
|
||||
monitor-exec stop
|
||||
# monitor "change ide1-cd0 ${KERNEL}"
|
||||
monitor system_reset
|
||||
monitor cont
|
||||
}
|
||||
|
||||
gdb() {
|
||||
gdb -q \
|
||||
-symbols "${KERNEL}" \
|
||||
-ex "target remote :${QEMU_GDB_PORT}" \
|
||||
-ex "set arch i386"
|
||||
monitor-exec system_reset
|
||||
monitor-exec cont
|
||||
}
|
||||
|
||||
"$@"
|
||||
|
|
|
|||
|
|
@ -59,7 +59,12 @@ pub fn initialize() void {
|
|||
isr.install_syscalls();
|
||||
interrupt.registerIRQ(0, interrupt.pit_handler);
|
||||
interrupt.registerIRQ(1, kernel.ps2.keyboard_handler);
|
||||
interrupt.register(3, test_b);
|
||||
|
||||
// load IDT
|
||||
lidt(@ptrToInt(&idtr));
|
||||
}
|
||||
|
||||
pub fn test_b() void {
|
||||
kernel.println("int3!");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
////
|
||||
// Load a new Task Register.
|
||||
//
|
||||
// Arguments:
|
||||
// desc: Segment selector of the TSS.
|
||||
//
|
||||
pub inline fn ltr(desc: u16) void {
|
||||
asm volatile ("ltr %[desc]"
|
||||
:
|
||||
|
|
@ -11,27 +5,25 @@ pub inline fn ltr(desc: u16) void {
|
|||
);
|
||||
}
|
||||
|
||||
////
|
||||
// Completely stop the computer.
|
||||
//
|
||||
pub inline fn hang() noreturn {
|
||||
asm volatile ("cli");
|
||||
cli();
|
||||
while (true) asm volatile ("hlt");
|
||||
}
|
||||
|
||||
pub inline fn sti() void {
|
||||
//TODO: inline this
|
||||
pub fn cli() void {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
//TODO: inline this
|
||||
pub fn sti() void {
|
||||
asm volatile ("sti");
|
||||
}
|
||||
|
||||
pub inline fn int3() void {
|
||||
asm volatile ("int3");
|
||||
}
|
||||
|
||||
////
|
||||
// Load a new Interrupt Descriptor Table.
|
||||
//
|
||||
// Arguments:
|
||||
// idtr: Address of the IDTR register.
|
||||
//
|
||||
pub inline fn lidt(idtr: usize) void {
|
||||
asm volatile ("lidt (%[idtr])"
|
||||
:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ pub fn x86_main(info: *const kernel.multiboot.MultibootInfo) void {
|
|||
idt.initialize();
|
||||
pmem.initialize(info);
|
||||
paging.initialize();
|
||||
|
||||
// enable interrupts
|
||||
sti();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ pub inline fn pageAlign(address: u32) u32 {
|
|||
|
||||
// Return the amount of variable elements (in bytes).
|
||||
//
|
||||
pub inline fn available() usize {
|
||||
pub fn available() usize {
|
||||
return stack_index * PAGE_SIZE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +1,55 @@
|
|||
;https://wiki.osdev.org/Multitasking_Systems
|
||||
//https://wiki.osdev.org/Multitasking_Systems
|
||||
|
||||
;C declaration:
|
||||
; void switch_tasks(thread_control_block *next_thread);
|
||||
;
|
||||
;WARNING: Caller is expected to disable IRQs before calling, and enable IRQs again after function returns
|
||||
//C declaration:
|
||||
// void switch_tasks(thread_control_block *next_thread)//
|
||||
//
|
||||
//WARNING: Caller is expected to disable IRQs before calling, and enable IRQs again after function returns
|
||||
|
||||
.type switch_tasks, @function
|
||||
.global switch_tasks
|
||||
|
||||
switch_tasks:
|
||||
push %ebp
|
||||
mov %esp, %ebp
|
||||
mov +12(%esp), %eax
|
||||
mov %esp, (%eax) //save old esp
|
||||
mov +8(%esp), %eax
|
||||
mov %eax, %esp // move the forged stack to esp
|
||||
pop %ebp // the top of the forged stack contains ebp
|
||||
ret //the top of the forged stack contains eip to go to
|
||||
|
||||
;Save previous task's state
|
||||
//Save previous task's state
|
||||
//Notes:
|
||||
// For cdecl// EAX, ECX, and EDX are already saved by the caller and don't need to be saved again
|
||||
// EIP is already saved on the stack by the caller's "CALL" instruction
|
||||
// The task isn't able to change CR3 so it doesn't need to be saved
|
||||
// Segment registers are constants (while running kernel code) so they don't need to be saved
|
||||
|
||||
;Notes:
|
||||
; For cdecl; EAX, ECX, and EDX are already saved by the caller and don't need to be saved again
|
||||
; EIP is already saved on the stack by the caller's "CALL" instruction
|
||||
; The task isn't able to change CR3 so it doesn't need to be saved
|
||||
; Segment registers are constants (while running kernel code) so they don't need to be saved
|
||||
// push %ebx
|
||||
// push %esi
|
||||
// push %edi
|
||||
// push %ebp
|
||||
|
||||
push ebx
|
||||
push esi
|
||||
push edi
|
||||
push ebp
|
||||
// mov %edi,[current_task] //edi = address of the previous task's "thread control block"
|
||||
// mov [edi+TCB.ESP], %esp //Save ESP for previous task's kernel stack in the thread's TCB
|
||||
|
||||
mov edi,[current_task_TCB] ;edi = address of the previous task's "thread control block"
|
||||
mov [edi+TCB.ESP],esp ;Save ESP for previous task's kernel stack in the thread's TCB
|
||||
// Load next task's state
|
||||
|
||||
;Load next task's state
|
||||
// mov %esi,[esp+(4+1)*4] //esi = address of the next task's "thread control block" (parameter passed on stack)
|
||||
// mov [current_task], %esi //Current task's TCB is the next task TCB
|
||||
|
||||
mov esi,[esp+(4+1)*4] ;esi = address of the next task's "thread control block" (parameter passed on stack)
|
||||
mov [current_task_TCB],esi ;Current task's TCB is the next task TCB
|
||||
// mov %esp,[esi+TCB.ESP] //Load ESP for next task's kernel stack from the thread's TCB
|
||||
// mov eax,[esi+TCB.CR3] //eax = address of page directory for next task
|
||||
// mov %ebx,[esi+TCB.ESP0] //ebx = address for the top of the next task's kernel stack
|
||||
// mov [TSS.ESP0],ebx //Adjust the ESP0 field in the TSS (used by CPU for for CPL=3 -> CPL=0 privilege level changes)
|
||||
// mov ecx,cr3 //ecx = previous task's virtual address space
|
||||
|
||||
mov esp,[esi+TCB.ESP] ;Load ESP for next task's kernel stack from the thread's TCB
|
||||
mov eax,[esi+TCB.CR3] ;eax = address of page directory for next task
|
||||
mov ebx,[esi+TCB.ESP0] ;ebx = address for the top of the next task's kernel stack
|
||||
mov [TSS.ESP0],ebx ;Adjust the ESP0 field in the TSS (used by CPU for for CPL=3 -> CPL=0 privilege level changes)
|
||||
mov ecx,cr3 ;ecx = previous task's virtual address space
|
||||
// cmp eax,ecx //Does the virtual address space need to being changed?
|
||||
// je .doneVAS // no, virtual address space is the same, so don't reload it and cause TLB flushes
|
||||
// mov cr3,eax // yes, load the next task's virtual address space
|
||||
// .doneVAS:
|
||||
|
||||
cmp eax,ecx ;Does the virtual address space need to being changed?
|
||||
je .doneVAS ; no, virtual address space is the same, so don't reload it and cause TLB flushes
|
||||
mov cr3,eax ; yes, load the next task's virtual address space
|
||||
.doneVAS:
|
||||
|
||||
pop ebp
|
||||
pop edi
|
||||
pop esi
|
||||
pop ebx
|
||||
|
||||
ret ;Load next task's EIP from its kernel stack)
|
||||
// pop %ebp
|
||||
// pop %edi
|
||||
// pop %esi
|
||||
// pop %ebx
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
pub const assert = @import("std").debug.assert;
|
||||
pub const std = @import("std");
|
||||
|
||||
pub usingnamespace @import("vga.zig");
|
||||
|
|
@ -3,6 +3,13 @@ usingnamespace @import("index.zig");
|
|||
var command: [10]u8 = undefined;
|
||||
var command_len: usize = 0;
|
||||
|
||||
fn test_a() void {
|
||||
var a: u32 = 2;
|
||||
a += 1;
|
||||
a = 4;
|
||||
a -= 1;
|
||||
}
|
||||
|
||||
fn execute(input: []u8) void {
|
||||
const eql = std.mem.eql;
|
||||
if (eql(u8, input, "x86paging")) return x86.paging.introspect();
|
||||
|
|
@ -11,6 +18,10 @@ fn execute(input: []u8) void {
|
|||
if (eql(u8, input, "lspci")) return pci.lspci();
|
||||
if (eql(u8, input, "uptime")) return time.uptime();
|
||||
if (eql(u8, input, "topbar")) return topbar();
|
||||
if (eql(u8, input, "test")) {
|
||||
const tbar = task.Task.new(@ptrToInt(topbar)) catch unreachable;
|
||||
while (true) tbar.switch_to();
|
||||
}
|
||||
println("{}: command not found", input);
|
||||
}
|
||||
|
||||
|
|
|
|||
11
src/main.zig
11
src/main.zig
|
|
@ -15,8 +15,6 @@ export const multiboot_header align(4) linksection(".multiboot") = multiboot: {
|
|||
};
|
||||
};
|
||||
|
||||
extern fn switch_tasks(stack: u32) void;
|
||||
|
||||
// arch independant initialization
|
||||
export fn kmain(magic: u32, info: *const multiboot.MultibootInfo) noreturn {
|
||||
assert(magic == multiboot.MULTIBOOT_BOOTLOADER_MAGIC);
|
||||
|
|
@ -24,10 +22,11 @@ export fn kmain(magic: u32, info: *const multiboot.MultibootInfo) noreturn {
|
|||
println("--- x86 initialization ---");
|
||||
x86.x86_main(info);
|
||||
println("--- core initialization ---");
|
||||
// pci.scan();
|
||||
vmem.initialize();
|
||||
task.initialize() catch unreachable;
|
||||
console.initialize();
|
||||
pci.scan();
|
||||
|
||||
while (true) asm volatile ("hlt");
|
||||
console.initialize();
|
||||
// while (true) asm volatile ("hlt");
|
||||
const tbar = task.Task.new(@ptrToInt(topbar)) catch unreachable;
|
||||
while (true) tbar.switch_to();
|
||||
}
|
||||
|
|
|
|||
55
src/task.zig
55
src/task.zig
|
|
@ -1,14 +1,18 @@
|
|||
pub usingnamespace @import("index.zig");
|
||||
const TASK_MAX = 1024;
|
||||
var tasks = [1]?*Task{null} ** TASK_MAX;
|
||||
var boot_task = Task{ .tid = 0, .esp = 0x47 };
|
||||
var current_task: *Task = &boot_task;
|
||||
pub var tasks = [1]?*Task{&boot_task} ++ ([1]?*Task{null} ** TASK_MAX);
|
||||
|
||||
const STACK_SIZE = x86.PAGE_SIZE; // Size of thread stacks.
|
||||
var tid_counter: u16 = 1;
|
||||
|
||||
pub const Task = struct {
|
||||
///ASM
|
||||
extern fn switch_tasks(new_esp: u32, old_esp_addr: u32) void;
|
||||
|
||||
pub const Task = packed struct {
|
||||
esp: usize,
|
||||
tid: u16,
|
||||
stack_top: usize,
|
||||
entrypoint: usize,
|
||||
//context: isr.Context,
|
||||
//cr3: usize,
|
||||
|
||||
|
|
@ -16,13 +20,20 @@ pub const Task = struct {
|
|||
// Allocate and initialize the thread structure.
|
||||
var t = try vmem.allocate(Task);
|
||||
|
||||
t.entrypoint = entrypoint;
|
||||
t.tid = tid_counter;
|
||||
tid_counter +%= 1;
|
||||
assert(tid_counter != 0); //overflow
|
||||
|
||||
t.stack_top = try vmem.malloc(STACK_SIZE);
|
||||
assert(t.stack_top < layout.USER_STACKS_END);
|
||||
// allocate a new stack
|
||||
t.esp = (try vmem.malloc(STACK_SIZE)) + STACK_SIZE;
|
||||
// top of stack is the address that ret will pop
|
||||
t.esp -= 4;
|
||||
@intToPtr(*usize, t.esp).* = entrypoint;
|
||||
// top of stack is ebp that we will pop
|
||||
t.esp -= 4;
|
||||
@intToPtr(*usize, t.esp).* = t.esp + 8;
|
||||
|
||||
println("new task esp=0x{x}, eip=0x{x}", t.esp, entrypoint);
|
||||
|
||||
tasks[t.tid] = t;
|
||||
return t;
|
||||
|
|
@ -30,20 +41,38 @@ pub const Task = struct {
|
|||
|
||||
pub fn destroy(self: *Task) void {
|
||||
tasks[self.tid] = null;
|
||||
vmem.free(self.stack_top);
|
||||
vmem.free(self.esp);
|
||||
vmem.free(@ptrToInt(self));
|
||||
}
|
||||
};
|
||||
|
||||
pub fn initialize() !void {
|
||||
const t = try Task.new(0x0);
|
||||
println("task=0x{x}", t.stack_top);
|
||||
pub fn switch_to(self: *Task) void {
|
||||
assert(self != current_task);
|
||||
// save old stack
|
||||
const old_task_esp_addr = ¤t_task.esp;
|
||||
current_task = self;
|
||||
// x86.cli();
|
||||
// don't inline the asm function, it needs to ret
|
||||
@noInlineCall(switch_tasks, self.esp, @ptrToInt(old_task_esp_addr));
|
||||
// comptime {
|
||||
// asm (
|
||||
// \\mov +8(%esp), %eax
|
||||
// \\mov %esp, (%eax)
|
||||
// \\mov +4(%esp), %eax
|
||||
// \\mov %eax, %esp
|
||||
// \\pop %ebp
|
||||
// \\ret
|
||||
// );
|
||||
// }
|
||||
// x86.sti();
|
||||
println("after switch");
|
||||
}
|
||||
};
|
||||
|
||||
pub fn introspect() void {
|
||||
for (tasks) |t| {
|
||||
if (t == null) continue;
|
||||
println("{}", t);
|
||||
if (t != current_task) println("{}", t);
|
||||
if (t == current_task) println("*{}", t);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
14
src/vga.zig
14
src/vga.zig
|
|
@ -1,7 +1,7 @@
|
|||
// usingnamespace @import("index.zig");
|
||||
const time = @import("time.zig");
|
||||
const x86 = @import("arch/x86/index.zig");
|
||||
const std = @import("std");
|
||||
usingnamespace @import("index.zig");
|
||||
// const time = @import("time.zig");
|
||||
// const x86 = @import("arch/x86/index.zig");
|
||||
// const std = @import("std");
|
||||
// Screen size.
|
||||
pub const VGA_WIDTH = 80;
|
||||
pub const VGA_HEIGHT = 25;
|
||||
|
|
@ -56,9 +56,8 @@ const Errors = error{};
|
|||
pub fn print(comptime format: []const u8, args: ...) void {
|
||||
var a = std.fmt.format({}, Errors, printCallback, format, args);
|
||||
}
|
||||
|
||||
pub fn println(comptime format: []const u8, args: ...) void {
|
||||
var a = std.fmt.format({}, Errors, printCallback, format ++ "\n", args);
|
||||
var a = print(format ++ "\n", args);
|
||||
}
|
||||
pub fn clear() void {
|
||||
vga.clear();
|
||||
|
|
@ -66,6 +65,7 @@ pub fn clear() void {
|
|||
pub fn topbar() void {
|
||||
const cursor = vga.cursor;
|
||||
const bg = vga.background;
|
||||
while (true) {
|
||||
vga.cursor = 0;
|
||||
vga.background = Color.Red;
|
||||
|
||||
|
|
@ -73,6 +73,8 @@ pub fn topbar() void {
|
|||
|
||||
vga.cursor = cursor;
|
||||
vga.background = bg;
|
||||
task.tasks[0].?.switch_to();
|
||||
}
|
||||
}
|
||||
|
||||
fn printCallback(context: void, string: []const u8) Errors!void {
|
||||
|
|
|
|||
Loading…
Reference in a new issue