/ src / main.zig
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  }