main.zig
1 const console = @import("console.zig"); 2 3 const MB_HEADER_MAGIC = 0x1BADB002; 4 const MB_FLAG_ALIGN = 1 << 0; 5 const MB_FLAG_MEMINFO = 1 << 1; 6 const FLAGS = MB_FLAG_ALIGN | MB_FLAG_MEMINFO; 7 8 /// https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Header-layout 9 const MultibootHeader = packed struct { 10 magic: u32 = MB_HEADER_MAGIC, 11 flags: u32 = FLAGS, 12 checksum: u32, 13 padding: u32 = 0, 14 }; 15 16 export var multiboot: MultibootHeader align(4) linksection(".multiboot") = .{ 17 // Here we are adding magic and flags and ~ to get 1's complement and by adding 1 we get 2's complement 18 .checksum = ~@as(u32, (MB_HEADER_MAGIC + FLAGS)) + 1, 19 }; 20 21 var stack_bytes: [16 * 1024]u8 align(16) linksection(".bss") = undefined; 22 23 // We specify that this function is "naked" to let the compiler know 24 // not to generate a standard function prologue and epilogue, since 25 // we don't have a stack yet. 26 export fn _start() callconv(.naked) noreturn { 27 // We use inline assembly to set up the stack before jumping to 28 // our kernel entry point. 29 asm volatile ( 30 \\ movl %[stack_top], %%esp 31 \\ movl %%esp, %%ebp 32 \\ call %[kmain:P] 33 : 34 // The stack grows downwards on x86, so we need to point ESP register 35 // to one element past the end of `stack_bytes`. 36 // 37 // Unfortunately, we can't just compute `&stack_bytes[stack_bytes.len]`, 38 // as the Zig compiler will notice the out-of-bounds access at 39 // compile-time and throw an error. We can instead take the start address 40 // of `stack_bytes` then convert into "multi-pointers" `[*]` where zig 41 // allows pointer arithmetic and get the &stack_bytes[stack_bytes.len] 42 // 43 // Finally, we pass the whole expression as an input operand with the 44 // "immediate" constraint to force the compiler to encode this as an 45 // absolute address. This prevents the compiler from doing unnecessary 46 // extra steps to compute the address at runtime (especially in Debug mode), 47 // which could possibly clobber registers that are specified by multiboot 48 // to hold special values (e.g. EAX). 49 : [stack_top] "i" (&@as([*]align(16) u8, @ptrCast(&stack_bytes))[stack_bytes.len]), 50 // We let the compiler handle the reference to kmain by passing it as an input operand as well. 51 [kmain] "X" (&kmain), 52 ); 53 } 54 55 // We use noinline to make sure it don't get inlined by compiler 56 noinline fn kmain() callconv(.c) noreturn { 57 // Initialize our VGA driver 58 console.init(); 59 // Printing string 60 console.print("Hello {s} kernel!", .{"zig"}); 61 // Loop forever as there is nothing to do 62 while (true) { 63 asm volatile ("hlt"); 64 } 65 }