]> prime8.dev >> repos - mos.git/commitdiff
x86 basic kernel master
authorDamian Myrda <damian@prime8.dev>
Sat, 21 Sep 2024 16:54:11 +0000 (11:54 -0500)
committerDamian Myrda <damian@prime8.dev>
Sat, 21 Sep 2024 16:54:11 +0000 (11:54 -0500)
42 files changed:
.gdbinit [new file with mode: 0644]
.gitignore [new file with mode: 0755]
.gitmodules [new file with mode: 0644]
README.md [new file with mode: 0755]
build.zig [new file with mode: 0644]
create_iso.sh [new file with mode: 0755]
kernel/.gitignore [new file with mode: 0644]
kernel/.gitmodules [new file with mode: 0644]
kernel/.pre-commit-config.yaml [new file with mode: 0644]
kernel/build.zig [new file with mode: 0644]
kernel/deps/limine/LICENSE [new file with mode: 0644]
kernel/deps/limine/README.md [new file with mode: 0644]
kernel/deps/limine/build.zig [new file with mode: 0644]
kernel/deps/limine/limine.zig [new file with mode: 0644]
kernel/deps/limine/zig-cache/h/5b6ecd7523182e2589cc991d9a944676.txt [new file with mode: 0644]
kernel/deps/limine/zig-cache/h/timestamp [new file with mode: 0644]
kernel/deps/limine/zig-cache/o/df65dd7eb12dada13f920fe098a02adf/dependencies.zig [new file with mode: 0644]
kernel/deps/limine/zig-cache/z/7cac848da18911d1686214078038a257 [new file with mode: 0644]
kernel/linker.ld [new file with mode: 0644]
kernel/src/FONT.F16 [new file with mode: 0644]
kernel/src/cli.zig [new file with mode: 0644]
kernel/src/dbg.zig [new file with mode: 0644]
kernel/src/fb.zig [new file with mode: 0644]
kernel/src/font.zig [new file with mode: 0644]
kernel/src/log.zig [new file with mode: 0755]
kernel/src/main.zig [new file with mode: 0755]
kernel/src/mem.zig [new file with mode: 0644]
kernel/src/srl.zig [new file with mode: 0644]
kernel/src/x86.zig [new file with mode: 0644]
kernel/src/x86/dbg.zig [new file with mode: 0644]
kernel/src/x86/gdt.zig [new file with mode: 0644]
kernel/src/x86/idt.zig [new file with mode: 0644]
kernel/src/x86/int.zig [new file with mode: 0644]
kernel/src/x86/io.zig [new file with mode: 0644]
kernel/src/x86/lvl.zig [new file with mode: 0644]
kernel/src/x86/pgn.zig [new file with mode: 0644]
kernel/src/x86/pic.zig [new file with mode: 0644]
kernel/src/x86/pmm.zig [new file with mode: 0644]
kernel/src/x86/srl.zig [new file with mode: 0644]
limine [new submodule]
limine.cfg [new file with mode: 0644]
qemu.sh [new file with mode: 0755]

diff --git a/.gdbinit b/.gdbinit
new file mode 100644 (file)
index 0000000..f72f66b
--- /dev/null
+++ b/.gdbinit
@@ -0,0 +1,2 @@
+file ./zig-out/root/boot/kernel
+target remote localhost:1234
diff --git a/.gitignore b/.gitignore
new file mode 100755 (executable)
index 0000000..d864d9e
--- /dev/null
@@ -0,0 +1,2 @@
+/zig-cache/
+/zig-out/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..7e36748
--- /dev/null
@@ -0,0 +1,4 @@
+[submodule "limine"]
+       path = limine
+       url = https://github.com/limine-bootloader/limine.git
+       branch = binary
diff --git a/README.md b/README.md
new file mode 100755 (executable)
index 0000000..e244a36
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# mos
+A monkey's operating system.
diff --git a/build.zig b/build.zig
new file mode 100644 (file)
index 0000000..c6408be
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,24 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) std.mem.Allocator.Error!void {
+    // filesystem
+    b.install_prefix = b.build_root.join(b.allocator, &[_][]const u8{ "zig-out", "root" }) catch unreachable;
+    b.install_path = b.install_prefix;
+
+    // kernel
+    const kernel = b.addInstallArtifact(b.anonymousDependency("./kernel", @import("./kernel/build.zig"), .{}).artifact("kernel"), .{
+        .dest_dir = .{ .override = .{ .custom = "/boot" } },
+    });
+    _ = b.step("kernel", "Build the operating system kernel").dependOn(&kernel.step);
+
+    // create iso
+    const create_iso = b.addSystemCommand(&[_][]const u8{"./create_iso.sh"});
+    create_iso.step.dependOn(&kernel.step);
+    _ = b.step("iso", "Create an iso image of the operating system").dependOn(&create_iso.step);
+    b.getInstallStep().dependOn(&create_iso.step);
+
+    // emulation
+    const qemu = b.addSystemCommand(&[_][]const u8{"./qemu.sh"});
+    _ = b.step("run", "Emulate the operating system using qemu").dependOn(&qemu.step);
+    qemu.step.dependOn(b.getInstallStep());
+}
diff --git a/create_iso.sh b/create_iso.sh
new file mode 100755 (executable)
index 0000000..d9a2ff5
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+mkdir -vp ./zig-out/root/boot
+cp -v ./limine.cfg \
+    ./limine/limine-bios.sys \
+    ./limine/limine-bios-cd.bin \
+    ./limine/limine-uefi-cd.bin \
+    ./zig-out/root/boot/
+
+mkdir -vp ./zig-out/root/boot/EFI/BOOT/
+cp -v ./limine/BOOTX64.EFI ./zig-out/root/boot/EFI/BOOT/
+
+xorriso -as mkisofs -b /boot/limine-bios-cd.bin \
+        -no-emul-boot -boot-load-size 4 -boot-info-table \
+        --efi-boot /boot/limine-uefi-cd.bin \
+        -efi-boot-part --efi-boot-image --protective-msdos-label \
+        ./zig-out/root -o ./zig-out/mos.iso
+
+./limine/limine bios-install ./zig-out/mos.iso
+
diff --git a/kernel/.gitignore b/kernel/.gitignore
new file mode 100644 (file)
index 0000000..d864d9e
--- /dev/null
@@ -0,0 +1,2 @@
+/zig-cache/
+/zig-out/
diff --git a/kernel/.gitmodules b/kernel/.gitmodules
new file mode 100644 (file)
index 0000000..94b6d59
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "deps/limine"]
+       path = deps/limine
+       url = git@github.com:limine-bootloader/limine-zig.git
diff --git a/kernel/.pre-commit-config.yaml b/kernel/.pre-commit-config.yaml
new file mode 100644 (file)
index 0000000..9f67749
--- /dev/null
@@ -0,0 +1,5 @@
+repos:
+  - repo: https://github.com/batmac/pre-commit-zig
+    rev: v0.3.0
+    hooks:
+      - id: zig-fmt
diff --git a/kernel/build.zig b/kernel/build.zig
new file mode 100644 (file)
index 0000000..31d11cc
--- /dev/null
@@ -0,0 +1,45 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
+pub fn build(b: *std.Build) anyerror!void {
+    // build options
+    var target = b.resolveTargetQuery(.{
+        .cpu_arch = .x86_64,
+        .os_tag = .freestanding,
+        .abi = .none,
+    });
+    target.query.cpu_features_sub.addFeature(@intFromEnum(std.Target.x86.Feature.mmx));
+    target.query.cpu_features_sub.addFeature(@intFromEnum(std.Target.x86.Feature.sse));
+    target.query.cpu_features_sub.addFeature(@intFromEnum(std.Target.x86.Feature.sse2));
+    target.query.cpu_features_sub.addFeature(@intFromEnum(std.Target.x86.Feature.avx));
+    target.query.cpu_features_sub.addFeature(@intFromEnum(std.Target.x86.Feature.avx2));
+    target.query.cpu_features_add.addFeature(@intFromEnum(std.Target.x86.Feature.soft_float));
+    const optimize = b.standardOptimizeOption(.{
+        .preferred_optimize_mode = .Debug,
+    });
+
+    // dependencies
+    const limine = b.createModule(.{
+        .root_source_file = .{ .path = "./deps/limine/limine.zig" },
+    });
+
+    // modules
+    const arch = b.createModule(.{ .root_source_file = .{ .path = switch (target.result.cpu.arch) {
+        .x86_64 => "./src/x86.zig",
+        else => unreachable,
+    } } });
+    arch.addImport("limine", limine);
+
+    // kernel binary
+    const kernel = b.addExecutable(.{
+        .name = "kernel",
+        .root_source_file = .{ .path = "./src/main.zig" },
+        .target = target,
+        .optimize = optimize,
+    });
+    kernel.setLinkerScriptPath(.{ .path = "./linker.ld" });
+    kernel.pie = true;
+    kernel.root_module.addImport("limine", limine);
+    kernel.root_module.addImport("arch", arch);
+    b.getInstallStep().dependOn(&b.addInstallArtifact(kernel, .{}).step);
+}
diff --git a/kernel/deps/limine/LICENSE b/kernel/deps/limine/LICENSE
new file mode 100644 (file)
index 0000000..72b01a7
--- /dev/null
@@ -0,0 +1,22 @@
+Copyright (C) 2022-2024 48cf <iretq@riseup.net> and contributors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/kernel/deps/limine/README.md b/kernel/deps/limine/README.md
new file mode 100644 (file)
index 0000000..2abf5ae
--- /dev/null
@@ -0,0 +1,6 @@
+# limine-zig
+
+This is a Zig library for working with [The Limine Boot Protocol](https://github.com/limine-bootloader/limine/blob/trunk/PROTOCOL.md).
+You can find an example barebones kernel using this library [here](https://github.com/limine-bootloader/limine-zig-barebones).
+
+To use this library, you need at least Zig 0.11.x.
diff --git a/kernel/deps/limine/build.zig b/kernel/deps/limine/build.zig
new file mode 100644 (file)
index 0000000..6c82de3
--- /dev/null
@@ -0,0 +1,17 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+    const CreateOptions = std.Build.Module.CreateOptions;
+    var options: CreateOptions = .{};
+
+    const root_source_path: std.Build.LazyPath = .{ .path = "limine.zig" };
+    if (@hasField(CreateOptions, "source_file")) {
+        options.source_file = root_source_path;
+    } else if (@hasField(CreateOptions, "root_source_file")) {
+        options.root_source_file = root_source_path;
+    } else {
+        @compileError("unsupported zig version");
+    }
+
+    _ = b.addModule("limine", options);
+}
diff --git a/kernel/deps/limine/limine.zig b/kernel/deps/limine/limine.zig
new file mode 100644 (file)
index 0000000..38c707b
--- /dev/null
@@ -0,0 +1,586 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
+inline fn magic(a: u64, b: u64) [4]u64 {
+    return .{ 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, a, b };
+}
+
+pub const BaseRevision = extern struct {
+    id: [2]u64 = .{ 0xf9562b2d5c95a6c8, 0x6a7b384944536bdc },
+    revision: u64,
+
+    pub fn is_supported(self: *const volatile @This()) bool {
+        return self.revision == 0;
+    }
+};
+
+pub const Uuid = extern struct {
+    a: u32,
+    b: u16,
+    c: u16,
+    d: [8]u8,
+};
+
+pub const MediaType = enum(u32) {
+    generic = 0,
+    optical = 1,
+    tftp = 2,
+};
+
+pub const File = extern struct {
+    revision: u64,
+    address: [*]u8,
+    size: u64,
+    path: [*:0]u8,
+    cmdline: [*:0]u8,
+    media_type: MediaType,
+    unused: u32,
+    tftp_ip: u32,
+    tftp_port: u32,
+    partition_index: u32,
+    mbr_disk_id: u32,
+    gpt_disk_uuid: Uuid,
+    gpt_part_uuid: Uuid,
+    part_uuid: Uuid,
+
+    pub inline fn data(self: *@This()) []u8 {
+        return self.address[0..self.size];
+    }
+};
+
+// Boot info
+
+pub const BootloaderInfoResponse = extern struct {
+    revision: u64,
+    name: [*:0]u8,
+    version: [*:0]u8,
+};
+
+pub const BootloaderInfoRequest = extern struct {
+    id: [4]u64 = magic(0xf55038d8e2a1202f, 0x279426fcf5f59740),
+    revision: u64 = 0,
+    response: ?*BootloaderInfoResponse = null,
+};
+
+// Stack size
+
+pub const StackSizeResponse = extern struct {
+    revision: u64,
+};
+
+pub const StackSizeRequest = extern struct {
+    id: [4]u64 = magic(0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d),
+    revision: u64 = 0,
+    response: ?*StackSizeResponse = null,
+    stack_size: u64,
+};
+
+// HHDM
+
+pub const HhdmResponse = extern struct {
+    revision: u64,
+    offset: u64,
+};
+
+pub const HhdmRequest = extern struct {
+    id: [4]u64 = magic(0x48dcf1cb8ad2b852, 0x63984e959a98244b),
+    revision: u64 = 0,
+    response: ?*HhdmResponse = null,
+};
+
+// Framebuffer
+
+pub const FramebufferMemoryModel = enum(u8) {
+    rgb = 1,
+    _,
+};
+
+pub const VideoMode = extern struct {
+    pitch: u64,
+    width: u64,
+    height: u64,
+    bpp: u16,
+    memory_model: u8,
+    red_mask_size: u8,
+    red_mask_shift: u8,
+    green_mask_size: u8,
+    green_mask_shift: u8,
+    blue_mask_size: u8,
+    blue_mask_shift: u8,
+};
+
+pub const Framebuffer = extern struct {
+    address: [*]u8,
+    width: u64,
+    height: u64,
+    pitch: u64,
+    bpp: u16,
+    memory_model: FramebufferMemoryModel,
+    red_mask_size: u8,
+    red_mask_shift: u8,
+    green_mask_size: u8,
+    green_mask_shift: u8,
+    blue_mask_size: u8,
+    blue_mask_shift: u8,
+    unused: [7]u8,
+    edid_size: u64,
+    edid: ?[*]u8,
+
+    // Response revision 1
+    mode_count: u64,
+    modes: [*]*VideoMode,
+
+    pub inline fn data(self: *@This()) []u8 {
+        return self.address[0 .. self.pitch * self.height];
+    }
+
+    pub inline fn edidData(self: *@This()) ?[]u8 {
+        if (self.edid) |edid_data| {
+            return edid_data[0..self.edid_size];
+        }
+        return null;
+    }
+
+    pub inline fn videoModes(self: *@This()) []*VideoMode {
+        return self.modes[0..self.mode_count];
+    }
+};
+
+pub const FramebufferResponse = extern struct {
+    revision: u64,
+    framebuffer_count: u64,
+    framebuffers_ptr: [*]*Framebuffer,
+
+    pub inline fn framebuffers(self: *@This()) []*Framebuffer {
+        return self.framebuffers_ptr[0..self.framebuffer_count];
+    }
+};
+
+pub const FramebufferRequest = extern struct {
+    id: [4]u64 = magic(0x9d5827dcd881dd75, 0xa3148604f6fab11b),
+    revision: u64 = 1,
+    response: ?*FramebufferResponse = null,
+};
+
+// Terminal
+
+pub const Terminal = extern struct {
+    columns: u64,
+    rows: u64,
+    framebuffer: *Framebuffer,
+};
+
+pub const OobOutputFlags = enum(u64) {
+    ocrnl = 1 << 0,
+    ofdel = 1 << 1,
+    ofill = 1 << 2,
+    olcuc = 1 << 3,
+    onlcr = 1 << 4,
+    onlret = 1 << 5,
+    onocr = 1 << 6,
+    opost = 1 << 7,
+};
+
+pub const TerminalResponse = extern struct {
+    revision: u64,
+    terminal_count: u64,
+    terminals_ptr: [*]*Terminal,
+    write_fn: *const fn (*Terminal, [*]const u8, u64) callconv(.C) void,
+
+    pub inline fn terminals(self: *@This()) []*Terminal {
+        return self.terminals_ptr[0..self.terminal_count];
+    }
+
+    pub inline fn write(self: *@This(), terminal: ?*Terminal, string: []const u8) void {
+        self.write_fn(terminal orelse self.terminals_ptr[0], string.ptr, string.len);
+    }
+
+    pub inline fn ctxSize(self: *@This(), terminal: ?*Terminal) u64 {
+        var result: u64 = undefined;
+        self.write_fn(terminal orelse self.terminals_ptr[0], @as([*]const u8, @ptrCast(&result)), @as(u64, @bitCast(@as(i64, -1))));
+        return result;
+    }
+
+    pub inline fn ctxSave(self: *@This(), terminal: ?*Terminal, ctx: [*]u8) void {
+        self.write_fn(terminal orelse self.terminals_ptr[0], @as([*]const u8, @ptrCast(ctx)), @as(u64, @bitCast(@as(i64, -2))));
+    }
+
+    pub inline fn ctxRestore(self: *@This(), terminal: ?*Terminal, ctx: [*]const u8) void {
+        self.write_fn(terminal orelse self.terminals_ptr[0], @as([*]const u8, @ptrCast(ctx)), @as(u64, @bitCast(@as(i64, -3))));
+    }
+
+    pub inline fn fullRefresh(self: *@This(), terminal: ?*Terminal) void {
+        self.write_fn(terminal orelse self.terminals_ptr[0], "", @as(u64, @bitCast(@as(i64, -4))));
+    }
+
+    // Response revision 1
+    pub inline fn oobOutputGet(self: *@This(), terminal: ?*Terminal) u64 {
+        var result: u64 = undefined;
+        self.write_fn(terminal orelse self.terminals_ptr[0], @as([*]const u8, @ptrCast(&result)), @as(u64, @bitCast(@as(i64, -10))));
+        return result;
+    }
+
+    pub inline fn oobOutputSet(self: *@This(), terminal: ?*Terminal, value: u64) void {
+        self.write_fn(terminal orelse self.terminals_ptr[0], @as([*]const u8, @ptrCast(&value)), @as(u64, @bitCast(@as(i64, -11))));
+    }
+};
+
+pub const CallbackType = enum(u64) {
+    dec = 10,
+    bell = 20,
+    private_id = 30,
+    status_report = 40,
+    pos_report = 50,
+    kbd_leds = 60,
+    mode = 70,
+    linux = 80,
+    _,
+};
+
+pub const TerminalRequest = extern struct {
+    id: [4]u64 = magic(0xc8ac59310c2b0844, 0xa68d0c7265d38878),
+    revision: u64 = 0,
+    response: ?*TerminalResponse = null,
+    callback: ?*const fn (*Terminal, CallbackType, u64, u64, u64) callconv(.C) void = null,
+};
+
+// Paging mode
+
+const X86PagingMode = enum(u64) {
+    four_level = 0,
+    five_level = 1,
+    default = .four_level,
+};
+
+const AArch64PagingMode = enum(u64) {
+    four_level = 0,
+    five_level = 1,
+    default = .four_level,
+};
+
+const RiscVPagingMode = enum(u64) {
+    sv39 = 0,
+    sv48 = 1,
+    sv57 = 2,
+    default = .sv48,
+};
+
+pub const PagingMode = switch (builtin.cpu.arch) {
+    .x86, .x86_64 => X86PagingMode,
+    .aarch64 => AArch64PagingMode,
+    .riscv64 => RiscVPagingMode,
+    else => |arch| @compileError("Unsupported architecture: " ++ @tagName(arch)),
+};
+
+pub const PagingModeResponse = extern struct {
+    revision: u64,
+    mode: PagingMode,
+    flags: u64,
+};
+
+pub const PagingModeRequest = extern struct {
+    id: [4]u64 = magic(0x95c1a0edab0944cb, 0xa4e5cb3842f7488a),
+    revision: u64 = 0,
+    response: ?*PagingModeResponse = null,
+    mode: PagingMode,
+    flags: u64,
+};
+
+// 5-level paging
+
+pub const FiveLevelPagingResponse = extern struct {
+    revision: u64,
+};
+
+pub const FiveLevelPagingRequest = extern struct {
+    id: [4]u64 = magic(0x94469551da9b3192, 0xebe5e86db7382888),
+    revision: u64 = 0,
+    response: ?*FiveLevelPagingResponse = null,
+};
+
+// SMP
+const X86SmpInfo = extern struct {
+    processor_id: u32,
+    lapic_id: u32,
+    reserved: u64,
+    goto_address: ?*const fn (*@This()) callconv(.C) noreturn,
+    extra_argument: u64,
+};
+
+const X86SmpFlags = enum(u32) {
+    x2apic = 1 << 0,
+};
+
+const X86SmpResponse = extern struct {
+    revision: u64,
+    flags: u32,
+    bsp_lapic_id: u32,
+    cpu_count: u64,
+    cpus_ptr: [*]*X86SmpInfo,
+
+    pub inline fn cpus(self: *@This()) []*X86SmpInfo {
+        return self.cpus_ptr[0..self.cpu_count];
+    }
+};
+
+const AArch64SmpInfo = extern struct {
+    processor_id: u32,
+    gic_iface_no: u32,
+    mpidr: u64,
+    reserved: u64,
+    goto_address: ?*const fn (*@This()) callconv(.C) noreturn,
+    extra_argument: u64,
+};
+
+const AArch64SmpFlags = enum(u32) {};
+
+const AArch64SmpResponse = extern struct {
+    revision: u64,
+    flags: u32,
+    bsp_mpidr: u64,
+    cpu_count: u64,
+    cpus_ptr: [*]*AArch64SmpInfo,
+
+    pub inline fn cpus(self: *@This()) []*AArch64SmpInfo {
+        return self.cpus_ptr[0..self.cpu_count];
+    }
+};
+
+const RiscVSmpInfo = extern struct {
+    processor_id: u32,
+    hart_id: u32,
+    reserved: u64,
+    goto_address: ?*const fn (*@This()) callconv(.C) noreturn,
+    extra_argument: u64,
+};
+
+const RiscVSmpFlags = enum(u32) {};
+
+const RiscVSmpResponse = extern struct {
+    revision: u64,
+    flags: u32,
+    bsp_hart_id: u64,
+    cpu_count: u64,
+    cpus_ptr: [*]*RiscVSmpInfo,
+
+    pub inline fn cpus(self: *@This()) []*RiscVSmpInfo {
+        return self.cpus_ptr[0..self.cpu_count];
+    }
+};
+
+pub const SmpInfo = switch (builtin.cpu.arch) {
+    .x86, .x86_64 => X86SmpInfo,
+    .aarch64 => AArch64SmpInfo,
+    .riscv64 => RiscVSmpInfo,
+    else => |arch| @compileError("Unsupported architecture: " ++ @tagName(arch)),
+};
+
+pub const SmpFlags = switch (builtin.cpu.arch) {
+    .x86, .x86_64 => X86SmpFlags,
+    .aarch64 => AArch64SmpFlags,
+    .riscv64 => RiscVSmpFlags,
+    else => |arch| @compileError("Unsupported architecture: " ++ @tagName(arch)),
+};
+
+pub const SmpResponse = switch (builtin.cpu.arch) {
+    .x86, .x86_64 => X86SmpResponse,
+    .aarch64 => AArch64SmpResponse,
+    .riscv64 => RiscVSmpResponse,
+    else => |arch| @compileError("Unsupported architecture: " ++ @tagName(arch)),
+};
+
+pub const SmpRequest = extern struct {
+    id: [4]u64 = magic(0x95a67b819a1b857e, 0xa0b61b723b6a73e0),
+    revision: u64 = 0,
+    response: ?*SmpResponse = null,
+    flags: u64 = 0,
+};
+
+// Memory map
+
+pub const MemoryMapEntryType = enum(u64) {
+    usable = 0,
+    reserved = 1,
+    acpi_reclaimable = 2,
+    acpi_nvs = 3,
+    bad_memory = 4,
+    bootloader_reclaimable = 5,
+    kernel_and_modules = 6,
+    framebuffer = 7,
+};
+
+pub const MemoryMapEntry = extern struct {
+    base: u64,
+    length: u64,
+    kind: MemoryMapEntryType,
+};
+
+pub const MemoryMapResponse = extern struct {
+    revision: u64,
+    entry_count: u64,
+    entries_ptr: [*]*MemoryMapEntry,
+
+    pub inline fn entries(self: *@This()) []*MemoryMapEntry {
+        return self.entries_ptr[0..self.entry_count];
+    }
+};
+
+pub const MemoryMapRequest = extern struct {
+    id: [4]u64 = magic(0x67cf3d9d378a806f, 0xe304acdfc50c3c62),
+    revision: u64 = 0,
+    response: ?*MemoryMapResponse = null,
+};
+
+// Entry point
+
+pub const EntryPointResponse = extern struct {
+    revision: u64,
+};
+
+pub const EntryPointRequest = extern struct {
+    id: [4]u64 = magic(0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a),
+    revision: u64 = 0,
+    response: ?*EntryPointResponse = null,
+    entry: ?*const fn () callconv(.C) noreturn = null,
+};
+
+// Kernel file
+
+pub const KernelFileResponse = extern struct {
+    revision: u64,
+    kernel_file: *File,
+};
+
+pub const KernelFileRequest = extern struct {
+    id: [4]u64 = magic(0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69),
+    revision: u64 = 0,
+    response: ?*KernelFileResponse = null,
+};
+
+// Module
+
+pub const InternalModuleFlags = enum(u64) {
+    required = 1 << 0,
+};
+
+pub const InternalModule = extern struct {
+    path: [*:0]const u8,
+    cmdline: [*:0]const u8,
+    flags: InternalModuleFlags,
+};
+
+pub const ModuleResponse = extern struct {
+    revision: u64,
+    module_count: u64,
+    modules_ptr: [*]*File,
+
+    pub inline fn modules(self: *@This()) []*File {
+        return self.modules_ptr[0..self.module_count];
+    }
+};
+
+pub const ModuleRequest = extern struct {
+    id: [4]u64 = magic(0x3e7e279702be32af, 0xca1c4f3bd1280cee),
+    revision: u64 = 1,
+    response: ?*ModuleResponse = null,
+
+    // Request revision 1
+    internal_module_count: u64 = 0,
+    internal_modules: ?[*]const *const InternalModule = null,
+};
+
+// RSDP
+
+pub const RsdpResponse = extern struct {
+    revision: u64,
+    address: *anyopaque,
+};
+
+pub const RsdpRequest = extern struct {
+    id: [4]u64 = magic(0xc5e77b6b397e7b43, 0x27637845accdcf3c),
+    revision: u64 = 0,
+    response: ?*RsdpResponse = null,
+};
+
+// SMBIOS
+
+pub const SmbiosResponse = extern struct {
+    revision: u64,
+    entry_32: ?*anyopaque,
+    entry_64: ?*anyopaque,
+};
+
+pub const SmbiosRequest = extern struct {
+    id: [4]u64 = magic(0x9e9046f11e095391, 0xaa4a520fefbde5ee),
+    revision: u64 = 0,
+    response: ?*SmbiosResponse = null,
+};
+
+// EFI system table
+
+pub const EfiSystemStableResponse = extern struct {
+    revision: u64,
+    address: *anyopaque,
+};
+
+pub const EfiSystemTableRequest = extern struct {
+    id: [4]u64 = magic(0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc),
+    revision: u64 = 0,
+    response: ?*EfiSystemStableResponse = null,
+};
+
+// EFI memory map
+
+pub const EfiMemoryMapResponse = extern struct {
+    revision: u64,
+    memmap: *anyopaque,
+    memmap_size: u64,
+    desc_size: u64,
+    desc_version: u64,
+};
+
+pub const EfiMemoryMapRequest = extern struct {
+    id: [4]u64 = magic(0x7df62a431d6872d5, 0xa4fcdfb3e57306c8),
+    revision: u64 = 0,
+    response: ?*EfiMemoryMapResponse = null,
+};
+
+// Boot time
+
+pub const BootTimeResponse = extern struct {
+    revision: u64,
+    boot_time: i64,
+};
+
+pub const BootTimeRequest = extern struct {
+    id: [4]u64 = magic(0x502746e184c088aa, 0xfbc5ec83e6327893),
+    revision: u64 = 0,
+    response: ?*BootTimeResponse = null,
+};
+
+// Kernel address
+
+pub const KernelAddressResponse = extern struct {
+    revision: u64,
+    physical_base: u64,
+    virtual_base: u64,
+};
+
+pub const KernelAddressRequest = extern struct {
+    id: [4]u64 = magic(0x71ba76863cc55f63, 0xb2644a48c516a487),
+    revision: u64 = 0,
+    response: ?*KernelAddressResponse = null,
+};
+
+// Device tree blob
+
+pub const DeviceTreeBlobResponse = extern struct {
+    revision: u64,
+    dtb: ?*anyopaque,
+};
+
+pub const DeviceTreeBlobRequest = extern struct {
+    id: [4]u64 = magic(0xb40ddb48fb54bac7, 0x545081493f81ffb7),
+    revision: u64 = 0,
+    response: ?*DeviceTreeBlobResponse = null,
+};
diff --git a/kernel/deps/limine/zig-cache/h/5b6ecd7523182e2589cc991d9a944676.txt b/kernel/deps/limine/zig-cache/h/5b6ecd7523182e2589cc991d9a944676.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/kernel/deps/limine/zig-cache/h/timestamp b/kernel/deps/limine/zig-cache/h/timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/kernel/deps/limine/zig-cache/o/df65dd7eb12dada13f920fe098a02adf/dependencies.zig b/kernel/deps/limine/zig-cache/o/df65dd7eb12dada13f920fe098a02adf/dependencies.zig
new file mode 100644 (file)
index 0000000..72e4e83
--- /dev/null
@@ -0,0 +1,2 @@
+pub const packages = struct {};
+pub const root_deps: []const struct { []const u8, []const u8 } = &.{};
diff --git a/kernel/deps/limine/zig-cache/z/7cac848da18911d1686214078038a257 b/kernel/deps/limine/zig-cache/z/7cac848da18911d1686214078038a257
new file mode 100644 (file)
index 0000000..64fd5b4
Binary files /dev/null and b/kernel/deps/limine/zig-cache/z/7cac848da18911d1686214078038a257 differ
diff --git a/kernel/linker.ld b/kernel/linker.ld
new file mode 100644 (file)
index 0000000..b51359a
--- /dev/null
@@ -0,0 +1,47 @@
+OUTPUT_FORMAT(elf64-x86-64)
+OUTPUT_ARCH(i386:x86-64)
+
+ENTRY(_start)
+
+PHDRS
+{
+    text    PT_LOAD    FLAGS((1 << 0) | (1 << 2)); /* Execute + Read */
+    rodata  PT_LOAD    FLAGS((1 << 2));            /* Read only */
+    data    PT_LOAD    FLAGS((1 << 1) | (1 << 2)); /* Write + Read */
+    dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)); /* Dynamic PHDR for relocations */
+}
+
+SECTIONS
+{
+    . = 0xffffffff80000000;
+
+    .text : {
+        *(.text .text.*)
+    } :text
+
+    . = ALIGN(4K);
+
+    .rodata : {
+        *(.rodata .rodata.*)
+    } :rodata
+
+    . = ALIGN(4K);
+
+    .data : {
+        *(.data .data.*)
+    } :data
+
+    .dynamic : {
+        *(.dynamic)
+    } :data :dynamic
+
+    .bss : {
+        *(.bss .bss.*)
+        *(COMMON)
+    } :data
+
+    /DISCARD/ : {
+        *(.eh_frame)
+        *(.note .note.*)
+    }
+}
diff --git a/kernel/src/FONT.F16 b/kernel/src/FONT.F16
new file mode 100644 (file)
index 0000000..ee47a54
Binary files /dev/null and b/kernel/src/FONT.F16 differ
diff --git a/kernel/src/cli.zig b/kernel/src/cli.zig
new file mode 100644 (file)
index 0000000..1092b5f
--- /dev/null
@@ -0,0 +1,10 @@
+const std = @import("std");
+const log = std.log.scoped(.cli);
+const srl = @import("./srl.zig");
+
+pub fn run() void {
+    while (true) {
+        const byte = srl.read(u8);
+        log.debug("input: {X}", .{byte});
+    }
+}
diff --git a/kernel/src/dbg.zig b/kernel/src/dbg.zig
new file mode 100644 (file)
index 0000000..fe24334
--- /dev/null
@@ -0,0 +1,10 @@
+const std = @import("std");
+const dbg = @import("arch").dbg;
+
+pub var writer = std.io.Writer(std.log.Level, error{}, callback){ .context = std.log.Level.info };
+fn callback(_: std.log.Level, string: []const u8) error{}!usize {
+    for (string) |character| {
+        dbg.out(character);
+    }
+    return string.len;
+}
diff --git a/kernel/src/fb.zig b/kernel/src/fb.zig
new file mode 100644 (file)
index 0000000..b994039
--- /dev/null
@@ -0,0 +1,78 @@
+const std = @import("std");
+const log = std.log.scoped(.fb);
+const limine = @import("limine");
+const font = @import("./font.zig");
+
+const MARGIN = 5;
+
+const Color = u32;
+
+var framebuffer: *limine.Framebuffer = undefined;
+var row: usize = 0;
+var column: usize = 0;
+var foreground: Color = 0xFFFFFF;
+var background: Color = 0x000000;
+
+fn nextline() void {
+    column = 0;
+    if ((2 * MARGIN) + ((row + 1) * font.HEIGHT) > framebuffer.height) {
+        scroll();
+        return;
+    }
+    row += 1;
+}
+
+fn scroll() void {}
+
+pub var writer = std.io.Writer(std.log.Level, error{}, callback){ .context = std.log.Level.info };
+fn callback(level: std.log.Level, string: []const u8) error{}!usize {
+    if (framebuffer == undefined)
+        return 0;
+    foreground = switch (level) {
+        .debug => 0x0000FF,
+        .info => 0xFFFFFF,
+        .warn => 0xFFFF00,
+        .err => 0xFF0000,
+    };
+    for (string) |character| {
+        const vertical_offset = MARGIN + (row * font.HEIGHT);
+        const horizontal_offset = MARGIN + (column * font.WIDTH);
+        if (character == '\n') {
+            nextline();
+            continue;
+        } else if (character == '\t') {
+            column += 4;
+            continue;
+        } else if (horizontal_offset > framebuffer.width - font.WIDTH - MARGIN) {
+            nextline();
+        }
+        for (0..font.HEIGHT) |i| {
+            var offset = ((vertical_offset + i) * framebuffer.pitch) + (horizontal_offset * 4);
+            var mask: u8 = 1 << 7;
+            for (0..font.WIDTH) |_| {
+                if (font.characters[character][i] & mask != 0) {
+                    @as(*u32, @ptrCast(@alignCast(framebuffer.address + offset))).* = @bitCast(foreground);
+                } else {
+                    @as(*u32, @ptrCast(@alignCast(framebuffer.address + offset))).* = @bitCast(background);
+                }
+                mask >>= 1;
+                offset += 4;
+            }
+        }
+        column += 1;
+    }
+    return string.len;
+}
+
+export var fb_request: limine.FramebufferRequest = .{};
+
+pub fn init() void {
+    const response = fb_request.response orelse {
+        log.err("request not fufilled", .{});
+        return;
+    };
+    if (response.framebuffer_count < 1) {
+        log.info("none found", .{});
+    }
+    framebuffer = response.framebuffers()[0];
+}
diff --git a/kernel/src/font.zig b/kernel/src/font.zig
new file mode 100644 (file)
index 0000000..8d0c59b
--- /dev/null
@@ -0,0 +1,19 @@
+const std = @import("std");
+
+const FONT = "./FONT.F16";
+pub const WIDTH = 8;
+pub const HEIGHT = 16;
+
+pub const characters = characters: {
+    @setEvalBranchQuota(10000);
+    const file = @embedFile(FONT);
+    var character_map: [256][HEIGHT]u8 = undefined;
+    for (0..256) |index| {
+        var rows: [HEIGHT]u8 = undefined;
+        for (0..HEIGHT) |row| {
+            rows[row] = file[(index * HEIGHT) + row];
+        }
+        character_map[index] = rows;
+    }
+    break :characters character_map;
+};
diff --git a/kernel/src/log.zig b/kernel/src/log.zig
new file mode 100755 (executable)
index 0000000..054315d
--- /dev/null
@@ -0,0 +1,39 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
+const dbg = @import("./dbg.zig");
+const srl = @import("./srl.zig");
+const fb = @import("./fb.zig");
+
+const MAX = size: {
+    var max = 0;
+    const levels = @typeInfo(std.log.Level).Enum.fields;
+    for (0..levels.len) |i| {
+        if (levels[i].name.len > max) {
+            max = levels[i].name.len;
+        }
+    }
+    break :size max;
+};
+
+pub fn print(
+    comptime level: std.log.Level,
+    comptime scope: @TypeOf(.EnumLiteral),
+    comptime format: []const u8,
+    args: anytype,
+) void {
+    const prefix = "[" ++ @tagName(level) ++ "]" ++ [1]u8{' '} ** (MAX - @tagName(level).len) ++ " " ++ "(" ++ switch (scope) {
+        .default => "krl",
+        else => @tagName(scope),
+    } ++ "): ";
+    const suffix = "\n";
+    const log = prefix ++ format ++ suffix;
+    if (builtin.mode == .Debug) {
+        dbg.writer.context = level;
+        dbg.writer.print(log, args) catch {};
+    }
+    srl.writer.context = level;
+    srl.writer.print(log, args) catch {};
+    fb.writer.context = level;
+    fb.writer.print(log, args) catch {};
+}
diff --git a/kernel/src/main.zig b/kernel/src/main.zig
new file mode 100755 (executable)
index 0000000..8b4e542
--- /dev/null
@@ -0,0 +1,48 @@
+const std = @import("std");
+const limine = @import("limine");
+
+const arch = @import("arch");
+const srl = @import("./srl.zig");
+const fb = @import("./fb.zig");
+const mem = @import("./mem.zig");
+const cli = @import("./cli.zig");
+
+pub const std_options = struct {
+    pub const logFn = @import("./log.zig").print;
+};
+
+pub const os = struct {
+    pub const heap = struct {
+        pub const page_allocator = mem.allocator;
+    };
+};
+
+export var base_revision: limine.BaseRevision = .{ .revision = 1 };
+
+export fn _start() callconv(.C) noreturn {
+    if (!base_revision.is_supported())
+        std.debug.panic("Unsupported version", .{});
+
+    srl.init();
+    fb.init();
+
+    std.log.info("booting", .{});
+    arch.init();
+
+    std.log.info("starting", .{});
+    std.log.debug("Something happened!", .{});
+    std.log.info("Hello World!", .{});
+    std.log.warn("Be warned!", .{});
+    std.log.err("Something went wrong!", .{});
+    // std.debug.panic("something really fucked up!", .{});
+    cli.run();
+    while (true) {}
+}
+
+pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_address: ?usize) noreturn {
+    @setCold(true);
+    _ = return_address;
+    _ = stack_trace;
+    std.log.err("panic!\nmsg: {s}", .{message});
+    while (true) {}
+}
diff --git a/kernel/src/mem.zig b/kernel/src/mem.zig
new file mode 100644 (file)
index 0000000..33283fe
--- /dev/null
@@ -0,0 +1,10 @@
+const std = @import("std");
+
+const allocator = std.mem.Allocator{
+    .ptr = undefined,
+    .vtable = .{
+        .alloc = undefined,
+        .resize = undefined,
+        .free = undefined,
+    },
+};
diff --git a/kernel/src/srl.zig b/kernel/src/srl.zig
new file mode 100644 (file)
index 0000000..3c2edcb
--- /dev/null
@@ -0,0 +1,157 @@
+const std = @import("std");
+const srl = @import("arch").srl;
+
+const Interrupt_Enable_Register = packed struct(u8) {
+    transmitter_empty: bool = false,
+    data_available: bool = false,
+    receiver_status: bool = false,
+    modem_status: bool = false,
+    sleep_mode: bool = false,
+    low_power_mode: bool = false,
+    _: u2 = 0,
+
+    fn transmit(self: Interrupt_Enable_Register) void {
+        srl.Port.COM2.out(@as(u8, @bitCast(self)));
+    }
+};
+
+const Line_Control_Register = packed struct(u8) {
+    length: CharacterLength = CharacterLength{},
+    stop_bits: StopBits = .one,
+    parity: Parity = .none,
+    break_enable: bool = false,
+    divisor_latch_access: bool = false,
+
+    const Parity = enum(u3) {
+        none = 0b000,
+        odd = 0b001,
+        even = 0b011,
+        mark = 0b101,
+        space = 0b111,
+    };
+
+    const StopBits = enum(u1) {
+        one = 0,
+        two = 1,
+    };
+
+    const CharacterLength = packed struct(u2) {
+        size: u2 = 8 - 5,
+
+        pub fn from(comptime bits: u4) CharacterLength {
+            return CharacterLength{ .size = bits - 5 };
+        }
+    };
+
+    fn transmit(self: Line_Control_Register) void {
+        srl.Port.COM4.out(@as(u8, @bitCast(self)));
+    }
+};
+
+const FIFO_Control_Register = packed struct(u8) {
+    enabled: bool = false,
+    clear_recieve: bool = false,
+    clear_transmit: bool = false,
+    dma_mode: u1 = 0,
+    _: u1 = 0,
+    large: bool = false, // 64 bytes
+    size: FIFOBufferSize = .byte,
+
+    const FIFOBufferSize = enum(u2) {
+        byte = 0b00, // 1 byte
+        word = 0b01, // 2 bytes
+        double_word = 0b10, // 4 bytes
+        huge = 0b11, // 14 bytes
+    };
+
+    fn transmit(self: FIFO_Control_Register) void {
+        srl.Port.COM3.out(@as(u8, @bitCast(self)));
+    }
+};
+
+const Modem_Control_Register = packed struct(u8) {
+    recieve: bool = false,
+    transmit: bool = false,
+    aux_output1: bool = false,
+    aux_output2: bool = false,
+    loopback: bool = false,
+    autoflow: bool = false,
+    _: u2 = 0,
+
+    fn transmit(self: Modem_Control_Register) void {
+        srl.Port.COM5.out(@as(u8, @bitCast(self)));
+    }
+};
+
+const Line_Status_Register = packed struct(u8) {
+    data_ready: bool,
+    overrun_error: bool,
+    parity_error: bool,
+    framing_error: bool,
+    break_interrupt: bool,
+    transmitter_empty: bool,
+    data_holding_empty: bool,
+    fifo_error: bool,
+
+    fn read() Line_Status_Register {
+        return @as(Line_Status_Register, @bitCast(srl.Port.COM6.in(u8)));
+    }
+};
+
+pub fn set_speed(divisor: u16) void {
+    Line_Control_Register.transmit(.{
+        .divisor_latch_access = true,
+    });
+    srl.Port.COM1.out(@as(u8, @truncate(divisor)));
+    srl.Port.COM2.out(@as(u8, @truncate(divisor >> 8)));
+}
+
+pub fn data_recieved() bool {
+    return Line_Status_Register.read().data_ready;
+}
+
+pub fn transmitter_ready() bool {
+    return Line_Status_Register.read().transmitter_empty;
+}
+
+pub fn read(comptime Type: type) Type {
+    while (!data_recieved()) {}
+    return srl.Port.COM1.in(Type);
+}
+
+pub fn write(byte: u8) void {
+    while (!transmitter_ready()) {}
+    srl.Port.COM1.out(byte);
+}
+
+pub var writer = std.io.Writer(std.log.Level, error{}, callback){ .context = std.log.Level.info };
+fn callback(_: std.log.Level, string: []const u8) error{}!usize {
+    for (string) |character| {
+        write(character);
+    }
+    return string.len;
+}
+
+pub fn init() void {
+    Interrupt_Enable_Register.transmit(.{});
+
+    set_speed(1);
+
+    Line_Control_Register.transmit(.{
+        .length = Line_Control_Register.CharacterLength.from(@bitSizeOf(u8)),
+        .stop_bits = .one,
+        .parity = .none,
+    });
+
+    FIFO_Control_Register.transmit(.{
+        .enabled = true,
+        .size = .byte,
+        .clear_recieve = true,
+        .clear_transmit = true,
+    });
+
+    Modem_Control_Register.transmit(.{
+        .recieve = true,
+        .transmit = true,
+    });
+}
diff --git a/kernel/src/x86.zig b/kernel/src/x86.zig
new file mode 100644 (file)
index 0000000..2008681
--- /dev/null
@@ -0,0 +1,22 @@
+// cpu
+pub const int = @import("./x86/int.zig");
+pub const idt = @import("./x86/idt.zig");
+
+// memory
+pub const gdt = @import("./x86/gdt.zig");
+pub const pmm = @import("./x86/pmm.zig");
+pub const pgn = @import("./x86/pgn.zig");
+
+// io
+pub const io = @import("./x86/io.zig");
+pub const dbg = @import("./x86/dbg.zig");
+pub const srl = @import("./x86/srl.zig");
+
+pub fn init() void {
+    int.disable();
+    gdt.init();
+    pmm.init();
+    pgn.init();
+    idt.init();
+    int.enable();
+}
diff --git a/kernel/src/x86/dbg.zig b/kernel/src/x86/dbg.zig
new file mode 100644 (file)
index 0000000..ea99d65
--- /dev/null
@@ -0,0 +1,7 @@
+const io = @import("./io.zig");
+
+const DEBUG_PORT = 0xE9;
+
+pub fn out(data: anytype) void {
+    io.out(DEBUG_PORT, data);
+}
diff --git a/kernel/src/x86/gdt.zig b/kernel/src/x86/gdt.zig
new file mode 100644 (file)
index 0000000..f6a68e2
--- /dev/null
@@ -0,0 +1,203 @@
+const std = @import("std");
+const log = std.log.scoped(.gdt);
+
+const Table = enum(u1) {
+    gdt = 0,
+    ldt = 1,
+};
+
+const Type = enum(u2) {
+    null = 0,
+    code = 0b11,
+    data = 0b10,
+};
+
+const Flags = packed struct(u4) {
+    const Mode = enum(u2) {
+        bits16 = 0b00, // 16 bit
+        bits32 = 0b10, // 32 bit
+        bits64 = 0b01, // 64 bit
+    };
+    const Granularity = enum(u1) {
+        byte = 0, // one byte
+        page = 1, // 4 kilobytes
+    };
+
+    _: u1 = 0,
+    mode: Mode = .bits32,
+    granularity: Granularity = .page,
+};
+
+const NullSegment = packed struct(u64) {
+    _: usize = 0,
+};
+
+const CodeSegment = packed struct(u64) {
+    const Access = packed struct(u8) {
+        accessed: bool = false,
+        readable: bool = true,
+        unprivilaged: bool = false,
+        kind: Type = .code,
+        level: u2,
+        present: bool = true,
+    };
+
+    limit_low: u16,
+    base_low: u24,
+    access: Access,
+    limit_high: u4,
+    flags: Flags,
+    base_high: u8,
+
+    fn new(base: u32, limit: u20, access: Access, flags: Flags) CodeSegment {
+        return CodeSegment{
+            .base_low = @truncate(base),
+            .base_high = @truncate(base >> 24),
+            .limit_low = @truncate(limit),
+            .limit_high = @truncate(limit >> 16),
+            .access = access,
+            .flags = flags,
+        };
+    }
+};
+
+const DataSegment = packed struct(u64) {
+    const Access = packed struct(u8) {
+        const Direction = enum(u1) {
+            up = 0,
+            down = 1,
+        };
+        accessed: bool = false,
+        writable: bool = true,
+        direction: Direction = .up,
+        kind: Type = .data,
+        level: u2,
+        present: bool = true,
+    };
+
+    limit_low: u16,
+    base_low: u24,
+    access: Access,
+    limit_high: u4,
+    flags: Flags,
+    base_high: u8,
+
+    fn new(base: u32, limit: u20, access: Access, flags: Flags) DataSegment {
+        return .{
+            .base_low = @truncate(base),
+            .base_high = @truncate(base >> 24),
+            .limit_low = @truncate(limit),
+            .limit_high = @truncate(limit >> 16),
+            .access = access,
+            .flags = flags,
+        };
+    }
+};
+
+var entries = [_]u64{
+    @bitCast(
+        NullSegment{},
+    ),
+    @bitCast(
+        CodeSegment.new(
+            0,
+            std.math.maxInt(u20),
+            .{
+                .level = 0,
+            },
+            .{
+                .mode = .bits64,
+            },
+        ),
+    ),
+    @bitCast(
+        DataSegment.new(
+            0,
+            std.math.maxInt(u20),
+            .{
+                .level = 0,
+            },
+            .{},
+        ),
+    ),
+    @bitCast(
+        CodeSegment.new(
+            0,
+            std.math.maxInt(u20),
+            .{
+                .level = 3,
+            },
+            .{
+                .mode = .bits64,
+            },
+        ),
+    ),
+    @bitCast(
+        DataSegment.new(
+            0,
+            std.math.maxInt(u20),
+            .{
+                .level = 3,
+            },
+            .{},
+        ),
+    ),
+};
+
+pub const Selector = packed struct(u16) {
+    level: u2,
+    table: Table,
+    index: u13,
+
+    pub const KERNEL_CODE = Selector{
+        .index = 1,
+        .level = 0,
+        .table = .gdt,
+    };
+    pub const KERNEL_DATA = Selector{
+        .index = 2,
+        .level = 0,
+        .table = .gdt,
+    };
+};
+
+const Descriptor = packed struct(u80) {
+    size: u16,
+    base: u64,
+
+    fn new(base: u64, size: u16) Descriptor {
+        return .{
+            .base = base,
+            .size = size - 1,
+        };
+    }
+
+    fn load(self: Descriptor) void {
+        asm volatile ("lgdt (%%rax)"
+            :
+            : [ptr] "{rax}" (&self),
+        );
+        log.debug("loaded", .{});
+        asm volatile (
+            \\push $0x08
+            \\leaq reload(%rip), %%rax
+            \\push %%rax
+            \\lretq
+            \\reload:
+            \\mov $0x10, %%ax
+            \\mov %%ax, %%ds
+            \\mov %%ax, %%ss
+            \\mov %%ax, %%es
+            \\mov %%ax, %%fs
+            \\mov %%ax, %%gs
+        );
+        log.debug("flushed segment registers", .{});
+    }
+};
+
+var descriptor: Descriptor = Descriptor.new(undefined, @sizeOf(@TypeOf(entries)));
+
+pub fn init() void {
+    descriptor.base = @intFromPtr(&entries);
+    descriptor.load();
+}
diff --git a/kernel/src/x86/idt.zig b/kernel/src/x86/idt.zig
new file mode 100644 (file)
index 0000000..1762a8e
--- /dev/null
@@ -0,0 +1,91 @@
+const std = @import("std");
+const log = std.log.scoped(.idt);
+const int = @import("./int.zig");
+const gdt = @import("./gdt.zig");
+
+const Gate = packed struct(u128) {
+    const Type = enum(u4) {
+        interrupt = 0xE,
+        trap = 0xF,
+    };
+
+    address_low: u16,
+    segment: gdt.Selector = gdt.Selector.KERNEL_CODE,
+    ist_offset: u3 = 0,
+    _: u5 = 0,
+    kind: Type,
+    _2: u1 = 0,
+    level: u2 = 0,
+    present: bool = true,
+    address_high: u48,
+    _3: u32 = 0,
+
+    fn new(handler: usize, kind: Type) Gate {
+        return .{
+            .address_low = @truncate(handler),
+            .address_high = @truncate(handler >> 16),
+            .kind = kind,
+        };
+    }
+};
+
+var entries: [256]Gate = undefined;
+
+const Descriptor = packed struct(u80) {
+    size: u16,
+    base: u64,
+
+    fn new(base: u64, size: u16) Descriptor {
+        return .{
+            .base = base,
+            .size = size - 1,
+        };
+    }
+
+    fn load(self: Descriptor) void {
+        asm volatile ("lidt (%%rax)"
+            :
+            : [ptr] "{rax}" (&self),
+        );
+    }
+};
+
+var descriptor: Descriptor = Descriptor.new(undefined, @sizeOf(@TypeOf(entries)));
+
+pub fn init() void {
+    descriptor.base = @intFromPtr(&entries);
+
+    entries[0] = Gate.new(@intFromPtr(&int.division_error), .interrupt);
+    entries[1] = Gate.new(@intFromPtr(&int.debug), .interrupt);
+    entries[2] = Gate.new(@intFromPtr(&int.nonmaskable_interrupt), .interrupt);
+    entries[3] = Gate.new(@intFromPtr(&int.breakpoint), .interrupt);
+    entries[4] = Gate.new(@intFromPtr(&int.overflow), .interrupt);
+    entries[5] = Gate.new(@intFromPtr(&int.bound_range_exceeded), .interrupt);
+    entries[6] = Gate.new(@intFromPtr(&int.invalid_opcode), .interrupt);
+    entries[7] = Gate.new(@intFromPtr(&int.device_not_available), .interrupt);
+    entries[8] = Gate.new(@intFromPtr(&int.double_fault), .interrupt);
+    entries[9] = Gate.new(@intFromPtr(&int.unhandled), .interrupt);
+    entries[10] = Gate.new(@intFromPtr(&int.invalid_tss), .interrupt);
+    entries[11] = Gate.new(@intFromPtr(&int.segment_not_present), .interrupt);
+    entries[12] = Gate.new(@intFromPtr(&int.stack_segment_fault), .interrupt);
+    entries[13] = Gate.new(@intFromPtr(&int.general_protection_fault), .interrupt);
+    entries[14] = Gate.new(@intFromPtr(&int.page_fault), .interrupt);
+    entries[15] = Gate.new(@intFromPtr(&int.unhandled), .interrupt);
+    entries[16] = Gate.new(@intFromPtr(&int.floating_point_exception), .interrupt);
+    entries[17] = Gate.new(@intFromPtr(&int.alignment_check), .interrupt);
+    entries[18] = Gate.new(@intFromPtr(&int.machine_check), .interrupt);
+    entries[19] = Gate.new(@intFromPtr(&int.simd_floating_point_exception), .interrupt);
+    entries[20] = Gate.new(@intFromPtr(&int.virtualization_exception), .interrupt);
+    entries[21] = Gate.new(@intFromPtr(&int.control_protection_exception), .interrupt);
+    inline for (22..28) |i| {
+        entries[i] = Gate.new(@intFromPtr(&int.unhandled), .interrupt);
+    }
+    entries[28] = Gate.new(@intFromPtr(&int.hypervisor_injection_exception), .interrupt);
+    entries[29] = Gate.new(@intFromPtr(&int.vmm_communication_exception), .interrupt);
+    entries[30] = Gate.new(@intFromPtr(&int.security_exception), .interrupt);
+    inline for (31..256) |i| {
+        entries[i] = Gate.new(@intFromPtr(&int.unhandled), .interrupt);
+    }
+
+    descriptor.load();
+}
diff --git a/kernel/src/x86/int.zig b/kernel/src/x86/int.zig
new file mode 100644 (file)
index 0000000..bc5536d
--- /dev/null
@@ -0,0 +1,108 @@
+const std = @import("std");
+const log = std.log.scoped(.int);
+
+pub fn disable() void {
+    asm volatile ("cli");
+    log.debug("disabled", .{});
+}
+
+pub fn enable() void {
+    asm volatile ("sti");
+    log.debug("enabled", .{});
+}
+
+pub fn unhandled() callconv(.Interrupt) void {
+    log.debug("unimplemented", .{});
+}
+
+pub fn division_error() callconv(.Interrupt) void {
+    log.err("divison error", .{});
+}
+
+pub fn debug() callconv(.Interrupt) void {
+    log.debug("debug", .{});
+}
+
+pub fn nonmaskable_interrupt() callconv(.Interrupt) void {
+    log.err("non-maskable interrupt", .{});
+}
+
+pub fn breakpoint() callconv(.Interrupt) void {
+    log.debug("breakpoint", .{});
+}
+
+pub fn overflow() callconv(.Interrupt) void {
+    log.err("overflow", .{});
+}
+
+pub fn bound_range_exceeded() callconv(.Interrupt) void {
+    log.err("bound range exceeded", .{});
+}
+
+pub fn invalid_opcode() callconv(.Interrupt) void {
+    log.err("invalid opcode", .{});
+}
+
+pub fn device_not_available() callconv(.Interrupt) void {
+    log.err("device not available", .{});
+}
+
+pub fn double_fault(_: u64) callconv(.Interrupt) void {
+    log.err("double fault", .{});
+}
+
+pub fn invalid_tss(code: u64) callconv(.Interrupt) void {
+    log.err("invalid tss: {X}", .{code});
+}
+
+pub fn segment_not_present(code: u64) callconv(.Interrupt) void {
+    log.err("segment not present: {X}", .{code});
+}
+
+pub fn stack_segment_fault(code: u64) callconv(.Interrupt) void {
+    log.err("stack segment fault: {X}", .{code});
+}
+
+pub fn general_protection_fault(code: u64) callconv(.Interrupt) void {
+    log.err("general protection fault: {X}", .{code});
+}
+
+pub fn page_fault(code: u64) callconv(.Interrupt) void {
+    log.err("page fault: {X}", .{code});
+}
+
+pub fn floating_point_exception() callconv(.Interrupt) void {
+    log.err("floating point exception", .{});
+}
+
+pub fn alignment_check(code: u64) callconv(.Interrupt) void {
+    log.err("alignment check: {X}", .{code});
+}
+
+pub fn machine_check() callconv(.Interrupt) void {
+    log.err("machine check", .{});
+}
+
+pub fn simd_floating_point_exception() callconv(.Interrupt) void {
+    log.err("simd floating point exception", .{});
+}
+
+pub fn virtualization_exception() callconv(.Interrupt) void {
+    log.err("virtualization exception", .{});
+}
+
+pub fn control_protection_exception(code: u64) callconv(.Interrupt) void {
+    log.err("control protection exception: {}", .{code});
+}
+
+pub fn hypervisor_injection_exception() callconv(.Interrupt) void {
+    log.err("hypervisor injection exception", .{});
+}
+
+pub fn vmm_communication_exception(code: u64) callconv(.Interrupt) void {
+    log.err("vmm communication exception: {}", .{code});
+}
+
+pub fn security_exception(code: u64) callconv(.Interrupt) void {
+    log.err("security exception: {}", .{code});
+}
diff --git a/kernel/src/x86/io.zig b/kernel/src/x86/io.zig
new file mode 100644 (file)
index 0000000..bc6c51d
--- /dev/null
@@ -0,0 +1,44 @@
+const WAIT_PORT: u16 = 0x80;
+
+pub inline fn in(port: u16, comptime Type: type) Type {
+    return switch (Type) {
+        u8 => asm volatile ("inb %[port], %[result]"
+            : [result] "={al}" (-> Type),
+            : [port] "N{dx}" (port),
+        ),
+        u16 => asm volatile ("inw %[port], %[result]"
+            : [result] "={ax}" (-> Type),
+            : [port] "N{dx}" (port),
+        ),
+        u32 => asm volatile ("inl %[port], %[result]"
+            : [result] "={eax}" (-> Type),
+            : [port] "N{dx}" (port),
+        ),
+        else => @compileError("Invalid data type. Only u8, u16, and u32 are allowed. Found: " ++ @typeName(@TypeOf(Type))),
+    };
+}
+
+pub inline fn out(port: u16, data: anytype) void {
+    switch (@TypeOf(data)) {
+        u8 => asm volatile ("outb %[data], %[port]"
+            :
+            : [port] "{dx}" (port),
+              [data] "{al}" (data),
+        ),
+        u16 => asm volatile ("outw %[data], %[port]"
+            :
+            : [port] "{dx}" (port),
+              [data] "{ax}" (data),
+        ),
+        u32 => asm volatile ("outl %[data], %[port]"
+            :
+            : [port] "{dx}" (port),
+              [data] "{eax}" (data),
+        ),
+        else => @compileError("Invalid data type. Only u8, u16, and u32 are allowed. Found: " ++ @typeName(@TypeOf(data))),
+    }
+}
+
+pub inline fn wait() void {
+    out(WAIT_PORT, @as(u8, 0));
+}
diff --git a/kernel/src/x86/lvl.zig b/kernel/src/x86/lvl.zig
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/kernel/src/x86/pgn.zig b/kernel/src/x86/pgn.zig
new file mode 100644 (file)
index 0000000..e17c44e
--- /dev/null
@@ -0,0 +1,60 @@
+const std = @import("std");
+const pmm = @import("./pmm.zig");
+
+pub const PAGE_SIZE: usize = 4096;
+
+const RECURSE: u9 = std.math.maxInt(u9);
+const KERNEL: u9 = RECURSE - 1;
+
+const Entry = packed struct(u64) {
+    const Access = enum(u1) {
+        supervisor = 0,
+        user = 1,
+    };
+
+    present: bool = true,
+    writable: bool = true,
+    access: Access = .supervisor,
+    write_through: bool = false,
+    cache_disabled: bool = false,
+    accessed: bool = false,
+    dirty: bool = false,
+    _: u2 = 0,
+    unused: u3 = 0, // free to use by us
+    pte: u12,
+    pde: u9,
+    pdpte: u9,
+    pml4e: u9,
+    unused2: u4 = 0,
+    execute_disable: bool = false,
+};
+
+const Table = packed struct(u64) {
+    _: u3 = 0,
+    write_through: bool = false,
+    cache_disabled: bool = false,
+    _2: u7 = 0,
+    address: u40,
+    _3: u12 = 0,
+};
+
+pub fn init() void {
+    // asm volatile ("mov %%rax, %%cr3"
+    //     :
+    //     : [pdpt] "{rax}" (&table),
+    // );
+}
+
+fn page_table(lvl4: u9, lvl3: u9, lvl2: u9, lvl1: u9, offset: u12) *Entry {
+    return @ptrFromInt((lvl4 << 39) | (lvl3 << 30) | (lvl2 << 21) | (lvl1 << 12) | offset);
+}
+
+pub fn map(virtual_address: usize, physical_address: usize) void {
+    const pml4e: u9 = virtual_address >> 39;
+    const pdpte: u9 = virtual_address >> 30;
+    const pde: u9 = virtual_address >> 21;
+    const pte: u9 = virtual_address >> 12;
+
+    const pt = page_table(RECURSE, RECURSE, RECURSE, RECURSE);
+}
+
diff --git a/kernel/src/x86/pic.zig b/kernel/src/x86/pic.zig
new file mode 100644 (file)
index 0000000..6c08045
--- /dev/null
@@ -0,0 +1,205 @@
+const io = @import("./io.zig");
+
+const ICW1 = packed struct(u8) {
+    ICW4_needed: bool = false,
+    setup: Controller_Setup = .cascading,
+    call_address_interval: Call_Address_Interval = .EVERY8,
+    trigger_mode: Trigger_Mode = .edge,
+    _: u4 = 1,
+
+    const Trigger_Mode = enum(u1) {
+        edge = 0,
+        level = 1,
+    };
+
+    const Call_Address_Interval = enum(u1) {
+        EVERY4 = 1,
+        EVERY8 = 0,
+    };
+
+    const Controller_Setup = enum(u1) {
+        single = 1,
+        cascading = 0,
+    };
+
+    fn transmit(self: ICW1) void {
+        Master.command.out(@as(u8, @bitCast(self)));
+        Slave.command.out(@as(u8, @bitCast(self)));
+    }
+};
+
+const ICW2 = struct {
+    master_ivt_offset: ?u8 = null,
+    slave_ivt_offset: ?u8 = null,
+
+    fn transmit(self: ICW2) void {
+        if (self.master_ivt_offset) |offset| {
+            Master.data.out(offset);
+        }
+        if (self.slave_ivt_offset) |offset| {
+            Slave.data.out(offset);
+        }
+    }
+};
+
+const ICW3 = enum(u3) {
+    IRQ0_has_slave = 0,
+    IRQ1_has_slave = 1,
+    IRQ2_has_slave = 2,
+    IRQ3_has_slave = 3,
+    IRQ4_has_slave = 4,
+    IRQ5_has_slave = 5,
+    IRQ6_has_slave = 6,
+    IRQ7_has_slave = 7,
+
+    fn transmit(self: ICW3) void {
+        Master.data.out(@as(u8, 1) << @intFromEnum(self));
+        Slave.data.out(@as(u8, @intFromEnum(self)));
+    }
+};
+
+const ICW4 = packed struct(u8) {
+    operating_mode: Operating_Mode = .mcs,
+    auto_end_of_interrupt: bool = false,
+    buffer_mode: Buffer_Mode = .neither,
+    special_fully_nested_mode: bool = false,
+    _: u3 = 0,
+
+    const Buffer_Mode = enum(u2) {
+        master = 0b11,
+        slave = 0b10,
+        neither = 0b00,
+    };
+
+    const Operating_Mode = enum(u1) {
+        mcs = 0,
+        x86 = 1,
+    };
+
+    fn transmit(self: ICW4) void {
+        Master.data.out(@as(u8, @bitCast(self)));
+        Slave.data.out(@as(u8, @bitCast(self)));
+    }
+};
+
+const OCW2 = packed struct(u8) {
+    interrupt_level: u3 = 0,
+    _: u2 = 0,
+    end_of_interrupt_mode: End_Of_Interrupt_Mode = .non_specific_command,
+
+    const End_Of_Interrupt_Mode = enum(u3) {
+        non_specific_command = 0b001,
+        specific_command = 0b011,
+        rotate_on_non_specific_command = 0b101,
+        rotate_on_specific_command = 0b111,
+        rotate_in_automatic_mode_set = 0b100,
+        rotate_in_automatic_mode_clear = 0b000,
+        set_priority_command = 0b110,
+        no_operation = 0b010,
+    };
+};
+
+const OCW3 = packed struct(u8) {};
+
+const Master = struct {
+    const Port = enum(u16) {
+        command = 0x20,
+        data = 0x21,
+
+        fn in(Type: type) Type {
+            return io.in(@intFromEnum(.data), Type);
+        }
+
+        fn out(port: Port, data: anytype) void {
+            io.out(@intFromEnum(port), data);
+            io.wait();
+        }
+    };
+
+    const IRQs = packed struct(u8) {
+        pit: bool = false,
+        keyboard: bool = false,
+        slave: bool = false,
+        COM2_and_COM4: bool = false,
+        COM1_and_COM3: bool = false,
+        LPT2: bool = false,
+        floppy: bool = false,
+        LPT1: bool = false,
+    };
+
+    fn enable(irqs: IRQs) void {
+        Port.data.out(@as(u8, @bitCast(irqs)));
+    }
+
+    fn end_of_interrupt() void {
+        Port.command.out(@as(u8, @bitCast(OCW2{})));
+    }
+
+    fn disable() void {
+        Port.data.out(@as(u8, @bitCast(IRQs)));
+    }
+};
+
+const Slave = enum(u16) {
+    const Port = enum(u16) {
+        command = 0xA0,
+        data = 0xA1,
+
+        fn in(Type: type) Type {
+            return io.in(@intFromEnum(.data), Type);
+        }
+
+        fn out(port: Port, data: anytype) void {
+            io.out(@intFromEnum(port), data);
+            io.wait();
+        }
+    };
+
+    const IRQs = packed struct(u8) {
+        rtc: bool = false,
+        _: u3 = 0,
+        mouse: bool = false,
+        math_coprocessor: bool = false,
+        disk1: bool = false,
+        disk2: bool = false,
+    };
+
+    fn enable(irqs: IRQs) void {
+        Port.data.out(@as(u8, @bitCast(irqs)));
+    }
+
+    fn end_of_interrupt() void {
+        Port.command.out(@as(u8, @bitCast(OCW2{})));
+    }
+
+    fn disable() void {
+        Port.data.out(@as(u8, @bitCast(IRQs)));
+    }
+};
+
+pub fn end_of_interrupt() void {
+    Master.end_of_interrupt();
+    Slave.end_of_interrupt();
+}
+
+pub fn disable() void {
+    Master.disable();
+    Slave.disable();
+}
+
+pub fn init() void {
+    disable();
+
+    ICW1.transmit(.{
+        .ICW4_needed = true,
+    });
+
+    // add offsets here
+    ICW2.transmit(.{});
+
+    ICW3.transmit(.IRQ2_has_slave);
+
+    ICW4.transmit(.{
+        .operating_mode = .x86,
+    });
+}
diff --git a/kernel/src/x86/pmm.zig b/kernel/src/x86/pmm.zig
new file mode 100644 (file)
index 0000000..9900d10
--- /dev/null
@@ -0,0 +1,49 @@
+const std = @import("std");
+const log = std.log.scoped(.mem);
+const limine = @import("limine");
+const pgn = @import("./pgn.zig");
+
+var stack: [*]usize = undefined;
+var stack_index: usize = 0;
+
+pub export var directmap_request: limine.HhdmRequest = .{};
+export var memmap_request: limine.MemoryMapRequest = .{};
+
+pub fn init() void {
+    const directmap = directmap_request.response orelse {
+        std.debug.panic("no directmap found", .{});
+        return;
+    };
+    stack = @ptrFromInt(directmap.offset);
+
+    const memory_map = memmap_request.response orelse {
+        std.debug.panic("no memmap found", .{});
+        return;
+    };
+    for (0..memory_map.entry_count) |i| {
+        const entry = memory_map.entries_ptr[i];
+        switch (entry.kind) {
+            .usable, .bootloader_reclaimable, .acpi_reclaimable => {
+                var j: usize = 0;
+                while (j < entry.length) : (j += pgn.PAGE_SIZE) {
+                    free(entry.base + j);
+                }
+            },
+            else => {},
+        }
+    }
+    log.info("{} blocks free", .{stack_index});
+}
+
+pub fn alloc() std.mem.Allocator.Error!usize {
+    if (stack_index == 0)
+        return error.OutOfMemory;
+    stack_index -= 1;
+    const address = stack[stack_index];
+    return address;
+}
+
+pub fn free(address: usize) void {
+    stack[stack_index] = std.mem.alignBackward(usize, address, pgn.PAGE_SIZE);
+    stack_index += 1;
+}
diff --git a/kernel/src/x86/srl.zig b/kernel/src/x86/srl.zig
new file mode 100644 (file)
index 0000000..f71112d
--- /dev/null
@@ -0,0 +1,21 @@
+const io = @import("./io.zig");
+
+pub const Port = enum(u16) {
+    const BASE = 0x3F8;
+    COM1 = BASE,
+    COM2 = BASE + 1,
+    COM3 = BASE + 2,
+    COM4 = BASE + 3,
+    COM5 = BASE + 4,
+    COM6 = BASE + 5,
+    COM7 = BASE + 6,
+    COM8 = BASE + 7,
+
+    pub fn in(self: Port, comptime Type: type) Type {
+        return io.in(@intFromEnum(self), Type);
+    }
+
+    pub fn out(self: Port, data: anytype) void {
+        io.out(@intFromEnum(self), data);
+    }
+};
diff --git a/limine b/limine
new file mode 160000 (submodule)
index 0000000..749a320
--- /dev/null
+++ b/limine
@@ -0,0 +1 @@
+Subproject commit 749a32066cd4aaf03ca6e352a918dc4992b9fa5c
diff --git a/limine.cfg b/limine.cfg
new file mode 100644 (file)
index 0000000..820a7a4
--- /dev/null
@@ -0,0 +1,7 @@
+TIMEOUT=0
+
+:mos
+    PROTOCOL=limine
+    KASLR=no
+    KERNEL_PATH=boot:///boot/kernel
+
diff --git a/qemu.sh b/qemu.sh
new file mode 100755 (executable)
index 0000000..932dbf5
--- /dev/null
+++ b/qemu.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+clear
+
+FLAGS="\
+-m 64M \
+-display none \
+-serial stdio"
+
+if [[ $1 == "debug" ]]; then
+    FLAGS="-s -S -no-reboot -d int $FLAGS"
+fi
+
+qemu-system-x86_64 $FLAGS -cdrom ./zig-out/mos.iso