--- /dev/null
+file ./zig-out/root/boot/kernel
+target remote localhost:1234
--- /dev/null
+/zig-cache/
+/zig-out/
--- /dev/null
+[submodule "limine"]
+ path = limine
+ url = https://github.com/limine-bootloader/limine.git
+ branch = binary
--- /dev/null
+# mos
+A monkey's operating system.
--- /dev/null
+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());
+}
--- /dev/null
+#!/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
+
--- /dev/null
+/zig-cache/
+/zig-out/
--- /dev/null
+[submodule "deps/limine"]
+ path = deps/limine
+ url = git@github.com:limine-bootloader/limine-zig.git
--- /dev/null
+repos:
+ - repo: https://github.com/batmac/pre-commit-zig
+ rev: v0.3.0
+ hooks:
+ - id: zig-fmt
--- /dev/null
+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);
+}
--- /dev/null
+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.
--- /dev/null
+# 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.
--- /dev/null
+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);
+}
--- /dev/null
+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,
+};
--- /dev/null
+pub const packages = struct {};
+pub const root_deps: []const struct { []const u8, []const u8 } = &.{};
--- /dev/null
+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.*)
+ }
+}
--- /dev/null
+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});
+ }
+}
--- /dev/null
+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;
+}
--- /dev/null
+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];
+}
--- /dev/null
+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;
+};
--- /dev/null
+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 {};
+}
--- /dev/null
+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) {}
+}
--- /dev/null
+const std = @import("std");
+
+const allocator = std.mem.Allocator{
+ .ptr = undefined,
+ .vtable = .{
+ .alloc = undefined,
+ .resize = undefined,
+ .free = undefined,
+ },
+};
--- /dev/null
+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,
+ });
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+const io = @import("./io.zig");
+
+const DEBUG_PORT = 0xE9;
+
+pub fn out(data: anytype) void {
+ io.out(DEBUG_PORT, data);
+}
--- /dev/null
+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();
+}
--- /dev/null
+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();
+}
--- /dev/null
+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});
+}
--- /dev/null
+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));
+}
--- /dev/null
+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);
+}
+
--- /dev/null
+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,
+ });
+}
--- /dev/null
+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;
+}
--- /dev/null
+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);
+ }
+};
--- /dev/null
+Subproject commit 749a32066cd4aaf03ca6e352a918dc4992b9fa5c
--- /dev/null
+TIMEOUT=0
+
+:mos
+ PROTOCOL=limine
+ KASLR=no
+ KERNEL_PATH=boot:///boot/kernel
+
--- /dev/null
+#!/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