commit before starting Brendan's multitasking tutorial

This commit is contained in:
Jack Halford 2019-11-29 21:24:32 +01:00
parent 5880d5296e
commit ed5b97a87b
17 changed files with 211 additions and 65 deletions

View file

@ -11,6 +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.setBuildMode(b.standardReleaseOptions());
kernel.setTarget(builtin.Arch.i386, builtin.Os.freestanding, builtin.Abi.none);

View file

@ -1,10 +1,9 @@
#!/bin/bash
exit_missing() {
printf "$_ must be installed\n" ; exit 1;
}
exit_missing() { printf "$_ must be installed\n" ; exit 1; }
which xorriso || exit_missing
which grub-mkrescue || exit_missing
mkdir -p build/iso/boot
cp build/bzImage build/iso/boot
grub-mkrescue -o build/kernel.iso build/iso

View file

@ -0,0 +1 @@
pub const PAGE_SIZE: usize = 4096;

View file

@ -58,7 +58,7 @@ pub fn initialize() void {
isr.install_irqs();
isr.install_syscalls();
interrupt.registerIRQ(0, interrupt.pit_handler);
interrupt.registerIRQ(1, ps2.keyboard_handler);
interrupt.registerIRQ(1, kernel.ps2.keyboard_handler);
// load IDT
lidt(@ptrToInt(&idtr));

View file

@ -1,18 +1,16 @@
// std
pub const assert = @import("std").debug.assert;
pub const std = @import("std");
pub const assert = std.debug.assert;
// from core kernel
pub usingnamespace @import("../../vga.zig");
pub const multiboot = @import("../../multiboot.zig");
pub const time = @import("../../time.zig");
pub const ps2 = @import("../../ps2.zig");
pub const kernel = @import("../../index.zig");
// x86 namespace
pub usingnamespace @import("lib/io.zig");
pub usingnamespace @import("lib/instructions.zig");
pub usingnamespace @import("main.zig");
pub const layout = @import("layout.zig");
pub const memory = @import("memory.zig");
pub usingnamespace @import("constants.zig");
pub const pmem = @import("pmem.zig");
pub const paging = @import("paging.zig");
pub const idt = @import("idt.zig");
pub const isr = @import("isr.zig");

View file

@ -35,11 +35,11 @@ var handlers = [_]fn () void{unhandled} ** 48;
fn unhandled() noreturn {
const n = isr.context.interrupt_n;
print("unhandled interrupt number {d}", n);
kernel.print("unhandled interrupt number {d}", n);
if (n < IRQ_0) {
println(" (exception)");
kernel.println(" (exception)");
} else {
println(" (IRQ number {d})", n - IRQ_0);
kernel.println(" (IRQ number {d})", n - IRQ_0);
}
hang();
}
@ -196,5 +196,5 @@ pub fn pit_handler() void {
// pit freq = 1.193182 MHz
// chan0 divisor = 2685
// PIT_RATE in us
time.increment(2251);
kernel.time.increment(2251);
}

View file

@ -1,12 +1,11 @@
// usingnamespace @import("kernel");
usingnamespace @import("index.zig");
// const multiboot = @import("../../multiboot.zig");
/// x86 specific intialization
pub fn x86_main(info: *const multiboot.MultibootInfo) void {
pub fn x86_main(info: *const kernel.multiboot.MultibootInfo) void {
gdt.initialize();
idt.initialize();
memory.initialize(info);
pmem.initialize(info);
paging.initialize();
// enable interrupts

View file

@ -1,10 +1,8 @@
usingnamespace @import("index.zig");
// usingnamespace @import("x86");
extern fn setupPaging(phys_pd: usize) void;
const PageEntry = usize;
pub const PAGE_SIZE = 4096;
pub const PT = @intToPtr([*]PageEntry, 0xFFC00000);
pub const PD = @intToPtr([*]PageEntry, 0xFFFFF000);
const PRESENT = 0x1;
@ -18,18 +16,18 @@ const HUGE = 0x80;
pub var pageDirectory: [1024]PageEntry align(4096) linksection(".bss") = [_]PageEntry{0} ** 1024;
fn pageFault() void {
println("pagefault");
kernel.println("pagefault");
while (true) asm volatile ("hlt");
}
inline fn pageBase(addr: usize) usize {
inline fn pageBase(virt: usize) usize {
return addr & (~PAGE_SIZE +% 1);
}
inline fn pde(addr: usize) *PageEntry {
return &PD[addr >> 22];
inline fn pde(virt: usize) *PageEntry {
return &PD[virt >> 22]; //relies on recursive mapping
}
inline fn pte(addr: usize) *PageEntry {
return &PT[addr >> 12];
inline fn pte(virt: usize) *PageEntry {
return &PT[virt >> 12]; //relies on recursive mapping
}
// virtual to physical
@ -40,17 +38,18 @@ pub fn translate(virt: usize) ?usize {
pub fn unmap(virt: usize) void {
if (translate(virt)) |phys| {
memory.free(translate(virt));
mem.free(phys);
} else {
println("can't unmap 0x{x}, map is empty.", addr);
kernel.println("can't unmap 0x{x} because it is not mapped.", virt);
}
}
pub fn mmap(virt: usize, phys: ?usize) !void {
var pde: *PageEntry = pde(virt);
if (pde.* == 0) pde.* = try memory.allocate() | WRITE | PRESENT;
var pte: *PageEntry = pte(virt);
pte.* = if (phys) |p| p else allocate() | PRESENT;
//TODO: support hugepages
// allocate a page directory if there is none
if (pde(virt).* == 0) pde(virt).* = (try pmem.allocate()) | WRITE | PRESENT;
// allocate a frame if phys isn't specified
pte(virt).* = (if (phys) |p| p else try pmem.allocate()) | PRESENT;
}
pub fn initialize() void {
@ -61,7 +60,8 @@ pub fn initialize() void {
// recursive mapping
p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE;
assert(memory.stack_end < layout.IDENTITY);
// TODO: verify is this a hack?
assert(pmem.stack_end < kernel.layout.IDENTITY);
interrupt.register(14, pageFault);
setupPaging(@ptrToInt(&pageDirectory[0]));
@ -72,12 +72,12 @@ pub fn introspect() void {
i = 0;
while (i < 1024) : (i += 1) {
if (PD[i] == 0) continue;
println("p2[{}] -> 0x{x}", i, PD[i]);
kernel.println("p2[{}] -> 0x{x}", i, PD[i]);
if (PD[i] & HUGE != 0) continue;
var j: usize = 0;
while (j < 1024) : (j += 1) {
var entry: PageEntry = PT[i * 1024 + j];
if (entry != 0) println("p2[{}]p1[{}] -> 0x{x}", i, j, entry);
if (entry != 0) kernel.println("p2[{}]p1[{}] -> 0x{x}", i, j, entry);
}
}
}

View file

@ -7,7 +7,6 @@ var stack_index: usize = 0; // Index into the stack.
pub var stack_size: usize = undefined;
pub var stack_end: usize = undefined;
pub const PAGE_SIZE: usize = 4096;
pub inline fn pageAlign(address: u32) u32 {
// 4095 -> 4096
// 4096 -> 4096
@ -15,7 +14,6 @@ pub inline fn pageAlign(address: u32) u32 {
return (address + PAGE_SIZE - 1) & (~PAGE_SIZE +% 1);
}
////
// Return the amount of variable elements (in bytes).
//
pub inline fn available() usize {
@ -26,19 +24,17 @@ pub inline fn available_MiB() usize {
return available() / (1024 * 1024);
}
////
// Request a free physical page and return its address.
//
pub fn allocate() !usize {
if (available() == 0) {
println("out of memory");
kernel.println("out of memory");
return error.OutOfMemory;
}
stack_index -= 1;
return stack[stack_index];
}
////
// Free a previously allocated physical page.
//
// Arguments:
@ -49,16 +45,15 @@ pub fn free(address: usize) void {
stack_index += 1;
}
////
// Scan the memory map to index all available memory.
//
// Arguments:
// info: Information structure from bootloader.
//
pub fn initialize(info: *const multiboot.MultibootInfo) void {
pub fn initialize(info: *const kernel.multiboot.MultibootInfo) void {
// Ensure the bootloader has given us the memory map.
assert((info.flags & multiboot.MULTIBOOT_INFO_MEMORY) != 0);
assert((info.flags & multiboot.MULTIBOOT_INFO_MEM_MAP) != 0);
assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEMORY) != 0);
assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEM_MAP) != 0);
// TODO: WHAT WHY WHAAAAT, must check back here later
// Place stack at 0x200000 so that in the future I trigger a
@ -74,7 +69,7 @@ pub fn initialize(info: *const multiboot.MultibootInfo) void {
var map: usize = info.mmap_addr;
while (map < info.mmap_addr + info.mmap_length) {
var entry = @intToPtr(*multiboot.MultibootMMapEntry, map);
var entry = @intToPtr(*kernel.multiboot.MultibootMMapEntry, map);
// Calculate the start and end of this memory area.
var start = @truncate(usize, entry.addr);
@ -83,16 +78,16 @@ pub fn initialize(info: *const multiboot.MultibootInfo) void {
start = if (start >= stack_end) start else stack_end;
// Flag all the pages in this memory area as free.
if (entry.type == multiboot.MULTIBOOT_MEMORY_AVAILABLE) while (start < end) : (start += PAGE_SIZE)
if (entry.type == kernel.multiboot.MULTIBOOT_MEMORY_AVAILABLE) while (start < end) : (start += PAGE_SIZE)
free(start);
// Go to the next entry in the memory map.
map += entry.size + @sizeOf(@typeOf(entry.size));
}
println("available memory: {d} MiB ", available() / 1024 / 1024);
kernel.println("available memory: {d} MiB ", available() / 1024 / 1024);
}
pub fn introspect() void {
println("physframes left: {d} ({d} MiB)", stack_index, available_MiB());
kernel.println("physframes left: {d} ({d} MiB)", stack_index, available_MiB());
}

View file

@ -0,0 +1,47 @@
;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
switch_tasks:
;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
push ebx
push esi
push edi
push ebp
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
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
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)

View file

@ -6,7 +6,7 @@ var command_len: usize = 0;
fn execute(com: []u8) void {
const eql = std.mem.eql;
if (eql(u8, com, "x86paging")) return x86.paging.introspect();
if (eql(u8, com, "x86memory")) return x86.memory.introspect();
if (eql(u8, com, "x86memory")) return x86.pmem.introspect();
if (eql(u8, com, "lspci")) return pci.lspci();
if (eql(u8, com, "uptime")) return time.uptime();
if (eql(u8, com, "topbar")) return topbar();

View file

@ -1,14 +1,20 @@
// std
/// std
pub const std = @import("std");
pub const assert = std.debug.assert;
// main namespace
pub usingnamespace @import("vga.zig");
///arch
pub const x86 = @import("arch/x86/index.zig");
///core
pub const layout = @import("layout.zig");
pub const multiboot = @import("multiboot.zig");
pub const mem = @import("memory.zig");
pub const vmem = @import("vmem.zig");
pub const task = @import("task.zig");
pub const time = @import("time.zig");
///extra
pub const console = @import("console.zig");
pub const pci = @import("pci/pci.zig");
pub const ps2 = @import("ps2.zig");
pub const time = @import("time.zig");
pub const ps2 = @import("ps2.zig"); // i don't know whether this is x86 specific or not

View file

@ -1,6 +1,7 @@
//https://wiki.osdev.org/Memory_Map_(x86)
// virtual memory layout of the kernel
const kiB = 1024;
const kiB = 1024; // bytes
const MiB = 1024 * kiB; // 0x100000
const GiB = 1024 * MiB;
@ -10,6 +11,7 @@ pub const KERNEL = 1 * MiB;
pub const IDENTITY = 4 * MiB; // 0->4MiB
pub const HEAP = 8 * MiB;
pub const HEAP_END = 0x1000000;
pub const USER_STACKS = 0x1000000;
pub const USER_STACKS_END = 0x10000000;
// zig fmt: on

View file

@ -1,5 +1,4 @@
usingnamespace @import("kernel");
// const x86 = @import("x86");
// Place the header at the very beginning of the binary.
export const multiboot_header align(4) linksection(".multiboot") = multiboot: {
@ -16,6 +15,8 @@ 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,9 +25,17 @@ export fn kmain(magic: u32, info: *const multiboot.MultibootInfo) noreturn {
x86.x86_main(info);
println("--- core initialization ---");
pci.scan();
console.initialize();
vmem.initialize();
// const t = task.Task.new(@ptrToInt(topbar));
// var a = vmem.allocate(u32) catch unreachable;
// println("a={}", &a);
// const b = vmem.allocate(VGAEntry) catch unreachable;
// println("b={x}", &b);
const t = task.Task.new(@ptrToInt(topbar)) catch unreachable;
println("task={x}", &t);
console.initialize();
while (true) asm volatile ("hlt");
}

View file

@ -1,7 +0,0 @@
pub usingnamespace @import("index.zig");
pub fn allocate(comptime T: type) !*type {}
pub fn free(address: usize) void {}
pub fn initialize() void {}

56
src/task.zig Normal file
View file

@ -0,0 +1,56 @@
pub usingnamespace @import("index.zig");
// var tasks = Array(?*Task).init(&mem.allocator);
const STACK_SIZE = x86.PAGE_SIZE; // Size of thread stacks.
pub const Task = struct {
// context: isr.Context,
////
// Create a new thread inside the current process.
// NOTE: Do not call this function directly. Use Process.createThread instead.
//
// Arguments:
// entry_point: The entry point of the new thread.
//
// Returns:
// Pointer to the new thread structure.
//
tid: u16,
pub fn stack(tid: u16) usize {
const stack = layout.USER_STACKS + (2 * (tid - 1) * STACK_SIZE);
assert(stack < layout.USER_STACKS_END);
return stack;
}
pub fn new(entry_point: usize) !*Task {
// assert(scheduler.current_process == process);
// map the stack
// Allocate and initialize the thread structure.
var this = try vmem.allocate(Task);
this.tid = 4;
return this;
}
};
// fn initContext(entry_point: usize, stack: usize) isr.Context {
// // Insert a trap return address to destroy the thread on return.
// var stack_top = @intToPtr(*usize, stack + STACK_SIZE - @sizeOf(usize));
// stack_top.* = layout.THREAD_DESTROY;
// return isr.Context{
// .cs = gdt.USER_CODE | gdt.USER_RPL,
// .ss = gdt.USER_DATA | gdt.USER_RPL,
// .eip = entry_point,
// .esp = @ptrToInt(stack_top),
// .eflags = 0x202,
// .registers = isr.Registers.init(),
// .interrupt_n = 0,
// .error_code = 0,
// };
// }

40
src/vmem.zig Normal file
View file

@ -0,0 +1,40 @@
pub usingnamespace @import("index.zig");
// TODO: make a better memory allocator
// stupid simple virtual memory allocator
// - does 1:1 virtual/physical mapping
// - no defragmentation
// - no allocation bigger than a page
const stack_size: usize = (layout.HEAP_END - layout.HEAP) / x86.PAGE_SIZE;
var stack: [stack_size]usize = undefined; // Stack of free virtual addresses
var stack_index: usize = 0; // Index into the stack.
pub fn available() usize {
return stack_index * x86.PAGE_SIZE;
}
pub fn allocate(comptime T: type) !*T {
assert(@sizeOf(T) < x86.PAGE_SIZE); // this allocator only support 1:1 mapping
if (available() == 0) return error.OutOfMemory;
stack_index -= 1;
var vaddr: usize = stack[stack_index];
try x86.paging.mmap(vaddr, null);
return @intToPtr(*T, vaddr);
}
pub fn free(address: usize) void {
x86.paging.unmap(address);
stack[stack_index] = address;
stack_index += 1;
}
pub fn initialize() void {
var addr: usize = layout.HEAP;
while (addr < layout.HEAP_END) : (addr += x86.PAGE_SIZE) {
// println("addr {x}", addr);
stack[stack_index] = addr;
stack_index += 1;
// return;
}
}