├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build.zig ├── build.zig.zon └── src ├── Tamzen6x12.psf ├── acpi.zig ├── arch.zig ├── arch ├── aarch64.zig ├── aarch64 │ ├── acpi.zig │ ├── boot.s │ ├── machines │ │ └── rpi │ │ │ ├── gpio.zig │ │ │ └── mmio.zig │ ├── main.zig │ ├── serial.zig │ ├── serial │ │ ├── ns16550.zig │ │ └── pl011.zig │ └── vmm.zig ├── riscv64.zig ├── riscv64 │ ├── main.zig │ ├── serial.zig │ └── serial │ │ ├── ns16550.zig │ │ └── pl011.zig ├── x86_64.zig └── x86_64 │ ├── acpi.zig │ ├── apic.zig │ ├── apic │ ├── ioapic.zig │ └── lapic.zig │ ├── cpu.zig │ ├── cr.zig │ ├── fpu.zig │ ├── gdt.zig │ ├── interrupt.zig │ ├── io.zig │ ├── main.zig │ ├── sched.zig │ ├── serial.zig │ ├── smp.zig │ └── vmm.zig ├── boot └── limine.cfg ├── framebuffer.zig ├── linker.ld ├── main.zig ├── math.zig ├── mm ├── pmm.zig └── slab.zig ├── mmio.zig ├── panic.zig ├── psf.zig ├── terminal.zig ├── uacpi ├── uacpi_libc.h └── uacpi_libc.zig ├── utils.zig └── writer.zig /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.zig text eol=lf 3 | zigmod.* text eol=lf 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .zig-cache 2 | zig-out 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2023 leap123 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ydin 2 | A very extendable and versatile hybrid kernel. Part of the Kora operating system. 3 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | 4 | const kora_version = std.SemanticVersion{ 5 | .major = 0, 6 | .minor = 1, 7 | .patch = 0, 8 | }; 9 | 10 | pub fn build(b: *std.Build) !void { 11 | var target = b.standardTargetOptions(.{ 12 | .default_target = .{ 13 | .cpu_arch = .x86_64, 14 | .os_tag = .freestanding, 15 | .abi = .none, 16 | }, 17 | .whitelist = &.{ 18 | .{ .cpu_arch = .x86_64, .os_tag = .freestanding, .abi = .none }, 19 | .{ .cpu_arch = .aarch64, .os_tag = .freestanding, .abi = .none }, 20 | .{ .cpu_arch = .riscv64, .os_tag = .freestanding, .abi = .none }, 21 | }, 22 | }); 23 | switch (target.query.cpu_arch.?) { 24 | .x86_64 => { 25 | const features = std.Target.x86.Feature; 26 | target.query.cpu_features_sub.addFeature(@intFromEnum(features.mmx)); 27 | target.query.cpu_features_sub.addFeature(@intFromEnum(features.sse)); 28 | target.query.cpu_features_sub.addFeature(@intFromEnum(features.sse2)); 29 | target.query.cpu_features_sub.addFeature(@intFromEnum(features.avx)); 30 | target.query.cpu_features_sub.addFeature(@intFromEnum(features.avx2)); 31 | target.query.cpu_features_add.addFeature(@intFromEnum(features.soft_float)); 32 | }, 33 | .aarch64, .riscv64 => {}, 34 | else => return error.UnsupportedArchitecture, 35 | } 36 | const optimize = b.standardOptimizeOption(.{}); 37 | const limine = b.dependency("limine", .{}); 38 | const limine_bootloader = b.dependency("limine_bootloader", .{}); 39 | const uacpi = b.dependency("uacpi", .{}); 40 | 41 | _ = try buildYdin(b, target, optimize, limine, uacpi); 42 | const iso = try buildLimineIso(b, limine_bootloader); 43 | _ = try runIsoQemu(b, iso, target.query.cpu_arch.?); 44 | } 45 | 46 | fn buildYdin(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, limine: *std.Build.Dependency, uacpi: *std.Build.Dependency) !*std.Build.Step.Compile { 47 | const exe_options = b.addOptions(); 48 | 49 | // From https://github.com/zigtools/zls 50 | const version = v: { 51 | const version_string = b.fmt("{d}.{d}.{d}", .{ kora_version.major, kora_version.minor, kora_version.patch }); 52 | const build_root_path = b.build_root.path orelse "."; 53 | 54 | var code: u8 = undefined; 55 | const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{ 56 | "git", "-C", build_root_path, "describe", "--match", "*.*.*", "--tags", 57 | }, &code, .Ignore) catch break :v version_string; 58 | 59 | const git_describe = std.mem.trim(u8, git_describe_untrimmed, " \n\r"); 60 | 61 | switch (std.mem.count(u8, git_describe, "-")) { 62 | 0 => { 63 | // Tagged release version (e.g. 0.10.0). 64 | std.debug.assert(std.mem.eql(u8, git_describe, version_string)); // tagged release must match version string 65 | break :v version_string; 66 | }, 67 | 2 => { 68 | // Untagged development build (e.g. 0.10.0-dev.216+34ce200). 69 | var it = std.mem.splitSequence(u8, git_describe, "-"); 70 | const tagged_ancestor = it.first(); 71 | const commit_height = it.next().?; 72 | const commit_id = it.next().?; 73 | 74 | const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor); 75 | std.debug.assert(kora_version.order(ancestor_ver) == .gt); // version must be greater than its previous version 76 | std.debug.assert(std.mem.startsWith(u8, commit_id, "g")); // commit hash is prefixed with a 'g' 77 | 78 | break :v b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] }); 79 | }, 80 | else => { 81 | std.debug.print("Unexpected 'git describe' output: '{s}'\n", .{git_describe}); 82 | std.process.exit(1); 83 | }, 84 | } 85 | }; 86 | 87 | exe_options.addOption([:0]const u8, "version", b.allocator.dupeZ(u8, version) catch "0.1.0-dev"); 88 | 89 | const ydin = b.addExecutable(.{ 90 | .name = "vmydin", 91 | .root_source_file = b.path("src/main.zig"), 92 | .target = target, 93 | .optimize = optimize, 94 | .code_model = switch (target.query.cpu_arch.?) { 95 | .x86_64 => .kernel, 96 | .aarch64 => .small, 97 | .riscv64 => .medium, 98 | else => return error.UnsupportedArchitecture, 99 | }, 100 | }); 101 | ydin.root_module.addOptions("build_options", exe_options); 102 | ydin.setLinkerScriptPath(b.path("src/linker.ld")); 103 | ydin.root_module.addImport("limine", limine.module("limine")); 104 | ydin.addIncludePath(b.path("src/uacpi")); 105 | ydin.addIncludePath(uacpi.path("include")); 106 | ydin.root_module.red_zone = false; 107 | const root_dir = b.pathFromRoot("."); 108 | ydin.addCSourceFiles(.{ 109 | .files = &[_][]const u8{ 110 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/tables.c").getPath(b)), 111 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/types.c").getPath(b)), 112 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/uacpi.c").getPath(b)), 113 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/utilities.c").getPath(b)), 114 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/interpreter.c").getPath(b)), 115 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/opcodes.c").getPath(b)), 116 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/namespace.c").getPath(b)), 117 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/stdlib.c").getPath(b)), 118 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/shareable.c").getPath(b)), 119 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/opregion.c").getPath(b)), 120 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/default_handlers.c").getPath(b)), 121 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/io.c").getPath(b)), 122 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/notify.c").getPath(b)), 123 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/sleep.c").getPath(b)), 124 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/registers.c").getPath(b)), 125 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/resources.c").getPath(b)), 126 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/event.c").getPath(b)), 127 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/mutex.c").getPath(b)), 128 | try std.fs.path.relative(b.allocator, root_dir, uacpi.path("source/osi.c").getPath(b)), 129 | }, 130 | .flags = &[_][]const u8{ 131 | "-ffreestanding", 132 | "-nostdlib", 133 | "-mno-red-zone", 134 | "-DUACPI_OVERRIDE_LIBC", 135 | "-DUACPI_SIZED_FREES", 136 | "-DUACPI_FORMATTED_LOGGING", 137 | }, 138 | }); 139 | 140 | b.installArtifact(ydin); 141 | return ydin; 142 | } 143 | 144 | fn buildLimineIso(b: *std.Build, limine: *std.Build.Dependency) !*std.Build.Step.Run { 145 | const limine_path = limine.path("."); 146 | //const limine_exe = b.addExecutable(.{ 147 | // .name = "limine-deploy", 148 | // .target = b.host, 149 | // .optimize = .ReleaseSafe, 150 | //}); 151 | //limine_exe.addCSourceFile(.{ .file = limine.path("limine.c"), .flags = &[_][]const u8{"-std=c99"} }); 152 | //limine_exe.linkLibC(); 153 | //const limine_exe_run = b.addRunArtifact(limine_exe); 154 | //limine_exe_run.addArg("bios-install"); 155 | 156 | const cmd = &[_][]const u8{ 157 | // zig fmt: off 158 | "/bin/sh", "-c", 159 | try std.mem.concat(b.allocator, u8, &[_][]const u8{ 160 | "rm -rf zig-out/iso/root && ", 161 | "mkdir -p zig-out/iso/root/EFI/BOOT && ", 162 | "cp zig-out/bin/vmydin zig-out/iso/root && ", 163 | "cp src/boot/limine.cfg zig-out/iso/root && ", 164 | "cp ", limine_path.getPath(b), "/limine-bios.sys ", 165 | limine_path.getPath(b), "/limine-bios-cd.bin ", 166 | limine_path.getPath(b), "/limine-uefi-cd.bin ", 167 | "zig-out/iso/root && ", 168 | "cp ", limine_path.getPath(b), "/BOOTX64.EFI ", 169 | limine_path.getPath(b), "/BOOTAA64.EFI ", 170 | limine_path.getPath(b), "/BOOTRISCV64.EFI ", 171 | "zig-out/iso/root/EFI/BOOT && ", 172 | "xorriso -as mkisofs -quiet -b limine-bios-cd.bin ", 173 | "-no-emul-boot -boot-load-size 4 -boot-info-table ", 174 | "--efi-boot limine-uefi-cd.bin ", 175 | "-efi-boot-part --efi-boot-image --protective-msdos-label ", 176 | "zig-out/iso/root -o zig-out/iso/kora.iso", 177 | }), 178 | // zig fmt: on 179 | }; 180 | 181 | const iso_cmd = b.addSystemCommand(cmd); 182 | iso_cmd.step.dependOn(b.getInstallStep()); 183 | 184 | //_ = limine_exe_run.addOutputFileArg("kora.iso"); 185 | //limine_exe_run.step.dependOn(&iso_cmd.step); 186 | 187 | const iso_step = b.step("iso", "Generate a bootable Limine ISO file"); 188 | iso_step.dependOn(&iso_cmd.step); 189 | 190 | return iso_cmd; 191 | } 192 | 193 | fn downloadEdk2(b: *std.Build, arch: std.Target.Cpu.Arch) !void { 194 | const link = switch (arch) { 195 | .x86_64 => "https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd", 196 | .aarch64 => "https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd", 197 | .riscv64 => "https://retrage.github.io/edk2-nightly/bin/RELEASERISCV64_VIRT.fd", 198 | else => return error.UnsupportedArchitecture, 199 | }; 200 | 201 | const cmd = &[_][]const u8{ "curl", link, "-Lo", try edk2FileName(b, arch) }; 202 | var child_proc = std.process.Child.init(cmd, b.allocator); 203 | try child_proc.spawn(); 204 | const ret_val = try child_proc.wait(); 205 | try std.testing.expectEqual(ret_val, std.process.Child.Term{ .Exited = 0 }); 206 | } 207 | 208 | fn edk2FileName(b: *std.Build, arch: std.Target.Cpu.Arch) ![]const u8 { 209 | return std.mem.concat(b.allocator, u8, &[_][]const u8{ ".zig-cache/edk2-", @tagName(arch), ".fd" }); 210 | } 211 | 212 | fn runIsoQemu(b: *std.Build, iso: *std.Build.Step.Run, arch: std.Target.Cpu.Arch) !*std.Build.Step.Run { 213 | _ = std.fs.cwd().statFile(try edk2FileName(b, arch)) catch try downloadEdk2(b, arch); 214 | 215 | const qemu_executable = switch (arch) { 216 | .x86_64 => "qemu-system-x86_64", 217 | .aarch64 => "qemu-system-aarch64", 218 | .riscv64 => "qemu-system-riscv64", 219 | else => return error.UnsupportedArchitecture, 220 | }; 221 | 222 | const qemu_iso_args = switch (arch) { 223 | .x86_64 => &[_][]const u8{ 224 | // zig fmt: off 225 | qemu_executable, 226 | //"-s", "-S", 227 | "-cpu", "host", 228 | "-smp", "4", 229 | "-M", "q35,accel=kvm:whpx:hvf:tcg", 230 | "-m", "2G", 231 | "-cdrom", "zig-out/iso/kora.iso", 232 | "-bios", try edk2FileName(b, arch), 233 | "-boot", "d", 234 | "-serial", "stdio", 235 | "-display", "none", 236 | "-no-reboot", 237 | // zig fmt: on 238 | }, 239 | .aarch64, .riscv64 => &[_][]const u8{ 240 | // zig fmt: off 241 | qemu_executable, 242 | //"-s", "-S", 243 | "-cpu", "max", 244 | "-smp", "4", 245 | "-M", "virt,accel=kvm:whpx:hvf:tcg", 246 | "-m", "2G", 247 | "-cdrom", "zig-out/iso/kora.iso", 248 | "-bios", try edk2FileName(b, arch), 249 | "-boot", "d", 250 | "-serial", "stdio", 251 | "-display", "none", 252 | "-no-reboot", 253 | "-no-shutdown", 254 | // zig fmt: on 255 | }, 256 | else => return error.UnsupportedArchitecture, 257 | }; 258 | 259 | const qemu_iso_cmd = b.addSystemCommand(qemu_iso_args); 260 | qemu_iso_cmd.step.dependOn(&iso.step); 261 | 262 | const qemu_iso_step = b.step("run", "Boot ISO in QEMU"); 263 | qemu_iso_step.dependOn(&qemu_iso_cmd.step); 264 | 265 | return qemu_iso_cmd; 266 | } 267 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = "ydin", 3 | .version = "0.1.0", 4 | .dependencies = .{ 5 | .limine = .{ 6 | .url = "git+https://codeberg.org/kora/limine#5883690b9553d4f23e1bb1ab9ee11d7fccac92af", 7 | .hash = "1220694dd409ebd652d1dc9913ad4e4855239850b07f1c3c4d33f505ab1117b3fae1", 8 | }, 9 | .limine_bootloader = .{ 10 | .url = "git+https://github.com/limine-bootloader/limine#v7.8.0-binary", 11 | .hash = "1220bfc95f2cb6e32fcfe8dae5a3faab66490796c5e0fd1d767321f5ceb13f6dbcdc", 12 | }, 13 | .uacpi = .{ 14 | .url = "git+https://github.com/UltraOS/uACPI#63051383b51297764a3730a76842a5c0b9a94331", 15 | .hash = "12200c871275c178efb86ab43dfaf2caf87a91426b656522bcd32bea9ec308bda513", 16 | }, 17 | }, 18 | .paths = .{""}, 19 | } 20 | -------------------------------------------------------------------------------- /src/Tamzen6x12.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kora-org/ydin/d3af71dc4a81a380dd9e5b860df2324187328031/src/Tamzen6x12.psf -------------------------------------------------------------------------------- /src/acpi.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | pub const uacpi = @cImport({ 4 | @cInclude("uacpi/kernel_api.h"); 5 | @cInclude("uacpi/acpi.h"); 6 | @cInclude("uacpi/uacpi.h"); 7 | }); 8 | const printf = @cImport(@cInclude("printf.h")); 9 | const mmio = @import("mmio.zig"); 10 | const pmm = @import("mm/pmm.zig"); 11 | const slab = @import("mm/slab.zig"); 12 | const arch = @import("arch.zig"); 13 | const log = std.log.scoped(.uacpi); 14 | 15 | comptime { 16 | _ = @import("uacpi/uacpi_libc.zig"); 17 | } 18 | 19 | pub export fn uacpi_kernel_raw_memory_read(address: uacpi.uacpi_phys_addr, byte_width: u8, ret: *u64) callconv(.C) uacpi.uacpi_status { 20 | ret.* = @intCast(switch (byte_width) { 21 | 1 => mmio.read(u8, address + pmm.hhdm_response.offset), 22 | 2 => mmio.read(u16, address + pmm.hhdm_response.offset), 23 | 4 => mmio.read(u32, address + pmm.hhdm_response.offset), 24 | 8 => mmio.read(u64, address + pmm.hhdm_response.offset), 25 | else => @panic("Invalid byte width"), 26 | }); 27 | return uacpi.UACPI_STATUS_OK; 28 | } 29 | 30 | pub export fn uacpi_kernel_raw_memory_write(address: uacpi.uacpi_phys_addr, byte_width: u8, value: u64) callconv(.C) uacpi.uacpi_status { 31 | switch (byte_width) { 32 | 1 => mmio.write(u8, address + pmm.hhdm_response.offset, @intCast(value)), 33 | 2 => mmio.write(u16, address + pmm.hhdm_response.offset, @intCast(value)), 34 | 4 => mmio.write(u32, address + pmm.hhdm_response.offset, @intCast(value)), 35 | 8 => mmio.write(u64, address + pmm.hhdm_response.offset, @intCast(value)), 36 | else => @panic("Invalid byte width"), 37 | } 38 | return uacpi.UACPI_STATUS_OK; 39 | } 40 | 41 | pub export fn uacpi_kernel_raw_io_read(port: uacpi.uacpi_io_addr, byte_width: u8, ret: *u64) callconv(.C) uacpi.uacpi_status { 42 | if (builtin.cpu.arch == .x86_64) { 43 | ret.* = @intCast(switch (byte_width) { 44 | 1 => arch.io.read(u8, @intCast(port)), 45 | 2 => arch.io.read(u16, @intCast(port)), 46 | 4 => arch.io.read(u32, @intCast(port)), 47 | else => @panic("Invalid byte width"), 48 | }); 49 | return uacpi.UACPI_STATUS_OK; 50 | } else { 51 | return uacpi.UACPI_STATUS_NO_HANDLER; 52 | } 53 | } 54 | 55 | pub export fn uacpi_kernel_raw_io_write(port: uacpi.uacpi_io_addr, byte_width: u8, value: u64) callconv(.C) uacpi.uacpi_status { 56 | if (builtin.cpu.arch == .x86_64) { 57 | switch (byte_width) { 58 | 1 => arch.io.write(u8, @intCast(port), @intCast(value)), 59 | 2 => arch.io.write(u16, @intCast(port), @intCast(value)), 60 | 4 => arch.io.write(u32, @intCast(port), @intCast(value)), 61 | else => @panic("Invalid byte width"), 62 | } 63 | return uacpi.UACPI_STATUS_OK; 64 | } else { 65 | return uacpi.UACPI_STATUS_NO_HANDLER; 66 | } 67 | } 68 | 69 | pub const IoMap = extern struct { 70 | port: u16, 71 | length: usize, 72 | }; 73 | 74 | pub export fn uacpi_kernel_io_map(port: uacpi.uacpi_io_addr, length: usize, ret: **IoMap) callconv(.C) uacpi.uacpi_status { 75 | if (builtin.cpu.arch == .x86_64) { 76 | ret.* = slab.allocator.create(IoMap) catch undefined; 77 | ret.*.port = @intCast(port); 78 | ret.*.length = length; 79 | return uacpi.UACPI_STATUS_OK; 80 | } else { 81 | return uacpi.UACPI_STATUS_NO_HANDLER; 82 | } 83 | } 84 | 85 | pub export fn uacpi_kernel_io_unmap(ret: *IoMap) callconv(.C) uacpi.uacpi_status { 86 | if (builtin.cpu.arch == .x86_64) { 87 | slab.allocator.destroy(ret); 88 | return uacpi.UACPI_STATUS_OK; 89 | } else { 90 | return uacpi.UACPI_STATUS_NO_HANDLER; 91 | } 92 | } 93 | 94 | pub export fn uacpi_kernel_io_read(handle: *IoMap, offset: usize, byte_width: u8, ret: *u64) callconv(.C) uacpi.uacpi_status { 95 | if (builtin.cpu.arch == .x86_64) { 96 | if (offset >= handle.length) return uacpi.UACPI_STATUS_INVALID_ARGUMENT; 97 | return uacpi_kernel_raw_io_read(handle.port + offset, byte_width, ret); 98 | } else { 99 | return uacpi.UACPI_STATUS_NO_HANDLER; 100 | } 101 | } 102 | 103 | pub export fn uacpi_kernel_io_write(handle: *IoMap, offset: usize, byte_width: u8, value: u64) callconv(.C) uacpi.uacpi_status { 104 | if (builtin.cpu.arch == .x86_64) { 105 | if (offset >= handle.length) return uacpi.UACPI_STATUS_INVALID_ARGUMENT; 106 | return uacpi_kernel_raw_io_write(handle.port + offset, byte_width, value); 107 | } else { 108 | return uacpi.UACPI_STATUS_NO_HANDLER; 109 | } 110 | } 111 | 112 | pub export fn uacpi_kernel_pci_read(address: *uacpi.uacpi_pci_address, offset: usize, byte_width: u8, ret: *u64) callconv(.C) uacpi.uacpi_status { 113 | _ = address; 114 | _ = offset; 115 | _ = byte_width; 116 | _ = ret; 117 | return uacpi.UACPI_STATUS_UNIMPLEMENTED; 118 | } 119 | 120 | pub export fn uacpi_kernel_pci_write(address: *uacpi.uacpi_pci_address, offset: usize, byte_width: u8, value: u64) callconv(.C) uacpi.uacpi_status { 121 | _ = address; 122 | _ = offset; 123 | _ = byte_width; 124 | _ = value; 125 | return uacpi.UACPI_STATUS_UNIMPLEMENTED; 126 | } 127 | 128 | pub export fn uacpi_kernel_map(address: uacpi.uacpi_phys_addr, length: usize) callconv(.C) u64 { 129 | _ = length; 130 | return address + pmm.hhdm_response.offset; 131 | } 132 | 133 | pub export fn uacpi_kernel_unmap(address: u64) callconv(.C) void { 134 | _ = address; 135 | } 136 | 137 | pub export fn uacpi_kernel_alloc(size: usize) callconv(.C) [*]u8 { 138 | const ret = slab.allocator.alloc(u8, size) catch undefined; 139 | return ret.ptr; 140 | } 141 | 142 | pub export fn uacpi_kernel_calloc(count: usize, size: usize) callconv(.C) [*]u8 { 143 | const ret = slab.allocator.alloc(u8, count * size) catch undefined; 144 | return ret.ptr; 145 | } 146 | 147 | pub export fn uacpi_kernel_free(address: [*]u8, size: usize) callconv(.C) void { 148 | slab.allocator.free(address[0..size]); 149 | } 150 | 151 | pub export fn uacpi_kernel_log(level: uacpi.uacpi_log_level, string: [*c]const u8) callconv(.C) void { 152 | switch (level) { 153 | uacpi.UACPI_LOG_TRACE => log.debug("{s}", .{string}), 154 | uacpi.UACPI_LOG_INFO => log.info("{s}", .{string}), 155 | uacpi.UACPI_LOG_WARN => log.warn("{s}", .{string}), 156 | uacpi.UACPI_LOG_ERROR => log.err("{s}", .{string}), 157 | else => @panic("Unknown log level"), 158 | } 159 | } 160 | pub export fn uacpi_kernel_get_ticks() callconv(.C) u64 { 161 | return 0; 162 | } 163 | 164 | pub export fn uacpi_kernel_stall(usec: u8) callconv(.C) void { 165 | _ = usec; 166 | } 167 | 168 | pub export fn uacpi_kernel_sleep(msec: u64) callconv(.C) void { 169 | _ = msec; 170 | } 171 | 172 | pub export fn uacpi_kernel_create_mutex() callconv(.C) *arch.Spinlock { 173 | //var ret = arch.Spinlock{}; 174 | //return &ret; 175 | return undefined; 176 | } 177 | 178 | pub export fn uacpi_kernel_free_mutex(_: *arch.Spinlock) callconv(.C) void {} 179 | 180 | pub export fn uacpi_kernel_get_thread_id() callconv(.C) uacpi.uacpi_thread_id { 181 | return null; 182 | } 183 | 184 | pub export fn uacpi_kernel_acquire_mutex(_: *arch.Spinlock, _: u16) callconv(.C) bool { 185 | return true; 186 | } 187 | 188 | pub export fn uacpi_kernel_release_mutex(_: *arch.Spinlock) callconv(.C) void {} 189 | 190 | pub export fn uacpi_kernel_create_event() callconv(.C) uacpi.uacpi_handle { 191 | return undefined; 192 | } 193 | pub export fn uacpi_kernel_free_event(_: uacpi.uacpi_handle) callconv(.C) void {} 194 | pub export fn uacpi_kernel_wait_for_event(_: uacpi.uacpi_handle, _: u16) callconv(.C) bool { 195 | return true; 196 | } 197 | pub export fn uacpi_kernel_signal_event(_: uacpi.uacpi_handle) callconv(.C) void {} 198 | pub export fn uacpi_kernel_reset_event(_: uacpi.uacpi_handle) callconv(.C) void {} 199 | 200 | pub export fn uacpi_kernel_handle_firmware_request(_: [*c]uacpi.uacpi_firmware_request) callconv(.C) uacpi.uacpi_status { 201 | return uacpi.UACPI_STATUS_UNIMPLEMENTED; 202 | } 203 | pub export fn uacpi_kernel_install_interrupt_handler(irq: u32, _: uacpi.uacpi_interrupt_handler, ctx: uacpi.uacpi_handle, out_irq_handle: [*c]uacpi.uacpi_handle) callconv(.C) uacpi.uacpi_status { 204 | _ = irq; 205 | _ = ctx; 206 | _ = out_irq_handle; 207 | return uacpi.UACPI_STATUS_UNIMPLEMENTED; 208 | } 209 | pub export fn uacpi_kernel_uninstall_interrupt_handler(_: uacpi.uacpi_interrupt_handler, irq_handle: uacpi.uacpi_handle) callconv(.C) uacpi.uacpi_status { 210 | _ = irq_handle; 211 | return uacpi.UACPI_STATUS_UNIMPLEMENTED; 212 | } 213 | pub export fn uacpi_kernel_create_spinlock() callconv(.C) *arch.Spinlock { 214 | //var ret = arch.Spinlock{}; 215 | //return &ret; 216 | return undefined; 217 | } 218 | 219 | pub export fn uacpi_kernel_free_spinlock(_: *arch.Spinlock) callconv(.C) void {} 220 | 221 | pub export fn uacpi_kernel_spinlock_lock(_: *arch.Spinlock) callconv(.C) uacpi.uacpi_cpu_flags { 222 | return undefined; 223 | } 224 | 225 | pub export fn uacpi_kernel_spinlock_unlock(_: *arch.Spinlock, _: uacpi.uacpi_cpu_flags) callconv(.C) void {} 226 | 227 | pub export fn uacpi_kernel_schedule_work(_: uacpi.uacpi_work_type, _: uacpi.uacpi_work_handler, _: uacpi.uacpi_handle) uacpi.uacpi_status { 228 | return uacpi.UACPI_STATUS_UNIMPLEMENTED; 229 | } 230 | 231 | pub export fn uacpi_kernel_wait_for_work_completion() uacpi.uacpi_status { 232 | return uacpi.UACPI_STATUS_UNIMPLEMENTED; 233 | } 234 | -------------------------------------------------------------------------------- /src/arch.zig: -------------------------------------------------------------------------------- 1 | const builtin = @import("builtin"); 2 | 3 | pub usingnamespace switch (builtin.cpu.arch) { 4 | .x86_64 => @import("arch/x86_64.zig"), 5 | .aarch64 => @import("arch/aarch64.zig"), 6 | else => unreachable, 7 | }; 8 | -------------------------------------------------------------------------------- /src/arch/aarch64.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const dtb = @import("dtb"); 3 | 4 | pub const acpi = @import("aarch64/acpi.zig"); 5 | 6 | pub fn halt() void { 7 | while (true) 8 | asm volatile ("wfi"); 9 | } 10 | 11 | pub var device_tree: u64 = 0x40000000; 12 | 13 | pub const Spinlock = struct { 14 | lock_bits: std.atomic.Atomic(u32) = .{ .value = 0 }, 15 | refcount: std.atomic.Atomic(usize) = .{ .value = 0 }, 16 | 17 | pub fn lock(self: *Spinlock) void { 18 | _ = self.refcount.fetchAdd(1, .Monotonic); 19 | 20 | while (true) { 21 | if (self.lock_bits.swap(1, .Acquire) == 0) 22 | break; 23 | 24 | while (self.lock_bits.fetchAdd(0, .Monotonic) != 0) { 25 | std.atomic.spinLoopHint(); 26 | } 27 | } 28 | 29 | _ = self.refcount.fetchSub(1, .Monotonic); 30 | std.atomic.compilerFence(.Acquire); 31 | } 32 | 33 | pub fn unlock(self: *Spinlock) void { 34 | self.lock_bits.store(0, .Release); 35 | std.atomic.compilerFence(.Release); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/arch/aarch64/acpi.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const mmio = @import("../../mmio.zig"); 4 | const pmm = @import("../../mm/pmm.zig"); 5 | const log = std.log.scoped(.acpi); 6 | 7 | pub const GenericAddress = extern struct { 8 | base_type: u8, 9 | bit_width: u8, 10 | bit_offset: u8, 11 | access_size: u8, 12 | base: u64, 13 | 14 | inline fn read(self: GenericAddress, comptime T: type) T { 15 | return if (self.base_type == 0) 16 | mmio.read(T, self.base) 17 | else 18 | @panic("PMIO is unsupported!"); 19 | } 20 | }; 21 | 22 | pub const Header = extern struct { 23 | signature: [4]u8, 24 | length: u32, 25 | revision: u8, 26 | checksum: u8, 27 | oem: [6]u8, 28 | oem_table: [8]u8, 29 | oem_revision: u32, 30 | creator_id: u32, 31 | creator_revision: u32, 32 | 33 | inline fn getContents(self: *Header) []const u8 { 34 | return @as([*]const u8, @ptrCast(self))[0..self.length][@sizeOf(Header)..]; 35 | } 36 | }; 37 | 38 | pub const Xsdp = extern struct { 39 | signature: [8]u8, 40 | checksum: u8, 41 | oem: [6]u8, 42 | revision: u8, 43 | rsdt: u32, 44 | length: u32, 45 | xsdt: u64, 46 | ext_checksum: u8, 47 | }; 48 | 49 | pub const Fadt = extern struct { 50 | firmware_control: u32, 51 | dsdt: u32, 52 | reserved: u8, 53 | profile: u8, 54 | sci_irq: u16, 55 | smi_command_port: u32, 56 | acpi_enable: u8, 57 | acpi_disable: u8, 58 | s4bios_req: u8, 59 | pstate_control: u8, 60 | pm1a_event_blk: u32, 61 | pm1b_event_blk: u32, 62 | pm1a_control_blk: u32, 63 | pm1b_control_blk: u32, 64 | pm2_control_blk: u32, 65 | pm_timer_blk: u32, 66 | gpe0_blk: u32, 67 | gpe1_blk: u32, 68 | pm1_event_length: u8, 69 | pm1_control_length: u8, 70 | pm2_control_length: u8, 71 | pm_timer_length: u8, 72 | gpe0_length: u8, 73 | gpe1_length: u8, 74 | gpe1_base: u8, 75 | cstate_control: u8, 76 | worst_c2_latency: u16, 77 | worst_c3_latency: u16, 78 | flush_size: u16, 79 | flush_stride: u16, 80 | duty_offset: u8, 81 | duty_width: u8, 82 | day_alarm: u8, 83 | month_alarm: u8, 84 | century: u8, 85 | iapc_boot_flags: u16, 86 | reserved2: u8, 87 | flags: u32, 88 | reset_register: GenericAddress, 89 | reset_command: u8, 90 | arm_boot_flags: u16, 91 | minor_version: u8, 92 | x_firmware_control: u64, 93 | x_dsdt: u64, 94 | x_pm1a_event_blk: GenericAddress, 95 | x_pm1b_event_blk: GenericAddress, 96 | x_pm1a_control_blk: GenericAddress, 97 | x_pm1b_control_blk: GenericAddress, 98 | x_pm2_control_blk: GenericAddress, 99 | x_pm_timer_blk: GenericAddress, 100 | x_gpe0_blk: GenericAddress, 101 | x_gpe1_blk: GenericAddress, 102 | }; 103 | 104 | pub export var rsdp_request: limine.Rsdp.Request = .{}; 105 | pub var rsdp_response: limine.Rsdp.Response = undefined; 106 | 107 | var timer_block: GenericAddress = undefined; 108 | var timer_bits: usize = 0; 109 | var xsdt: ?*Header = null; 110 | var rsdt: ?*Header = null; 111 | 112 | inline fn getEntries(comptime T: type, header: *Header) []align(1) const T { 113 | return std.mem.bytesAsSlice(T, header.getContents()); 114 | } 115 | 116 | inline fn printTable(sdt: *Header) void { 117 | if (std.mem.eql(u8, "SSDT", &sdt.signature)) return; 118 | log.debug( 119 | " signature={s}, base=0x{x:0>16}, length={}, revision={}", 120 | .{ sdt.signature, @intFromPtr(sdt), sdt.length, sdt.revision }, 121 | ); 122 | } 123 | 124 | pub fn getTable(signature: []const u8) ?*Header { 125 | if (xsdt) |x| { 126 | for (getEntries(u64, x)) |ent| { 127 | var entry = @as(*Header, @ptrFromInt(ent + pmm.hhdm_response.offset)); 128 | if (std.mem.eql(u8, signature[0..4], &entry.signature)) 129 | return entry; 130 | } 131 | } else { 132 | for (getEntries(u32, rsdt.?)) |ent| { 133 | var entry = @as(*Header, @ptrFromInt(ent + pmm.hhdm_response.offset)); 134 | if (std.mem.eql(u8, signature[0..4], &entry.signature)) 135 | return entry; 136 | } 137 | } 138 | 139 | return null; 140 | } 141 | 142 | pub fn pmSleep(us: u64) void { 143 | const shift: u64 = @as(u64, 1) << @as(u6, @truncate(timer_bits)); 144 | const target = (us * 3) + ((us * 5) / 10) + ((us * 8) / 100); 145 | 146 | var n: u64 = target / shift; 147 | var remaining: u64 = target % shift; 148 | 149 | var cur_ticks = timer_block.read(u32); 150 | remaining += cur_ticks; 151 | 152 | if (remaining < cur_ticks) n += 1 else { 153 | n += remaining / shift; 154 | remaining = remaining % shift; 155 | } 156 | 157 | var new_ticks: u32 = 0; 158 | while (n > 0) { 159 | new_ticks = timer_block.read(u32); 160 | if (new_ticks < cur_ticks) n -= 1; 161 | cur_ticks = new_ticks; 162 | } 163 | 164 | while (remaining > cur_ticks) { 165 | new_ticks = timer_block.read(u32); 166 | if (new_ticks < cur_ticks) break; 167 | cur_ticks = new_ticks; 168 | } 169 | } 170 | 171 | pub fn init() void { 172 | if (rsdp_request.response) |rsdp| 173 | rsdp_response = rsdp.*; 174 | 175 | const xsdp = @as(*align(1) const Xsdp, @ptrFromInt(rsdp_response.address)); 176 | 177 | if (xsdp.revision >= 2 and xsdp.xsdt != 0) 178 | xsdt = @as(*Header, @ptrFromInt(xsdp.xsdt + pmm.hhdm_response.offset)) 179 | else 180 | rsdt = @as(*Header, @ptrFromInt(xsdp.rsdt + pmm.hhdm_response.offset)); 181 | 182 | log.debug("ACPI tables:", .{}); 183 | if (xsdt) |x| { 184 | for (getEntries(u64, x)) |ent| { 185 | const entry = @as(*Header, @ptrFromInt(ent + pmm.hhdm_response.offset)); 186 | printTable(entry); 187 | } 188 | } else { 189 | for (getEntries(u32, rsdt.?)) |ent| { 190 | const entry = @as(*Header, @ptrFromInt(ent + pmm.hhdm_response.offset)); 191 | printTable(entry); 192 | } 193 | } 194 | 195 | if (getTable("FACP")) |fadt_sdt| { 196 | const fadt = @as(*align(1) const Fadt, @ptrCast(fadt_sdt.getContents())); 197 | 198 | if (xsdp.revision >= 2 and fadt.x_pm_timer_blk.base_type == 0) { 199 | timer_block = fadt.x_pm_timer_blk; 200 | timer_block.base = timer_block.base + pmm.hhdm_response.offset; 201 | } else { 202 | if (fadt.pm_timer_blk == 0 or fadt.pm_timer_length != 4) 203 | @panic("ACPI timer is unsupported/malformed"); 204 | 205 | timer_block = GenericAddress{ 206 | .base = fadt.pm_timer_blk, 207 | .base_type = 1, 208 | .bit_width = 32, 209 | .bit_offset = 0, 210 | .access_size = 0, 211 | }; 212 | } 213 | 214 | timer_bits = if ((fadt.flags & (1 << 8)) == 0) 32 else 24; 215 | 216 | if (timer_block.base_type == 0) 217 | log.debug("Detected MMIO ACPI timer with a {}-bit counter width", .{timer_bits}) 218 | else 219 | log.debug("Detected PMIO ACPI timer with a {}-bit counter width", .{timer_bits}); 220 | } else { 221 | //return error.InvalidHardware; 222 | @panic("ACPI timer is unsupported/malformed"); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/arch/aarch64/boot.s: -------------------------------------------------------------------------------- 1 | .global _start 2 | _start: 3 | mrs x1, mpidr_el1 4 | and x1, x1, #3 5 | cbz x1, 2f 6 | 7 | 1: 8 | wfe 9 | b 1b 10 | 11 | 2: 12 | ldr x5, =_start 13 | mov sp, x5 14 | 15 | ldr x5, =bss_start 16 | ldr w6, =bss_size 17 | 18 | 3: 19 | cbz w6, 4f 20 | str xzr, [x5], #8 21 | sub w6, w6, #1 22 | cbnz w6, 3b 23 | 24 | 4: 25 | bl kmain 26 | b 1b 27 | -------------------------------------------------------------------------------- /src/arch/aarch64/machines/rpi/gpio.zig: -------------------------------------------------------------------------------- 1 | const mmio = @import("mmio.zig"); 2 | 3 | pub const Registers = enum(u64) { 4 | GPFSEL0 = mmio.getMmioBase() + 0x200000, 5 | GPFSEL1 = mmio.getMmioBase() + 0x200004, 6 | GPFSEL2 = mmio.getMmioBase() + 0x200008, 7 | GPFSEL3 = mmio.getMmioBase() + 0x20000c, 8 | GPFSEL4 = mmio.getMmioBase() + 0x200010, 9 | GPFSEL5 = mmio.getMmioBase() + 0x200014, 10 | GPSET0 = mmio.getMmioBase() + 0x20001c, 11 | GPSET1 = mmio.getMmioBase() + 0x200020, 12 | GPCLR0 = mmio.getMmioBase() + 0x200028, 13 | GPCLR1 = mmio.getMmioBase() + 0x20002c, 14 | GPLEV0 = mmio.getMmioBase() + 0x200034, 15 | GPLEV1 = mmio.getMmioBase() + 0x200038, 16 | GPEDS0 = mmio.getMmioBase() + 0x200040, 17 | GPEDS1 = mmio.getMmioBase() + 0x200044, 18 | GPREN0 = mmio.getMmioBase() + 0x20004c, 19 | GPREN1 = mmio.getMmioBase() + 0x200050, 20 | GPFEN0 = mmio.getMmioBase() + 0x200058, 21 | GPFEN1 = mmio.getMmioBase() + 0x20005c, 22 | GPHEN0 = mmio.getMmioBase() + 0x200064, 23 | GPHEN1 = mmio.getMmioBase() + 0x200068, 24 | GPLEN0 = mmio.getMmioBase() + 0x200070, 25 | GPLEN1 = mmio.getMmioBase() + 0x200074, 26 | GPAREN0 = mmio.getMmioBase() + 0x20007c, 27 | GPAREN1 = mmio.getMmioBase() + 0x200080, 28 | GPAFEN0 = mmio.getMmioBase() + 0x200088, 29 | GPAFEN1 = mmio.getMmioBase() + 0x20008c, 30 | GPPUPPDN0 = mmio.getMmioBase() + 0x2000e4, 31 | GPPUPPDN1 = mmio.getMmioBase() + 0x2000e8, 32 | GPPUPPDN2 = mmio.getMmioBase() + 0x2000ec, 33 | GPPUPPDN3 = mmio.getMmioBase() + 0x2000f0, 34 | }; 35 | 36 | pub const AlternateFunctions = enum(u32) { 37 | Alt0 = 4, 38 | Alt1 = 5, 39 | Alt2 = 6, 40 | Alt3 = 7, 41 | Alt4 = 3, 42 | Alt5 = 2, 43 | }; 44 | 45 | pub const Pull = enum(u32) { 46 | None, 47 | Down, // Are down and up the right way around? 48 | Up, 49 | }; 50 | 51 | pub fn call(pin_number: usize, value: u32, base: Registers, field_size: usize) void { 52 | const field_mask: usize = (1 << field_size) - 1; 53 | const num_fields: usize = 32 / field_size; 54 | const register: u64 = @intFromEnum(base) + ((pin_number / num_fields) * 4); 55 | const shift: usize = (pin_number % num_fields) * field_size; 56 | 57 | var current_value = mmio.read(register); 58 | current_value &= ~(field_mask << shift); 59 | current_value |= value << shift; 60 | mmio.write(register, current_value); 61 | } 62 | 63 | pub fn set(pin_number: usize, value: u32) void { 64 | call(pin_number, value, .GPSET0, 1); 65 | } 66 | 67 | pub fn clear(pin_number: usize, value: u32) void { 68 | call(pin_number, value, .GPCLR0, 1); 69 | } 70 | 71 | pub fn pull(pin_number: usize, value: Pull) void { 72 | call(pin_number, @intFromEnum(value), .GPPUPPDN0, 2); 73 | } 74 | 75 | pub fn function(pin_number: usize, value: u32) void { 76 | call(pin_number, value, .GPFSEL0, 3); 77 | } 78 | 79 | pub fn useAlternateFunction(pin_number: usize, alt_function: AlternateFunctions) void { 80 | pull(pin_number, .None); 81 | function(pin_number, @intFromEnum(alt_function)); 82 | } 83 | -------------------------------------------------------------------------------- /src/arch/aarch64/machines/rpi/mmio.zig: -------------------------------------------------------------------------------- 1 | const arch = @import("../../../aarch64.zig"); 2 | 3 | pub fn getMmioBase() !u64 { 4 | return switch (try arch.getBoardType()) { 5 | .RaspberryPi3 => 0x3f000000, 6 | .RaspberryPi4 => 0xfe000000, 7 | else => error.NotAPi, 8 | }; 9 | } 10 | 11 | pub fn write(reg: u64, data: u32) void { 12 | @fence(.SeqCst); 13 | @as(*volatile u32, @ptrFromInt(reg)).* = data; 14 | } 15 | 16 | pub fn read(reg: u64) u32 { 17 | @fence(.SeqCst); 18 | return @as(*volatile u32, @ptrFromInt(reg)).*; 19 | } 20 | -------------------------------------------------------------------------------- /src/arch/aarch64/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const arch = @import("../aarch64.zig"); 4 | const writer = @import("../../writer.zig"); 5 | const lara = @import("../../main.zig"); 6 | 7 | pub export var dtb_request: limine.DeviceTree.Request = .{}; 8 | pub fn start() callconv(.C) void { 9 | if (dtb_request.response) |dtb| 10 | arch.device_tree = dtb.address; 11 | 12 | writer.init(); 13 | lara.main() catch unreachable; 14 | arch.halt(); 15 | } 16 | -------------------------------------------------------------------------------- /src/arch/aarch64/serial.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const dtb = @import("dtb"); 3 | const arch = @import("../aarch64.zig"); 4 | const utils = @import("../../utils.zig"); 5 | const Pl011 = @import("serial/pl011.zig"); 6 | const Ns16550 = @import("serial/ns16550.zig"); 7 | 8 | const Uart = struct { 9 | address: u64, 10 | kind: Kind, 11 | 12 | pub const Kind = enum { 13 | /// "arm,pl011" (QEMU ARM Virt, Raspberry Pi) 14 | Pl011, 15 | /// "snps,dw-apb-uart" (ROCKPro64) 16 | /// "ns16550a" (QEMU RISC-V Virt) 17 | Ns16550, 18 | }; 19 | 20 | pub fn init() !Uart { 21 | var traverser: dtb.Traverser = undefined; 22 | const size = try dtb.totalSize(@as(*anyopaque, @ptrFromInt(arch.device_tree))); 23 | try traverser.init(@as([*]u8, @ptrFromInt(arch.device_tree))[0..size]); 24 | 25 | var in_node = false; 26 | var state: struct { 27 | compatible: ?[]const u8 = null, 28 | reg: ?u64 = null, 29 | } = undefined; 30 | 31 | var address_cells: ?u32 = null; 32 | var size_cells: ?u32 = null; 33 | 34 | var event = try traverser.event(); 35 | while (event != .End) : (event = try traverser.event()) { 36 | if (!in_node) { 37 | switch (event) { 38 | .BeginNode => |name| { 39 | if (std.mem.startsWith(u8, name, "pl011@") or 40 | std.mem.startsWith(u8, name, "serial@") or 41 | std.mem.startsWith(u8, name, "uart@")) 42 | { 43 | in_node = true; 44 | state = .{}; 45 | } 46 | }, 47 | .Prop => |prop| { 48 | if (std.mem.eql(u8, prop.name, "#address-cells") and address_cells == null) { 49 | address_cells = utils.readU32(prop.value); 50 | } else if (std.mem.eql(u8, prop.name, "#size-cells") and size_cells == null) { 51 | size_cells = utils.readU32(prop.value); 52 | } 53 | }, 54 | else => {}, 55 | } 56 | } else switch (event) { 57 | .Prop => |prop| { 58 | if (std.mem.eql(u8, prop.name, "reg") and address_cells != null and size_cells != null) { 59 | state.reg = try utils.firstReg(address_cells.?, prop.value); 60 | } else if (std.mem.eql(u8, prop.name, "status")) { 61 | if (!std.mem.eql(u8, prop.value, "okay\x00")) { 62 | in_node = false; 63 | } 64 | } else if (std.mem.eql(u8, prop.name, "compatible")) { 65 | state.compatible = prop.value; 66 | } 67 | }, 68 | .BeginNode => in_node = false, 69 | .EndNode => { 70 | in_node = false; 71 | const reg = state.reg orelse continue; 72 | const compatible = state.compatible orelse continue; 73 | const kind: Kind = if (std.mem.indexOf(u8, compatible, "arm,pl011\x00") != null) 74 | .Pl011 75 | else if (std.mem.indexOf(u8, compatible, "snps,dw-apb-uart\x00") != null or 76 | std.mem.indexOf(u8, compatible, "ns16550a\x00") != null) 77 | .Ns16550 78 | else 79 | continue; 80 | 81 | return Uart{ 82 | .address = reg, 83 | .kind = kind, 84 | }; 85 | }, 86 | else => {}, 87 | } 88 | } 89 | 90 | return error.NotFound; 91 | } 92 | }; 93 | 94 | pub const SerialImpl = union(enum) { 95 | pl011: Pl011, 96 | ns16550: Ns16550, 97 | 98 | pub fn init(self: SerialImpl) void { 99 | switch (self) { 100 | inline else => |impl| return impl.init(), 101 | } 102 | } 103 | 104 | pub fn write(self: SerialImpl, bytes: []const u8) !usize { 105 | switch (self) { 106 | inline else => |impl| return impl.write(bytes), 107 | } 108 | } 109 | }; 110 | 111 | pub fn Serial() !SerialImpl { 112 | const uart = try Uart.init(); 113 | return switch (uart.kind) { 114 | .Pl011 => .{ .pl011 = .{ .address = uart.address } }, 115 | .Ns16550 => .{ .ns16550 = .{ .address = uart.address } }, 116 | }; 117 | } 118 | -------------------------------------------------------------------------------- /src/arch/aarch64/serial/ns16550.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mmio = @import("../../../mmio.zig"); 3 | 4 | // Same as x86's serial implementation except using MMIO instead of 5 | // x86's PMIO. 6 | 7 | address: u64, 8 | 9 | const INTERRUPT_OFFSET: u64 = 1; 10 | const FIFO_CONTROL_OFFSET: u64 = 2; 11 | const LINE_CONTROL_OFFSET: u64 = 3; 12 | const MODEM_CONTROL_OFFSET: u64 = 4; 13 | const LINE_STATUS_OFFSET: u64 = 5; 14 | 15 | const Self = @This(); 16 | pub const Error = error{}; 17 | 18 | /// Initialize the serial port. 19 | pub fn init(self: Self) void { 20 | // Disable all interupts. 21 | mmio.write(u8, self.address + INTERRUPT_OFFSET, 0x00); 22 | 23 | // Enable DLAB. 24 | mmio.write(u8, self.address + LINE_CONTROL_OFFSET, 0x80); 25 | 26 | // Set maximum speed to 115200 baud by configuring DLL 27 | // and DLM. 28 | mmio.write(u8, self.address, 0x01); 29 | mmio.write(u8, self.address + INTERRUPT_OFFSET, 0); 30 | 31 | // Disable DLAB and set data word length to 8 bits. 32 | mmio.write(u8, self.address + LINE_CONTROL_OFFSET, 0x03); 33 | 34 | // Enable FIFO, clear TX/RX queues, and set a 14-byte 35 | // interrupt threshold. 36 | mmio.write(u8, self.address + FIFO_CONTROL_OFFSET, 0xc7); 37 | 38 | // Mark data terminal ready, signal request to send and 39 | // enable auxilliary output #2. (used as interrupt line 40 | // for the CPU) 41 | mmio.write(u8, self.address + MODEM_CONTROL_OFFSET, 0x0b); 42 | 43 | // Enable interrupts. 44 | mmio.write(u8, self.address + INTERRUPT_OFFSET, 0x01); 45 | } 46 | 47 | /// Sends a byte on the serial port. 48 | pub fn writeByte(self: Self, byte: u8) void { 49 | switch (byte) { 50 | 0x7f => { 51 | mmio.write(u8, self.address, 0x7f); 52 | mmio.write(u8, self.address, ' '); 53 | mmio.write(u8, self.address, 0x7f); 54 | }, 55 | 0x0a => { 56 | mmio.write(u8, self.address, 0x0d); 57 | mmio.write(u8, self.address, 0x0a); 58 | }, 59 | else => mmio.write(u8, self.address, byte), 60 | } 61 | } 62 | 63 | /// Sends bytes to the serial port. 64 | pub fn write(self: Self, bytes: []const u8) Error!usize { 65 | for (bytes) |byte| 66 | writeByte(self, byte); 67 | 68 | return bytes.len; 69 | } 70 | 71 | /// Receives a byte on the serial port. 72 | pub fn read(self: Self) u8 { 73 | return mmio.read(u8, self.address); 74 | } 75 | -------------------------------------------------------------------------------- /src/arch/aarch64/serial/pl011.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mmio = @import("../../../mmio.zig"); 3 | 4 | address: u64, 5 | 6 | const FLAG_OFFSET = 0x18; 7 | const INTEGER_BAUD_DIVISOR_OFFSET: u64 = 0x24; 8 | const FRACTIONAL_BAUD_DIVISOR_OFFSET: u64 = 0x28; 9 | const LINE_CONTROL_OFFSET: u64 = 0x2c; 10 | const CONTROL_OFFSET: u64 = 0x30; 11 | const INTERRUPT_OFFSET: u64 = 0x44; 12 | 13 | const Self = @This(); 14 | pub const Error = error{}; 15 | 16 | /// Initialize the serial port. 17 | pub fn init(self: Self) void { 18 | // Turn off the UART temporarily 19 | mmio.write(u32, self.address + CONTROL_OFFSET, 0); 20 | 21 | // Clear all interupts. 22 | mmio.write(u32, self.address + INTERRUPT_OFFSET, 0x7ff); 23 | 24 | // Set maximum speed to 115200 baud. 25 | mmio.write(u32, self.address + INTEGER_BAUD_DIVISOR_OFFSET, 0x02); 26 | mmio.write(u32, self.address + FRACTIONAL_BAUD_DIVISOR_OFFSET, 0x0b); 27 | 28 | // Enable 8N1 and FIFO. 29 | mmio.write(u32, self.address + LINE_CONTROL_OFFSET, 0x07 << 0x04); 30 | 31 | // Enable interrupts. 32 | mmio.write(u32, self.address + INTERRUPT_OFFSET, 0x301); 33 | } 34 | 35 | /// Sends a byte on the serial port. 36 | pub fn writeByte(self: Self, byte: u8) void { 37 | switch (byte) { 38 | 0x7f => { 39 | mmio.write(u32, self.address, 0x7f); 40 | mmio.write(u32, self.address, ' '); 41 | mmio.write(u32, self.address, 0x7f); 42 | }, 43 | 0x0a => { 44 | mmio.write(u32, self.address, 0x0d); 45 | mmio.write(u32, self.address, 0x0a); 46 | }, 47 | else => mmio.write(u32, self.address, byte), 48 | } 49 | } 50 | 51 | /// Sends bytes to the serial port. 52 | pub fn write(self: Self, bytes: []const u8) Error!usize { 53 | for (bytes) |byte| 54 | writeByte(self, byte); 55 | 56 | return bytes.len; 57 | } 58 | 59 | /// Receives a byte on the serial port. 60 | pub fn read(self: Self) u8 { 61 | return @truncate(mmio.read(u32, self.address)); 62 | } 63 | -------------------------------------------------------------------------------- /src/arch/aarch64/vmm.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const pmm = @import("../../mm/pmm.zig"); 4 | const slab = @import("../../mm/slab.zig"); 5 | const log = std.log.scoped(.vmm); 6 | 7 | pub export var kernel_address_request: limine.KernelAddress.Request = .{}; 8 | 9 | pub const CacheMode = enum(u4) { 10 | Uncached, 11 | WriteCombining, 12 | WriteProtect, 13 | WriteBack, 14 | }; 15 | 16 | pub const PageFlags = packed struct { 17 | read: bool = false, 18 | write: bool = false, 19 | exec: bool = false, 20 | user: bool = false, 21 | cache_type: CacheMode = .WriteBack, 22 | }; 23 | 24 | pub var pagemap = Pagemap{}; 25 | 26 | pub fn init() void { 27 | pagemap.root.ttbr1 = @intFromPtr(pmm.alloc(1)); 28 | 29 | var page_flags = PageFlags{ 30 | .read = true, 31 | .write = true, 32 | .exec = true, 33 | }; 34 | 35 | if (kernel_address_request.response) |kernel_address| { 36 | const pbase: usize = kernel_address.physical_base; 37 | const vbase: usize = kernel_address.virtual_base; 38 | 39 | var i: usize = 0; 40 | while (i < (0x400 * 0x1000)) : (i += 0x1000) 41 | pagemap.mapPage(page_flags, vbase + i, pbase + i, false); 42 | 43 | i = 0; 44 | page_flags.exec = false; 45 | while (i < (0x800 * 0x200000)) : (i += 0x200000) 46 | pagemap.mapPage(page_flags, i + pmm.hhdm_response.offset, i, true); 47 | } 48 | 49 | for (pmm.memmap_response.getEntries()) |ent| { 50 | if (ent.base + ent.length < @as(usize, @intCast((0x800 * 0x200000)))) { 51 | continue; 52 | } 53 | 54 | const base: usize = std.mem.alignBackward(ent.base, 0x200000); 55 | var i: usize = 0; 56 | 57 | while (i < std.mem.alignForward(ent.length, 0x200000)) : (i += 0x200000) 58 | pagemap.mapPage(page_flags, (base + i) + pmm.hhdm_response.offset, base + i, true); 59 | } 60 | 61 | pagemap.load(); 62 | } 63 | 64 | pub const Pagemap = struct { 65 | const Self = @This(); 66 | 67 | root: struct { 68 | ttbr0: u64, 69 | ttbr1: u64, 70 | } = undefined, 71 | 72 | pub fn load(self: *Self) void { 73 | asm volatile ("msr ttbr0_el1, %[val]" 74 | : 75 | : [val] "r" (self.root.ttbr0), 76 | ); 77 | asm volatile ("msr ttbr1_el1, %[val]" 78 | : 79 | : [val] "r" (self.root.ttbr1), 80 | ); 81 | } 82 | 83 | pub fn save(self: *Self) void { 84 | self.root = .{ 85 | .ttbr0 = asm volatile ("mrs %[ret], ttbr0_el1" 86 | : [ret] "=r" (-> u64), 87 | ), 88 | .ttbr1 = asm volatile ("mrs %[ret], ttbr1_el1" 89 | : [ret] "=r" (-> u64), 90 | ), 91 | }; 92 | } 93 | 94 | pub fn mapPage(self: *Self, flags: PageFlags, virt: u64, phys: u64, huge: bool) void { 95 | const ttbr = if ((virt & (1 << 63)) == 1) self.root.ttbr1 else self.root.ttbr0; 96 | var root: ?[*]u64 = @as([*]u64, @ptrFromInt(ttbr + pmm.hhdm_response.offset)); 97 | 98 | const indices: [4]u64 = [_]u64{ 99 | genIndex(virt, 39), genIndex(virt, 30), 100 | genIndex(virt, 21), genIndex(virt, 12), 101 | }; 102 | 103 | root = getNextLevel(root.?, indices[0], true); 104 | if (root == null) return; 105 | 106 | root = getNextLevel(root.?, indices[1], true); 107 | if (root == null) return; 108 | 109 | if (huge) 110 | root.?[indices[2]] = createPte(flags, phys, true) 111 | else { 112 | root = getNextLevel(root.?, indices[2], true); 113 | root.?[indices[3]] = createPte(flags, phys, false); 114 | } 115 | } 116 | 117 | pub fn unmapPage(self: *Self, virt: u64) void { 118 | const ttbr = if ((virt & (1 << 63)) == 1) self.root.ttbr1 else self.root.ttbr0; 119 | var root: ?[*]u64 = @as([*]u64, @ptrFromInt(ttbr + pmm.hhdm_response.offset)); 120 | 121 | const indices: [4]u64 = [_]u64{ 122 | genIndex(virt, 39), genIndex(virt, 30), 123 | genIndex(virt, 21), genIndex(virt, 12), 124 | }; 125 | 126 | root = getNextLevel(root.?, indices[0], false); 127 | if (root == null) return; 128 | 129 | root = getNextLevel(root.?, indices[1], false); 130 | if (root == null) return; 131 | 132 | if ((root.?[indices[2]] & (1 << 7)) != 0) 133 | root.?[indices[2]] &= ~@as(u64, 1) 134 | else if (getNextLevel(root.?, indices[2], false)) |final_root| 135 | final_root[indices[3]] &= ~@as(u64, 1); 136 | 137 | invalidatePage(virt); 138 | } 139 | }; 140 | 141 | inline fn genIndex(virt: u64, comptime shift: usize) u64 { 142 | return ((virt & (0x1ff << shift)) >> shift); 143 | } 144 | 145 | fn getNextLevel(level: [*]u64, index: usize, create: bool) ?[*]u64 { 146 | if ((level[index] & 3) == 0) { 147 | if (!create) return null; 148 | 149 | if (pmm.alloc(1)) |table_ptr| { 150 | level[index] = @intFromPtr(table_ptr); 151 | level[index] |= 0b111; 152 | } else return null; 153 | } 154 | 155 | return @as([*]u64, @ptrFromInt((level[index] & ~@as(u64, 0x1ff)) + pmm.hhdm_response.offset)); 156 | } 157 | 158 | fn createPte(flags: PageFlags, phys_ptr: u64, huge: bool) u64 { 159 | var result: u64 = 1 | (3 << 8) | (1 << 10); 160 | 161 | if (!flags.write) result |= (1 << 7); 162 | if (!flags.exec) result |= (1 << 54); 163 | if (flags.user) result |= (1 << 6); 164 | if (!huge) result |= (1 << 1); 165 | 166 | switch (flags.cache_type) { 167 | .Uncached => result |= (1 << 2), 168 | .WriteCombining => result |= (2 << 2), 169 | else => {}, 170 | } 171 | 172 | result |= phys_ptr; 173 | return result; 174 | } 175 | 176 | pub inline fn invalidatePage(addr: u64) void { 177 | asm volatile ( 178 | \\tlbi vale1, %[virt] 179 | : 180 | : [virt] "r" (addr << 12), 181 | : "memory" 182 | ); 183 | } 184 | -------------------------------------------------------------------------------- /src/arch/riscv64.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const dtb = @import("dtb"); 3 | 4 | //pub const mm = @import("riscv64/mm.zig"); 5 | 6 | pub fn halt() void { 7 | while (true) 8 | asm volatile ("wfi"); 9 | } 10 | 11 | pub var device_tree: u64 = 0x40000000; 12 | 13 | pub const Spinlock = struct { 14 | lock_bits: std.atomic.Atomic(u32) = .{ .value = 0 }, 15 | refcount: std.atomic.Atomic(usize) = .{ .value = 0 }, 16 | 17 | pub fn lock(self: *Spinlock) void { 18 | _ = self.refcount.fetchAdd(1, .Monotonic); 19 | 20 | while (true) { 21 | if (self.lock_bits.swap(1, .Acquire) == 0) 22 | break; 23 | 24 | while (self.lock_bits.fetchAdd(0, .Monotonic) != 0) { 25 | std.atomic.spinLoopHint(); 26 | } 27 | } 28 | 29 | _ = self.refcount.fetchSub(1, .Monotonic); 30 | std.atomic.compilerFence(.Acquire); 31 | } 32 | 33 | pub fn unlock(self: *Spinlock) void { 34 | self.lock_bits.store(0, .Release); 35 | std.atomic.compilerFence(.Release); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/arch/riscv64/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const arch = @import("../riscv64.zig"); 4 | const writer = @import("../../writer.zig"); 5 | const lara = @import("../../main.zig"); 6 | 7 | pub export var dtb_request: limine.DeviceTree.Request = .{}; 8 | pub export fn start() callconv(.C) void { 9 | if (dtb_request.response) |dtb| 10 | arch.device_tree = dtb.address; 11 | 12 | writer.init(); 13 | lara.main() catch unreachable; 14 | arch.halt(); 15 | } 16 | -------------------------------------------------------------------------------- /src/arch/riscv64/serial.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const dtb = @import("dtb"); 3 | const arch = @import("../riscv64.zig"); 4 | const utils = @import("../../utils.zig"); 5 | const Pl011 = @import("serial/pl011.zig"); 6 | const Ns16550 = @import("serial/ns16550.zig"); 7 | 8 | const Uart = struct { 9 | address: u64, 10 | kind: Kind, 11 | 12 | pub const Kind = enum { 13 | /// "arm,pl011" (QEMU ARM Virt, Raspberry Pi) 14 | Pl011, 15 | /// "snps,dw-apb-uart" (ROCKPro64) 16 | /// "ns16550a" (QEMU RISC-V Virt) 17 | Ns16550, 18 | }; 19 | 20 | pub fn init() !Uart { 21 | var traverser: dtb.Traverser = undefined; 22 | const size = try dtb.totalSize(@as(*anyopaque, @ptrFromInt(arch.device_tree))); 23 | try traverser.init(@as([*]u8, @ptrFromInt(arch.device_tree))[0..size]); 24 | 25 | var in_node = false; 26 | var state: struct { 27 | compatible: ?[]const u8 = null, 28 | reg: ?u64 = null, 29 | } = undefined; 30 | 31 | var address_cells: ?u32 = null; 32 | var size_cells: ?u32 = null; 33 | 34 | var event = try traverser.event(); 35 | while (event != .End) : (event = try traverser.event()) { 36 | if (!in_node) { 37 | switch (event) { 38 | .BeginNode => |name| { 39 | if (std.mem.startsWith(u8, name, "pl011@") or 40 | std.mem.startsWith(u8, name, "serial@") or 41 | std.mem.startsWith(u8, name, "uart@")) 42 | { 43 | in_node = true; 44 | state = .{}; 45 | } 46 | }, 47 | .Prop => |prop| { 48 | if (std.mem.eql(u8, prop.name, "#address-cells") and address_cells == null) { 49 | address_cells = utils.readU32(prop.value); 50 | } else if (std.mem.eql(u8, prop.name, "#size-cells") and size_cells == null) { 51 | size_cells = utils.readU32(prop.value); 52 | } 53 | }, 54 | else => {}, 55 | } 56 | } else switch (event) { 57 | .Prop => |prop| { 58 | if (std.mem.eql(u8, prop.name, "reg") and address_cells != null and size_cells != null) { 59 | state.reg = try utils.firstReg(address_cells.?, prop.value); 60 | } else if (std.mem.eql(u8, prop.name, "status")) { 61 | if (!std.mem.eql(u8, prop.value, "okay\x00")) { 62 | in_node = false; 63 | } 64 | } else if (std.mem.eql(u8, prop.name, "compatible")) { 65 | state.compatible = prop.value; 66 | } 67 | }, 68 | .BeginNode => in_node = false, 69 | .EndNode => { 70 | in_node = false; 71 | const reg = state.reg orelse continue; 72 | const compatible = state.compatible orelse continue; 73 | const kind: Kind = if (std.mem.indexOf(u8, compatible, "arm,pl011\x00") != null) 74 | .Pl011 75 | else if (std.mem.indexOf(u8, compatible, "snps,dw-apb-uart\x00") != null or 76 | std.mem.indexOf(u8, compatible, "ns16550a\x00") != null) 77 | .Ns16550 78 | else 79 | continue; 80 | 81 | return Uart{ 82 | .address = reg, 83 | .kind = kind, 84 | }; 85 | }, 86 | else => {}, 87 | } 88 | } 89 | 90 | return error.NotFound; 91 | } 92 | }; 93 | 94 | pub const SerialImpl = union(enum) { 95 | pl011: Pl011, 96 | ns16550: Ns16550, 97 | 98 | pub fn init(self: SerialImpl) void { 99 | switch (self) { 100 | inline else => |impl| return impl.init(), 101 | } 102 | } 103 | 104 | pub fn write(self: SerialImpl, bytes: []const u8) !usize { 105 | switch (self) { 106 | inline else => |impl| return impl.write(bytes), 107 | } 108 | } 109 | }; 110 | 111 | pub fn Serial() !SerialImpl { 112 | const uart = try Uart.init(); 113 | return switch (uart.kind) { 114 | .Pl011 => .{ .pl011 = .{ .address = uart.address } }, 115 | .Ns16550 => .{ .ns16550 = .{ .address = uart.address } }, 116 | }; 117 | } 118 | -------------------------------------------------------------------------------- /src/arch/riscv64/serial/ns16550.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mmio = @import("../../../mmio.zig"); 3 | 4 | // Same as x86's serial implementation except using MMIO instead of 5 | // x86's PMIO. 6 | 7 | address: u64, 8 | 9 | const INTERRUPT_OFFSET: u64 = 1; 10 | const FIFO_CONTROL_OFFSET: u64 = 2; 11 | const LINE_CONTROL_OFFSET: u64 = 3; 12 | const MODEM_CONTROL_OFFSET: u64 = 4; 13 | const LINE_STATUS_OFFSET: u64 = 5; 14 | 15 | const Self = @This(); 16 | pub const Error = error{}; 17 | 18 | /// Initialize the serial port. 19 | pub fn init(self: Self) void { 20 | // Disable all interupts. 21 | mmio.write(u8, self.address + INTERRUPT_OFFSET, 0x00); 22 | 23 | // Enable DLAB. 24 | mmio.write(u8, self.address + LINE_CONTROL_OFFSET, 0x80); 25 | 26 | // Set maximum speed to 115200 baud by configuring DLL 27 | // and DLM. 28 | mmio.write(u8, self.address, 0x01); 29 | mmio.write(u8, self.address + INTERRUPT_OFFSET, 0); 30 | 31 | // Disable DLAB and set data word length to 8 bits. 32 | mmio.write(u8, self.address + LINE_CONTROL_OFFSET, 0x03); 33 | 34 | // Enable FIFO, clear TX/RX queues, and set a 14-byte 35 | // interrupt threshold. 36 | mmio.write(u8, self.address + FIFO_CONTROL_OFFSET, 0xc7); 37 | 38 | // Mark data terminal ready, signal request to send and 39 | // enable auxilliary output #2. (used as interrupt line 40 | // for the CPU) 41 | mmio.write(u8, self.address + MODEM_CONTROL_OFFSET, 0x0b); 42 | 43 | // Enable interrupts. 44 | mmio.write(u8, self.address + INTERRUPT_OFFSET, 0x01); 45 | } 46 | 47 | /// Sends a byte on the serial port. 48 | pub fn writeByte(self: Self, byte: u8) void { 49 | switch (byte) { 50 | 0x7f => { 51 | mmio.write(u8, self.address, 0x7f); 52 | mmio.write(u8, self.address, ' '); 53 | mmio.write(u8, self.address, 0x7f); 54 | }, 55 | 0x0a => { 56 | mmio.write(u8, self.address, 0x0d); 57 | mmio.write(u8, self.address, 0x0a); 58 | }, 59 | else => mmio.write(u8, self.address, byte), 60 | } 61 | } 62 | 63 | /// Sends bytes to the serial port. 64 | pub fn write(self: Self, bytes: []const u8) Error!usize { 65 | for (bytes) |byte| 66 | writeByte(self, byte); 67 | 68 | return bytes.len; 69 | } 70 | 71 | /// Receives a byte on the serial port. 72 | pub fn read(self: Self) u8 { 73 | return mmio.read(u8, self.address); 74 | } 75 | -------------------------------------------------------------------------------- /src/arch/riscv64/serial/pl011.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mmio = @import("../../../mmio.zig"); 3 | 4 | address: u64, 5 | 6 | const FLAG_OFFSET = 0x18; 7 | const INTEGER_BAUD_DIVISOR_OFFSET: u64 = 0x24; 8 | const FRACTIONAL_BAUD_DIVISOR_OFFSET: u64 = 0x28; 9 | const LINE_CONTROL_OFFSET: u64 = 0x2c; 10 | const CONTROL_OFFSET: u64 = 0x30; 11 | const INTERRUPT_OFFSET: u64 = 0x44; 12 | 13 | const Self = @This(); 14 | pub const Error = error{}; 15 | 16 | /// Initialize the serial port. 17 | pub fn init(self: Self) void { 18 | // Turn off the UART temporarily 19 | mmio.write(u32, self.address + CONTROL_OFFSET, 0); 20 | 21 | // Clear all interupts. 22 | mmio.write(u32, self.address + INTERRUPT_OFFSET, 0x7ff); 23 | 24 | // Set maximum speed to 115200 baud. 25 | mmio.write(u32, self.address + INTEGER_BAUD_DIVISOR_OFFSET, 0x02); 26 | mmio.write(u32, self.address + FRACTIONAL_BAUD_DIVISOR_OFFSET, 0x0b); 27 | 28 | // Enable 8N1 and FIFO. 29 | mmio.write(u32, self.address + LINE_CONTROL_OFFSET, 0x07 << 0x04); 30 | 31 | // Enable interrupts. 32 | mmio.write(u32, self.address + INTERRUPT_OFFSET, 0x301); 33 | } 34 | 35 | /// Sends a byte on the serial port. 36 | pub fn writeByte(self: Self, byte: u8) void { 37 | switch (byte) { 38 | 0x7f => { 39 | mmio.write(u32, self.address, 0x7f); 40 | mmio.write(u32, self.address, ' '); 41 | mmio.write(u32, self.address, 0x7f); 42 | }, 43 | 0x0a => { 44 | mmio.write(u32, self.address, 0x0d); 45 | mmio.write(u32, self.address, 0x0a); 46 | }, 47 | else => mmio.write(u32, self.address, byte), 48 | } 49 | } 50 | 51 | /// Sends bytes to the serial port. 52 | pub fn write(self: Self, bytes: []const u8) Error!usize { 53 | for (bytes) |byte| 54 | writeByte(self, byte); 55 | 56 | return bytes.len; 57 | } 58 | 59 | /// Receives a byte on the serial port. 60 | pub fn read(self: Self) u8 { 61 | return @truncate(mmio.read(u32, self.address)); 62 | } 63 | -------------------------------------------------------------------------------- /src/arch/x86_64.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub const cpu = @import("x86_64/cpu.zig"); 4 | pub const main = @import("x86_64/main.zig"); 5 | pub const acpi = @import("x86_64/acpi.zig"); 6 | pub const smp = @import("x86_64/smp.zig"); 7 | pub const sched = @import("x86_64/sched.zig"); 8 | pub const vmm = @import("x86_64/vmm.zig"); 9 | 10 | pub fn interruptsEnabled() bool { 11 | const eflags = asm volatile ( 12 | \\pushf 13 | \\pop %[result] 14 | : [result] "=r" (-> u64), 15 | ); 16 | 17 | return ((eflags & 0x200) != 0); 18 | } 19 | 20 | pub fn enableInterrupts() void { 21 | asm volatile ("sti"); 22 | } 23 | 24 | pub fn disableInterrupts() void { 25 | asm volatile ("cli"); 26 | } 27 | 28 | pub fn pause() void { 29 | asm volatile ("pause" ::: "memory"); 30 | } 31 | 32 | pub fn halt() void { 33 | disableInterrupts(); 34 | while (true) { 35 | asm volatile ("hlt"); 36 | } 37 | } 38 | 39 | pub const Spinlock = extern struct { 40 | lock_bits: std.atomic.Value(u32) = .{ .raw = 0 }, 41 | refcount: std.atomic.Value(usize) = .{ .raw = 0 }, 42 | interrupts: bool = false, 43 | 44 | pub fn lock(self: *Spinlock) void { 45 | _ = self.refcount.fetchAdd(1, .monotonic); 46 | 47 | const current = interruptsEnabled(); 48 | disableInterrupts(); 49 | 50 | while (true) { 51 | if (self.lock_bits.swap(1, .acquire) == 0) 52 | break; 53 | 54 | while (self.lock_bits.fetchAdd(0, .monotonic) != 0) { 55 | if (interruptsEnabled()) 56 | enableInterrupts() 57 | else 58 | disableInterrupts(); 59 | 60 | std.atomic.spinLoopHint(); 61 | disableInterrupts(); 62 | } 63 | } 64 | 65 | _ = self.refcount.fetchSub(1, .monotonic); 66 | @fence(.acquire); 67 | self.interrupts = current; 68 | } 69 | 70 | pub fn unlock(self: *Spinlock) void { 71 | self.lock_bits.store(0, .release); 72 | @fence(.release); 73 | 74 | if (self.interrupts) 75 | enableInterrupts() 76 | else 77 | disableInterrupts(); 78 | } 79 | }; 80 | 81 | /// x86 specific stuff 82 | pub const io = @import("x86_64/io.zig"); 83 | pub const cr = @import("x86_64/cr.zig"); 84 | 85 | pub const Descriptor = packed struct { 86 | size: u16, 87 | ptr: u64, 88 | }; 89 | 90 | pub const CpuidResult = struct { 91 | eax: u32, 92 | ebx: u32, 93 | ecx: u32, 94 | edx: u32, 95 | }; 96 | 97 | pub fn cpuid(leaf: u32, sub_leaf: u32) CpuidResult { 98 | var eax: u32 = undefined; 99 | var ebx: u32 = undefined; 100 | var ecx: u32 = undefined; 101 | var edx: u32 = undefined; 102 | 103 | asm volatile ("cpuid" 104 | : [eax] "={eax}" (eax), 105 | [ebx] "={ebx}" (ebx), 106 | [ecx] "={ecx}" (ecx), 107 | [edx] "={edx}" (edx), 108 | : [leaf] "{eax}" (leaf), 109 | [sub_leaf] "{ecx}" (sub_leaf), 110 | : "memory" 111 | ); 112 | 113 | return .{ 114 | .eax = eax, 115 | .ebx = ebx, 116 | .ecx = ecx, 117 | .edx = edx, 118 | }; 119 | } 120 | 121 | pub fn rdtsc() u64 { 122 | var low: u32 = undefined; 123 | var high: u32 = undefined; 124 | 125 | asm volatile ("rdtsc" 126 | : [_] "={eax}" (low), 127 | [_] "={edx}" (high), 128 | ); 129 | 130 | return @as(u64, low) | (@as(u64, high) << 32); 131 | } 132 | 133 | pub fn wrmsr(reg: u64, val: u64) void { 134 | asm volatile ("wrmsr" 135 | : 136 | : [_] "{eax}" (val & 0xFFFFFFFF), 137 | [_] "{edx}" (val >> 32), 138 | [_] "{ecx}" (reg), 139 | ); 140 | } 141 | 142 | pub fn rdmsr(reg: u64) u64 { 143 | var low: u32 = undefined; 144 | var high: u32 = undefined; 145 | 146 | asm volatile ("rdmsr" 147 | : [_] "={eax}" (low), 148 | [_] "={edx}" (high), 149 | : [_] "{ecx}" (reg), 150 | ); 151 | 152 | return @as(u64, low) | (@as(u64, high) << 32); 153 | } 154 | -------------------------------------------------------------------------------- /src/arch/x86_64/acpi.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const io = @import("io.zig"); 4 | const mmio = @import("../../mmio.zig"); 5 | const pmm = @import("../../mm/pmm.zig"); 6 | const apic = @import("apic.zig"); 7 | const log = std.log.scoped(.acpi); 8 | 9 | pub const GenericAddress = extern struct { 10 | base_type: u8, 11 | bit_width: u8, 12 | bit_offset: u8, 13 | access_size: u8, 14 | base: u64, 15 | 16 | inline fn read(self: GenericAddress, comptime T: type) T { 17 | return if (self.base_type == 0) 18 | mmio.read(T, self.base) 19 | else 20 | io.read(T, @as(u16, @truncate(self.base))); 21 | } 22 | }; 23 | 24 | pub const Header = extern struct { 25 | signature: [4]u8, 26 | length: u32, 27 | revision: u8, 28 | checksum: u8, 29 | oem: [6]u8, 30 | oem_table: [8]u8, 31 | oem_revision: u32, 32 | creator_id: u32, 33 | creator_revision: u32, 34 | 35 | inline fn getContents(self: *Header) []const u8 { 36 | return @as([*]const u8, @ptrCast(self))[0..self.length][@sizeOf(Header)..]; 37 | } 38 | }; 39 | 40 | pub const Xsdp = extern struct { 41 | signature: [8]u8, 42 | checksum: u8, 43 | oem: [6]u8, 44 | revision: u8, 45 | rsdt: u32, 46 | length: u32, 47 | xsdt: u64, 48 | ext_checksum: u8, 49 | }; 50 | 51 | pub const Fadt = extern struct { 52 | firmware_control: u32, 53 | dsdt: u32, 54 | reserved: u8, 55 | profile: u8, 56 | sci_irq: u16, 57 | smi_command_port: u32, 58 | acpi_enable: u8, 59 | acpi_disable: u8, 60 | s4bios_req: u8, 61 | pstate_control: u8, 62 | pm1a_event_blk: u32, 63 | pm1b_event_blk: u32, 64 | pm1a_control_blk: u32, 65 | pm1b_control_blk: u32, 66 | pm2_control_blk: u32, 67 | pm_timer_blk: u32, 68 | gpe0_blk: u32, 69 | gpe1_blk: u32, 70 | pm1_event_length: u8, 71 | pm1_control_length: u8, 72 | pm2_control_length: u8, 73 | pm_timer_length: u8, 74 | gpe0_length: u8, 75 | gpe1_length: u8, 76 | gpe1_base: u8, 77 | cstate_control: u8, 78 | worst_c2_latency: u16, 79 | worst_c3_latency: u16, 80 | flush_size: u16, 81 | flush_stride: u16, 82 | duty_offset: u8, 83 | duty_width: u8, 84 | day_alarm: u8, 85 | month_alarm: u8, 86 | century: u8, 87 | iapc_boot_flags: u16, 88 | reserved2: u8, 89 | flags: u32, 90 | reset_register: GenericAddress, 91 | reset_command: u8, 92 | arm_boot_flags: u16, 93 | minor_version: u8, 94 | x_firmware_control: u64, 95 | x_dsdt: u64, 96 | x_pm1a_event_blk: GenericAddress, 97 | x_pm1b_event_blk: GenericAddress, 98 | x_pm1a_control_blk: GenericAddress, 99 | x_pm1b_control_blk: GenericAddress, 100 | x_pm2_control_blk: GenericAddress, 101 | x_pm_timer_blk: GenericAddress, 102 | x_gpe0_blk: GenericAddress, 103 | x_gpe1_blk: GenericAddress, 104 | }; 105 | 106 | pub const Madt = extern struct { 107 | header: Madt.Header, 108 | lapic_addr: u32, 109 | flags: u32, 110 | entries: [*]u8, 111 | 112 | pub fn get_iso(self: *align(1) Madt, irq: u8) ?*align(1) Iso { 113 | var entry: *Madt.Header = undefined; 114 | var i: usize = 0; 115 | while (i < self.header.length - @sizeOf(Madt)) { 116 | entry = @ptrFromInt(@intFromPtr(&self.entries) + i); 117 | if (entry.type == 2) { 118 | const iso: *align(1) Iso = @ptrCast(entry); 119 | if (iso.irq_src == irq) return iso; 120 | } 121 | i += @max(@sizeOf(Madt.Header), entry.length); 122 | } 123 | return null; 124 | } 125 | 126 | pub fn get_ioapic(self: *align(1) Madt) !*align(1) IoApic { 127 | var entry: *Madt.Header = undefined; 128 | var i: usize = 0; 129 | while (i < self.header.length - @sizeOf(Madt)) { 130 | entry = @ptrFromInt(@intFromPtr(&self.entries) + i); 131 | if (entry.type == 1) return @ptrCast(entry); 132 | i += @max(@sizeOf(Madt.Header), entry.length); 133 | } 134 | return error.NotFound; 135 | } 136 | 137 | pub const Header = packed struct { 138 | type: u8, 139 | length: u8, 140 | }; 141 | 142 | pub const Iso = packed struct { 143 | header: Madt.Header, 144 | bus_src: u8, 145 | irq_src: u8, 146 | gsi: u32, 147 | flags: u16, 148 | }; 149 | 150 | pub const IoApic = extern struct { 151 | header: Madt.Header, 152 | ioapic: apic.ioapic.IoApic, 153 | }; 154 | 155 | pub const LocalApic = packed struct { 156 | processor_id: u8, 157 | apic_id: u8, 158 | flags: u32, 159 | }; 160 | }; 161 | 162 | pub export var rsdp_request: limine.Rsdp.Request = .{}; 163 | pub var rsdp_response: limine.Rsdp.Response = undefined; 164 | 165 | var timer_block: GenericAddress = undefined; 166 | var timer_bits: usize = 0; 167 | var xsdt: ?*Header = null; 168 | var rsdt: ?*Header = null; 169 | pub var madt: ?*align(1) Madt = null; 170 | 171 | inline fn getEntries(comptime T: type, header: *Header) []align(1) const T { 172 | return std.mem.bytesAsSlice(T, header.getContents()); 173 | } 174 | 175 | inline fn printTable(sdt: *Header) void { 176 | if (std.mem.eql(u8, "SSDT", &sdt.signature)) return; 177 | log.debug( 178 | " signature={s}, base=0x{x:0>16}, length={}, revision={}", 179 | .{ sdt.signature, @intFromPtr(sdt), sdt.length, sdt.revision }, 180 | ); 181 | } 182 | 183 | pub fn getTable(signature: []const u8) ?*Header { 184 | if (xsdt) |x| { 185 | for (getEntries(u64, x)) |ent| { 186 | var entry = @as(*Header, @ptrFromInt(ent + pmm.hhdm_response.offset)); 187 | if (std.mem.eql(u8, signature[0..4], &entry.signature)) 188 | return entry; 189 | } 190 | } else { 191 | for (getEntries(u32, rsdt.?)) |ent| { 192 | var entry = @as(*Header, @ptrFromInt(ent + pmm.hhdm_response.offset)); 193 | if (std.mem.eql(u8, signature[0..4], &entry.signature)) 194 | return entry; 195 | } 196 | } 197 | 198 | return null; 199 | } 200 | 201 | pub fn pmSleep(us: u64) void { 202 | const shift: u64 = @as(u64, 1) << @as(u6, @truncate(timer_bits)); 203 | const target = (us * 3) + ((us * 5) / 10) + ((us * 8) / 100); 204 | 205 | var n: u64 = target / shift; 206 | var remaining: u64 = target % shift; 207 | 208 | var cur_ticks = timer_block.read(u32); 209 | remaining += cur_ticks; 210 | 211 | if (remaining < cur_ticks) n += 1 else { 212 | n += remaining / shift; 213 | remaining = remaining % shift; 214 | } 215 | 216 | var new_ticks: u32 = 0; 217 | while (n > 0) { 218 | new_ticks = timer_block.read(u32); 219 | if (new_ticks < cur_ticks) n -= 1; 220 | cur_ticks = new_ticks; 221 | } 222 | 223 | while (remaining > cur_ticks) { 224 | new_ticks = timer_block.read(u32); 225 | if (new_ticks < cur_ticks) break; 226 | cur_ticks = new_ticks; 227 | } 228 | } 229 | 230 | pub fn init() void { 231 | if (rsdp_request.response) |rsdp| 232 | rsdp_response = rsdp.*; 233 | 234 | const xsdp = @as(*align(1) const Xsdp, @ptrFromInt(rsdp_response.address)); 235 | 236 | if (xsdp.revision >= 2 and xsdp.xsdt != 0) 237 | xsdt = @as(*Header, @ptrFromInt(xsdp.xsdt + pmm.hhdm_response.offset)) 238 | else 239 | rsdt = @as(*Header, @ptrFromInt(xsdp.rsdt + pmm.hhdm_response.offset)); 240 | 241 | log.debug("ACPI tables:", .{}); 242 | if (xsdt) |x| { 243 | for (getEntries(u64, x)) |ent| { 244 | const entry = @as(*Header, @ptrFromInt(ent + pmm.hhdm_response.offset)); 245 | printTable(entry); 246 | } 247 | } else { 248 | for (getEntries(u32, rsdt.?)) |ent| { 249 | const entry = @as(*Header, @ptrFromInt(ent + pmm.hhdm_response.offset)); 250 | printTable(entry); 251 | } 252 | } 253 | 254 | if (getTable("APIC")) |madt_sdt| 255 | madt = @as(*align(1) Madt, @constCast(@ptrCast(madt_sdt.getContents()))); 256 | 257 | if (getTable("FACP")) |fadt_sdt| { 258 | const fadt = @as(*align(1) const Fadt, @ptrCast(fadt_sdt.getContents())); 259 | 260 | if (xsdp.revision >= 2 and fadt.x_pm_timer_blk.base_type == 0) { 261 | timer_block = fadt.x_pm_timer_blk; 262 | timer_block.base = timer_block.base + pmm.hhdm_response.offset; 263 | } else { 264 | if (fadt.pm_timer_blk == 0 or fadt.pm_timer_length != 4) 265 | @panic("ACPI timer is unsupported/malformed"); 266 | 267 | timer_block = GenericAddress{ 268 | .base = fadt.pm_timer_blk, 269 | .base_type = 1, 270 | .bit_width = 32, 271 | .bit_offset = 0, 272 | .access_size = 0, 273 | }; 274 | } 275 | 276 | timer_bits = if ((fadt.flags & (1 << 8)) == 0) 32 else 24; 277 | 278 | if (timer_block.base_type == 0) 279 | log.debug("Detected MMIO ACPI timer with a {}-bit counter width", .{timer_bits}) 280 | else 281 | log.debug("Detected PMIO ACPI timer with a {}-bit counter width", .{timer_bits}); 282 | } else { 283 | //return error.InvalidHardware; 284 | @panic("ACPI timer is unsupported/malformed"); 285 | } 286 | } 287 | -------------------------------------------------------------------------------- /src/arch/x86_64/apic.zig: -------------------------------------------------------------------------------- 1 | pub const lapic = @import("apic/lapic.zig"); 2 | pub const ioapic = @import("apic/ioapic.zig"); -------------------------------------------------------------------------------- /src/arch/x86_64/apic/ioapic.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const acpi = @import("../acpi.zig"); 3 | const mmio = @import("../../../mmio.zig"); 4 | const pmm = @import("../../../mm/pmm.zig"); 5 | const arch = @import("../../x86_64.zig"); 6 | 7 | pub const Redirect = packed struct(u64) { 8 | interrupt_vector: u8 = 0, 9 | delivery_mode: DeliveryMode = .Fixed, 10 | destination_mode: DestinationMode = .Physical, 11 | delivery_status: bool = false, 12 | interrupt_pin: InterruptPinPolarity = .High, 13 | remote_irr: bool = false, 14 | trigger_mode: bool = false, 15 | interrupt_mask: bool = false, 16 | reserved: u39 = 0, 17 | destination: u8 = 0, 18 | 19 | const InterruptPinPolarity = enum(u1) { 20 | High = 0, 21 | Low = 1, 22 | }; 23 | 24 | const DestinationMode = enum(u1) { 25 | Physical = 0, 26 | Logical = 1, 27 | }; 28 | 29 | const DeliveryMode = enum(u3) { 30 | Fixed = 0b000, 31 | LowestPriority = 0b001, 32 | SystemManagementInterrupt = 0b010, 33 | NonMaskableInterrupt = 0b100, 34 | Init = 0b101, 35 | ExtInt = 0b111, 36 | }; 37 | }; 38 | 39 | pub const IoApic = extern struct { 40 | id: u8, 41 | _reserved: u8, 42 | address: u32, 43 | gsi_base: u32, 44 | 45 | pub fn read(self: *align(1) IoApic, reg: u32) void { 46 | const base: u64 = self.ioapic_addr + pmm.hhdm_response.offset; 47 | @as(*volatile u32, @ptrFromInt(base)).* = reg; 48 | return @as(*volatile u32, @ptrFromInt(base + 16)).*; 49 | } 50 | 51 | pub fn write(self: *align(1) IoApic, reg: u32, value: u32) void { 52 | const base: u64 = self.address + pmm.hhdm_response.offset; 53 | @as(*volatile u32, @ptrFromInt(base)).* = reg; 54 | @as(*volatile u32, @ptrFromInt(base + 16)).* = value; 55 | } 56 | 57 | pub fn redirect_gsi(self: *align(1) IoApic, lapic_id: u32, interrupt_number: u8, gsi: u32, flags: u16) void { 58 | var redirect = Redirect{ .interrupt_vector = interrupt_number }; 59 | if ((flags & (1 << 1)) == (1 << 1)) redirect.interrupt_pin = .Low; 60 | if ((flags & (1 << 3)) == (1 << 3)) redirect.trigger_mode = true; 61 | redirect.destination = @intCast(lapic_id); 62 | 63 | const redirect_table = (gsi - self.gsi_base) * 2 + 16; 64 | self.write(redirect_table, @as(u32, @truncate(@as(u64, @bitCast(redirect))))); 65 | self.write(redirect_table + 1, @as(u32, @truncate(@as(u64, @bitCast(redirect)) >> 32))); 66 | } 67 | 68 | pub fn redirect_irq(self: *align(1) IoApic, lapic_id: u32, interrupt_number: u8, irq: u8) void { 69 | if (acpi.madt.?.get_iso(irq)) |iso| 70 | self.redirect_gsi(lapic_id, interrupt_number, iso.gsi, iso.flags) 71 | else 72 | self.redirect_gsi(lapic_id, interrupt_number, irq, 0); 73 | } 74 | 75 | pub fn init(self: *align(1) IoApic) void { 76 | for (0..16) |i| 77 | self.redirect_irq(0, @as(u8, @intCast(i)) + 32, @as(u8, @intCast(i))); 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /src/arch/x86_64/apic/lapic.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const acpi = @import("../acpi.zig"); 3 | const pmm = @import("../../../mm/pmm.zig"); 4 | const vmm = @import("../vmm.zig"); 5 | const smp = @import("../smp.zig"); 6 | const arch = @import("../../x86_64.zig"); 7 | const log = std.log.scoped(.lapic); 8 | 9 | const TimerMode = enum(u4) { 10 | Tsc, 11 | Lapic, 12 | Unknown, 13 | }; 14 | 15 | var mmio_base: u64 = 0; 16 | var tsc_mode: TimerMode = .Unknown; 17 | 18 | // general regs 19 | const REG_VER = 0x30; 20 | const REG_EOI = 0xb0; 21 | const REG_SPURIOUS = 0xf0; 22 | 23 | // timer regs 24 | const REG_TIMER_LVT = 0x320; 25 | const REG_TIMER_INIT = 0x380; 26 | const REG_TIMER_CNT = 0x390; 27 | const REG_TIMER_DIV = 0x3e0; 28 | 29 | pub fn read(reg: u32) u32 { 30 | return @as(*volatile u32, @ptrFromInt(mmio_base + reg)).*; 31 | } 32 | 33 | pub fn write(reg: u32, value: u32) void { 34 | @as(*volatile u32, @ptrFromInt(mmio_base + reg)).* = value; 35 | } 36 | 37 | inline fn canUseTsc() bool { 38 | if (tsc_mode == .Lapic) { 39 | return false; 40 | } else if (tsc_mode == .Tsc) { 41 | return true; 42 | } else { 43 | if (arch.cpuid(0x1, 0).ecx & (1 << 24) == 0 and 44 | arch.cpuid(0x80000007, 0).edx & (1 << 8) == 0) 45 | { 46 | tsc_mode = .Tsc; 47 | return true; 48 | } else { 49 | tsc_mode = .Lapic; 50 | return false; 51 | } 52 | } 53 | } 54 | 55 | pub fn enable() void { 56 | if (mmio_base == 0) 57 | mmio_base = (arch.rdmsr(0x1b) & 0xfffff000) + pmm.hhdm_response.offset; 58 | 59 | // enable the APIC 60 | arch.wrmsr(0x1b, arch.rdmsr(0x1b) | (1 << 11)); 61 | write(REG_SPURIOUS, read(REG_SPURIOUS) | (1 << 8) | 0xff); 62 | 63 | if (canUseTsc()) { 64 | const initial = arch.rdtsc(); 65 | 66 | // since AMD requires a "mfence" instruction to serialize the 67 | // TSC, and Intel requires a "lfence", use both here (not a big 68 | // deal since this is the only place where we need a serializing TSC) 69 | asm volatile ("mfence; lfence" ::: "memory"); 70 | 71 | acpi.pmSleep(1000); 72 | const final = arch.rdtsc(); 73 | asm volatile ("mfence; lfence" ::: "memory"); 74 | 75 | smp.getCoreInfo().ticks_per_ms = final - initial; 76 | } else { 77 | // on certain platforms (simics and some KVM machines), the 78 | // timer starts counting as soon as the APIC is enabled. 79 | // therefore, we must stop the timer before calibration... 80 | write(REG_TIMER_INIT, 0); 81 | 82 | // calibrate the APIC timer (using a 10ms sleep) 83 | write(REG_TIMER_DIV, 0x3); 84 | write(REG_TIMER_LVT, 0xff | (1 << 16)); 85 | write(REG_TIMER_INIT, std.math.maxInt(u32)); 86 | acpi.pmSleep(1000); 87 | 88 | // set the frequency, then set the timer back to a disabled state 89 | smp.getCoreInfo().ticks_per_ms = std.math.maxInt(u32) - read(REG_TIMER_CNT); 90 | write(REG_TIMER_INIT, 0); 91 | write(REG_TIMER_LVT, (1 << 16)); 92 | } 93 | } 94 | 95 | pub fn submitEoi(irq: u8) void { 96 | _ = irq; 97 | write(REG_EOI, 0); 98 | } 99 | 100 | pub fn oneshot(vec: u8, ms: u64) void { 101 | // stop the timer 102 | write(REG_TIMER_INIT, 0); 103 | write(REG_TIMER_LVT, (1 << 16)); 104 | 105 | // set the deadline, and off we go! 106 | const deadline: u32 = @truncate(smp.getCoreInfo().ticks_per_ms * ms); 107 | 108 | if (canUseTsc()) { 109 | write(REG_TIMER_LVT, @as(u32, vec) | (1 << 18)); 110 | arch.wrmsr(0x6e0, deadline); 111 | } else { 112 | write(REG_TIMER_LVT, vec); 113 | write(REG_TIMER_INIT, deadline); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/arch/x86_64/cpu.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arch = @import("../x86_64.zig"); 3 | const cr = @import("cr.zig"); 4 | const fpu = @import("fpu.zig"); 5 | const smp = @import("smp.zig"); 6 | const interrupt = @import("interrupt.zig"); 7 | const log = std.log.scoped(.cpu); 8 | 9 | pub export fn handleSyscall(frame: *interrupt.Frame) callconv(.C) void { 10 | log.err("Unsupported syscall #{}!", .{frame.rax}); 11 | } 12 | 13 | pub fn init() void { 14 | // Initialize x87 FPU 15 | asm volatile ("fninit"); 16 | 17 | // Enable SSE 18 | var cr0 = arch.cr.read(0); 19 | cr0 &= ~(@as(u64, 1) << 2); 20 | cr0 |= @as(u64, 1) << 1; 21 | arch.cr.write(0, cr0); 22 | 23 | var cr4 = arch.cr.read(4); 24 | cr4 |= @as(u64, 3) << 9; 25 | arch.cr.write(4, cr4); 26 | 27 | fpu.init(); 28 | 29 | // Set the CPU to a acceptable state 30 | cr.write(0, (cr.read(0) & ~@as(u64, 1 << 2)) | 0b10); 31 | cr.write(4, cr.read(4) | (1 << 7)); 32 | 33 | // Enable pkeys (if supported) 34 | if (arch.cpuid(7, 0).ecx & (1 << 3) != 0) 35 | cr.write(4, cr.read(4) | (1 << 22)); 36 | 37 | // Enable umip (if supported) 38 | if (arch.cpuid(7, 0).ecx & (1 << 2) != 0) 39 | cr.write(4, cr.read(4) | (1 << 11)); 40 | 41 | // Enable syscall 42 | arch.wrmsr(0xc0000081, (@as(u64, 0x30 | 0b11) << 48) | ((@as(u64, 0x28) << 32))); 43 | arch.wrmsr(0xc0000082, @intFromPtr(&syscallEntry)); 44 | arch.wrmsr(0xc0000080, arch.rdmsr(0xc0000080) | 1); 45 | arch.wrmsr(0xc0000084, ~@as(u32, 2)); 46 | } 47 | 48 | fn syscallEntry() callconv(.Naked) void { 49 | // zig fmt: off 50 | asm volatile ( 51 | // perform a swapgs and switch to the kernel stack 52 | \\swapgs 53 | \\movq %rsp, %%gs:16 54 | \\movq %%gs:28, %rsp 55 | \\sti 56 | 57 | // create a fake trapframe header 58 | \\pushq $0x38 59 | \\pushq %%gs:16 60 | \\pushq %r11 61 | \\pushq $0x40 62 | \\pushq %rcx 63 | \\pushq $0 64 | \\pushq $0 65 | 66 | // push remaining registers 67 | \\push %r15 68 | \\push %r14 69 | \\push %r13 70 | \\push %r12 71 | \\push %r11 72 | \\push %r10 73 | \\push %r9 74 | \\push %r8 75 | \\push %rbp 76 | \\push %rdi 77 | \\push %rsi 78 | \\push %rdx 79 | \\push %rcx 80 | \\push %rbx 81 | \\push %rax 82 | \\cld 83 | 84 | // call the syscall handler 85 | \\mov %rsp, %rdi 86 | \\xor %rbp, %rbp 87 | \\call handleSyscall 88 | 89 | // pop the trapframe back into place 90 | \\pop %rax 91 | \\pop %rbx 92 | \\pop %rcx 93 | \\pop %rdx 94 | \\pop %rsi 95 | \\pop %rdi 96 | \\pop %rbp 97 | \\pop %r8 98 | \\pop %r9 99 | \\pop %r10 100 | \\pop %r11 101 | \\pop %r12 102 | \\pop %r13 103 | \\pop %r14 104 | \\pop %r15 105 | \\add $16, %rsp 106 | 107 | // restore the context back to place 108 | \\cli 109 | \\mov %rsp, %%gs:16 110 | \\swapgs 111 | \\sysretq 112 | ); 113 | // zig fmt: on 114 | } 115 | -------------------------------------------------------------------------------- /src/arch/x86_64/cr.zig: -------------------------------------------------------------------------------- 1 | pub inline fn read(comptime cr: i8) u64 { 2 | return switch (cr) { 3 | 0 => asm volatile ("mov %%cr0, %[ret]" 4 | : [ret] "=r" (-> u64), 5 | ), 6 | 7 | 2 => asm volatile ("mov %%cr2, %[ret]" 8 | : [ret] "=r" (-> u64), 9 | ), 10 | 11 | 3 => asm volatile ("mov %%cr3, %[ret]" 12 | : [ret] "=r" (-> u64), 13 | ), 14 | 15 | 4 => asm volatile ("mov %%cr4, %[ret]" 16 | : [ret] "=r" (-> u64), 17 | ), 18 | 19 | 8 => asm volatile ("mov %%cr8, %[ret]" 20 | : [ret] "=r" (-> u64), 21 | ), 22 | 23 | else => unreachable, 24 | }; 25 | } 26 | 27 | pub inline fn write(comptime cr: i8, value: u64) void { 28 | switch (cr) { 29 | 0 => asm volatile ("mov %[value], %%cr0" 30 | : 31 | : [value] "r" (value), 32 | ), 33 | 34 | 2 => asm volatile ("mov %[value], %%cr2" 35 | : 36 | : [value] "r" (value), 37 | ), 38 | 39 | 3 => asm volatile ("mov %[value], %%cr3" 40 | : 41 | : [value] "r" (value), 42 | ), 43 | 44 | 4 => asm volatile ("mov %[value], %%cr4" 45 | : 46 | : [value] "r" (value), 47 | ), 48 | 49 | 8 => asm volatile ("mov %[value], %%cr8" 50 | : 51 | : [value] "r" (value), 52 | ), 53 | 54 | else => unreachable, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/arch/x86_64/fpu.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arch = @import("../x86_64.zig"); 3 | const smp = @import("smp.zig"); 4 | const log = std.log.scoped(.fpu); 5 | 6 | const SaveType = enum { 7 | fxsave, 8 | xsave, 9 | xsaveopt, 10 | xsavec, 11 | xsaves, 12 | }; 13 | 14 | const State = extern struct { 15 | // legacy x87 fpu context 16 | ctrl: u16, 17 | status: u16, 18 | tag: u16, 19 | fop: u16, 20 | ip: u64, 21 | dp: u64, 22 | 23 | // mxcsr control double-words 24 | mxcsr: u32, 25 | mxcsr_mask: u32, 26 | 27 | // x87 floating point regs 28 | st_regs: [32]u32, 29 | 30 | // SSE simd regs and padding 31 | xmm_regs: [64]u32, 32 | padding: [24]u32, 33 | }; 34 | 35 | const XSaveState = extern struct { 36 | state: State, 37 | xfeatures: u64, 38 | xcomp_bv: u64, 39 | reserved: [6]u64, 40 | }; 41 | 42 | const supported_mask = 0x602e7; 43 | 44 | var storage_size: usize = 0; 45 | var storage_align: usize = 0; 46 | var mode: SaveType = undefined; 47 | 48 | inline fn wrxcr(comptime reg: usize, value: u64) void { 49 | const edx: u32 = @as(u32, @truncate(value >> 32)); 50 | const eax: u32 = @as(u32, @truncate(value)); 51 | 52 | asm volatile ("xsetbv" 53 | : 54 | : [eax] "{eax}" (eax), 55 | [edx] "{edx}" (edx), 56 | [ecx] "{ecx}" (reg), 57 | : "memory" 58 | ); 59 | } 60 | 61 | pub fn restore(save_area: []const u8) void { 62 | std.debug.assert(@intFromPtr(&save_area) % storage_align == 0); 63 | 64 | const rbfm: u32 = 0xffffffff; 65 | const rbfm_high: u32 = 0xffffffff; 66 | 67 | switch (mode) { 68 | .xsave, .xsavec, .xsaveopt => { 69 | asm volatile ("xrstorq (%[context])" 70 | : 71 | : [context] "r" (save_area), 72 | [eax] "{eax}" (rbfm), 73 | [edx] "{edx}" (rbfm_high), 74 | : "memory" 75 | ); 76 | }, 77 | .xsaves => { 78 | asm volatile ("xrstorsq (%[context])" 79 | : 80 | : [context] "r" (save_area), 81 | [eax] "{eax}" (rbfm), 82 | [edx] "{edx}" (rbfm_high), 83 | : "memory" 84 | ); 85 | }, 86 | .fxsave => { 87 | asm volatile ("fxrstorq (%[context])" 88 | : 89 | : [context] "r" (save_area), 90 | : "memory" 91 | ); 92 | }, 93 | } 94 | } 95 | 96 | pub fn save(save_area: []const u8) void { 97 | std.debug.assert(@intFromPtr(&save_area) % storage_align == 0); 98 | 99 | const rbfm: u32 = 0xffffffff; 100 | const rbfm_high: u32 = 0xffffffff; 101 | 102 | switch (mode) { 103 | .xsave => { 104 | asm volatile ("xsaveq (%[context])" 105 | : 106 | : [context] "r" (@intFromPtr(&save_area)), 107 | [eax] "{eax}" (rbfm), 108 | [edx] "{edx}" (rbfm_high), 109 | : "memory" 110 | ); 111 | }, 112 | .xsavec => { 113 | asm volatile ("xsavecq (%[context])" 114 | : 115 | : [context] "r" (@intFromPtr(&save_area)), 116 | [eax] "{eax}" (rbfm), 117 | [edx] "{edx}" (rbfm_high), 118 | : "memory" 119 | ); 120 | }, 121 | .xsaves => { 122 | asm volatile ("xsavesq (%[context])" 123 | : 124 | : [context] "r" (@intFromPtr(&save_area)), 125 | [eax] "{eax}" (rbfm), 126 | [edx] "{edx}" (rbfm_high), 127 | : "memory" 128 | ); 129 | }, 130 | .xsaveopt => { 131 | asm volatile ("xsaveoptq (%[context])" 132 | : 133 | : [context] "r" (@intFromPtr(&save_area)), 134 | [eax] "{eax}" (rbfm), 135 | [edx] "{edx}" (rbfm_high), 136 | : "memory" 137 | ); 138 | }, 139 | .fxsave => { 140 | asm volatile ("fxsaveq (%[context])" 141 | : 142 | : [context] "r" (@intFromPtr(&save_area)), 143 | : "memory" 144 | ); 145 | }, 146 | } 147 | } 148 | 149 | pub fn init() void { 150 | // enable SSE & FXSAVE/FXRSTOR 151 | arch.cr.write(4, arch.cr.read(4) | (3 << 9)); 152 | 153 | if (arch.cpuid(1, 0).ecx & (1 << 26) != 0) { 154 | arch.cr.write(4, arch.cr.read(4) | (1 << 18)); 155 | storage_align = 64; 156 | mode = .xsave; 157 | 158 | var result = arch.cpuid(0x0d, 1); 159 | if (result.eax & (1 << 0) != 0) { 160 | mode = .xsaveopt; 161 | } 162 | if (result.eax & (1 << 1) != 0) { 163 | mode = .xsavec; 164 | } 165 | if (result.eax & (1 << 3) != 0) { 166 | mode = .xsaves; 167 | 168 | // clear XSS, since munix doesn't support any supervisor states 169 | arch.wrmsr(0xda0, 0); 170 | } 171 | 172 | wrxcr(0, @as(u64, arch.cpuid(0x0d, 0).eax) & supported_mask); 173 | result = arch.cpuid(0x0d, 0); 174 | 175 | if (smp.isBsp()) { 176 | log.debug("Supported extensions bitmask: 0x{x}", .{result.eax}); 177 | } 178 | 179 | switch (mode) { 180 | .xsave, .xsaveopt => { 181 | storage_size = result.ecx; 182 | }, 183 | .xsavec => { 184 | storage_size = result.ebx; 185 | }, 186 | .xsaves => { 187 | storage_size = arch.cpuid(0x0d, 1).ebx; 188 | }, 189 | else => {}, 190 | } 191 | } else { 192 | storage_size = 512; 193 | storage_align = 16; 194 | mode = .fxsave; 195 | } 196 | 197 | if (smp.isBsp()) { 198 | log.debug("Using \"{s}\" instruction (with size={}) for FPU context management", .{ @tagName(mode), storage_size }); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/arch/x86_64/gdt.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arch = @import("../x86_64.zig"); 3 | 4 | pub const Gdt = extern struct { 5 | entries: [9]u64, 6 | tss: Tss.Entry, 7 | 8 | pub fn flush(gdtr: arch.Descriptor) void { 9 | const gs_base = arch.rdmsr(0xc0000101); 10 | 11 | asm volatile ( 12 | \\lgdt %[gdtr] 13 | \\push $0x28 14 | \\lea 1f(%%rip), %%rax 15 | \\push %%rax 16 | \\lretq 17 | \\1: 18 | \\mov $0x30, %%eax 19 | \\mov %%eax, %%ds 20 | \\mov %%eax, %%es 21 | \\mov %%eax, %%fs 22 | \\mov %%eax, %%gs 23 | \\mov %%eax, %%ss 24 | : 25 | : [gdtr] "*p" (&gdtr), 26 | : "rax", "rcx", "memory" 27 | ); 28 | 29 | arch.wrmsr(0xc0000101, gs_base); 30 | } 31 | }; 32 | 33 | pub const Tss = extern struct { 34 | pub const Entry = extern struct { 35 | length: u16 align(1), 36 | base_low: u16 align(1), 37 | base_mid: u8 align(1), 38 | flags: u16 align(1), 39 | base_high: u8 align(1), 40 | base_upper: u32 align(1), 41 | _reserved: u32 align(1) = 0, 42 | }; 43 | 44 | _reserved0: u32 align(1) = 0, 45 | rsp0: u64 align(1) = 0, 46 | rsp1: u64 align(1) = 0, 47 | rsp2: u64 align(1) = 0, 48 | _reserved1: u64 align(1) = 0, 49 | ist1: u64 align(1) = 0, 50 | ist2: u64 align(1) = 0, 51 | ist3: u64 align(1) = 0, 52 | ist4: u64 align(1) = 0, 53 | ist5: u64 align(1) = 0, 54 | ist6: u64 align(1) = 0, 55 | ist7: u64 align(1) = 0, 56 | _reserved2: u64 align(1) = 0, 57 | _reserved3: u16 align(1) = 0, 58 | iopb_offset: u16 align(1) = 0, 59 | 60 | pub fn init(self: *Tss) void { 61 | const addr: u64 = @intFromPtr(self); 62 | 63 | gdt.tss.base_low = @as(u16, @truncate(addr)); 64 | gdt.tss.base_mid = @as(u8, @truncate(addr >> 16)); 65 | gdt.tss.flags = 0b10001001; 66 | gdt.tss.base_high = @as(u8, @truncate(addr >> 24)); 67 | gdt.tss.base_upper = @as(u32, @truncate(addr >> 32)); 68 | } 69 | 70 | pub fn flush(_: *Tss) void { 71 | asm volatile ("ltr %[tss]" 72 | : 73 | : [tss] "r" (@as(u16, 0x48)), 74 | : "memory" 75 | ); 76 | } 77 | }; 78 | 79 | var gdt: Gdt = .{ 80 | .entries = .{ 81 | // null entry 82 | 0x0000000000000000, 83 | 84 | // 16-bit kernel code/data 85 | 0x00009a000000ffff, 86 | 0x000093000000ffff, 87 | 88 | // 32-bit kernel code/data 89 | 0x00cf9a000000ffff, 90 | 0x00cf93000000ffff, 91 | 92 | // 64-bit kernel code/data 93 | 0x00af9b000000ffff, 94 | 0x00af93000000ffff, 95 | 96 | // 64-bit user data/code 97 | 0x00affa000000ffff, 98 | 0x008ff2000000ffff, 99 | }, 100 | .tss = .{ 101 | .length = 0x68, 102 | .base_low = 0, 103 | .base_mid = 0, 104 | .flags = 0b10001001, 105 | .base_high = 0, 106 | .base_upper = 0, 107 | }, 108 | }; 109 | 110 | pub fn init() void { 111 | const descriptor = arch.Descriptor{ 112 | .size = @sizeOf(Gdt) - 1, 113 | .ptr = @intFromPtr(&gdt), 114 | }; 115 | 116 | Gdt.flush(descriptor); 117 | } 118 | -------------------------------------------------------------------------------- /src/arch/x86_64/interrupt.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arch = @import("../x86_64.zig"); 3 | const sched = @import("sched.zig"); 4 | const log = std.log.scoped(.interrupt); 5 | 6 | pub const Frame = extern struct { 7 | const Self = @This(); 8 | 9 | rax: u64, 10 | rbx: u64, 11 | rcx: u64, 12 | rdx: u64, 13 | rsi: u64, 14 | rdi: u64, 15 | rbp: u64, 16 | r8: u64, 17 | r9: u64, 18 | r10: u64, 19 | r11: u64, 20 | r12: u64, 21 | r13: u64, 22 | r14: u64, 23 | r15: u64, 24 | vec: u64, 25 | error_code: u64, 26 | rip: u64, 27 | cs: u64, 28 | rflags: u64, 29 | rsp: u64, 30 | ss: u64, 31 | 32 | pub fn dump(self: *Self, log_func: anytype) void { 33 | log_func("RAX: {X:0>16} RBX: {X:0>16} RCX: {X:0>16}", .{ self.rax, self.rbx, self.rcx }); 34 | log_func("RDX: {X:0>16} RDI: {X:0>16} RSI: {X:0>16}", .{ self.rdx, self.rdi, self.rsi }); 35 | log_func("RBP: {X:0>16} R8: {X:0>16} R9: {X:0>16}", .{ self.rbp, self.r8, self.r9 }); 36 | log_func("R10: {X:0>16} R11: {X:0>16} R12: {X:0>16}", .{ self.r10, self.r11, self.r12 }); 37 | log_func("R13: {X:0>16} R14: {X:0>16} R15: {X:0>16}", .{ self.r13, self.r14, self.r15 }); 38 | log_func("RSP: {X:0>16} RIP: {X:0>16} CS: {X:0>16}", .{ self.rsp, self.rip, self.cs }); 39 | 40 | const cr2 = asm volatile ("mov %%cr2, %[out]" 41 | : [out] "=r" (-> u64), 42 | : 43 | : "memory" 44 | ); 45 | log_func("Linear address: 0x{X:0>16}, EC bits: 0x{X:0>8}", .{ cr2, self.error_code }); 46 | } 47 | }; 48 | 49 | const Entry = packed struct { 50 | base_low: u16, 51 | selector: u16, 52 | ist: u8, 53 | flags: u8, 54 | base_mid: u16, 55 | base_high: u32, 56 | _reserved: u32 = 0, 57 | 58 | fn init(stub: Stub, ist: u8) Entry { 59 | const addr: u64 = @intFromPtr(stub); 60 | 61 | return Entry{ 62 | .base_low = @as(u16, @truncate(addr)), 63 | .selector = 0x28, 64 | .ist = ist, 65 | .flags = 0x8e, 66 | .base_mid = @as(u16, @truncate(addr >> 16)), 67 | .base_high = @as(u32, @truncate(addr >> 32)), 68 | }; 69 | } 70 | }; 71 | 72 | const Stub = *const fn () callconv(.Naked) void; 73 | const Handler = *const fn (*Frame) callconv(.C) void; 74 | var entries: [256]Entry = undefined; 75 | var entries_generated: bool = false; 76 | 77 | export var handlers: [256]Handler = [_]Handler{handleException} ** 32 ++ [_]Handler{handleIrq} ** 224; 78 | 79 | pub fn setHandler(func: anytype, vec: u8) void { 80 | handlers[vec] = func; 81 | } 82 | 83 | pub fn init() void { 84 | const idtr = arch.Descriptor{ 85 | .size = @as(u16, (@sizeOf(Entry) * 256) - 1), 86 | .ptr = @intFromPtr(&entries), 87 | }; 88 | 89 | if (!entries_generated) { 90 | for (genStubTable(), 0..) |stub, idx| { 91 | if (idx == sched.TIMER_VECTOR) { 92 | entries[idx] = Entry.init(stub, 1); 93 | } else { 94 | entries[idx] = Entry.init(stub, 0); 95 | } 96 | } 97 | 98 | entries_generated = true; 99 | } 100 | 101 | asm volatile ("lidt %[idtr]" 102 | : 103 | : [idtr] "*p" (&idtr), 104 | ); 105 | } 106 | 107 | fn handleIrq(frame: *Frame) callconv(.C) void { 108 | log.err("CPU triggered IRQ #{}, which has no handler!", .{frame.vec}); 109 | @panic("Unhandled IRQ"); 110 | } 111 | 112 | fn handleException(frame: *Frame) callconv(.C) void { 113 | log.err("CPU exception #{}: {s}", .{ frame.vec, getExceptionName(frame.vec) }); 114 | frame.dump(log.err); 115 | log.err("System halted.", .{}); 116 | arch.halt(); 117 | } 118 | 119 | fn getExceptionName(vec: u64) []const u8 { 120 | return switch (vec) { 121 | 0 => "Division-by-zero", 122 | 1 => "Debug", 123 | 2 => "Non-maskable interrupt", 124 | 3 => "Breakpoint", 125 | 4 => "Overflow", 126 | 5 => "Bound range exceeded", 127 | 6 => "Invalid opcode", 128 | 7 => "Device not available", 129 | 8 => "Double fault", 130 | 9 => "Coprocessor segment overrun", 131 | 10 => "Invalid TSS", 132 | 11 => "Segment not present", 133 | 12 => "Stack fault", 134 | 13 => "General protection fault", 135 | 14 => "Page fault", 136 | 16 => "x87 floating-point exception", 137 | 17 => "Alignment check", 138 | 18 => "Machine check", 139 | 19 => "SIMD exception", 140 | 20 => "Virtualization exception", 141 | 30 => "Security exception", 142 | else => "Unknown", 143 | }; 144 | } 145 | 146 | fn genStubTable() [256]Stub { 147 | var result = [1]Stub{undefined} ** 256; 148 | 149 | comptime var i: usize = 0; 150 | inline while (i < 256) : (i += 1) { 151 | result[i] = comptime makeStub(i); 152 | } 153 | 154 | return result; 155 | } 156 | 157 | fn makeStub(comptime vec: u8) Stub { 158 | return struct { 159 | fn stub() callconv(.Naked) void { 160 | const has_ec = switch (vec) { 161 | 0x08 => true, 162 | 0x0a...0x0e => true, 163 | 0x11 => true, 164 | 0x15 => true, 165 | 0x1d...0x1e => true, 166 | else => false, 167 | }; 168 | 169 | if (!comptime (has_ec)) { 170 | asm volatile ("push $0"); 171 | } 172 | 173 | asm volatile ("push %[vec]" 174 | : 175 | : [vec] "i" (vec), 176 | ); 177 | 178 | // zig fmt: off 179 | asm volatile ( 180 | // perform a swapgs (if we came from usermode) 181 | \\cmpq $0x3b, 16(%rsp) 182 | \\jne 1f 183 | \\swapgs 184 | 185 | // push the trapframe 186 | \\1: 187 | \\push %r15 188 | \\push %r14 189 | \\push %r13 190 | \\push %r12 191 | \\push %r11 192 | \\push %r10 193 | \\push %r9 194 | \\push %r8 195 | \\push %rbp 196 | \\push %rdi 197 | \\push %rsi 198 | \\push %rdx 199 | \\push %rcx 200 | \\push %rbx 201 | \\push %rax 202 | \\cld 203 | 204 | // setup C enviroment and index into the handler 205 | \\lea handlers(%rip), %rbx 206 | \\add %[vec_off], %rbx 207 | \\mov %rsp, %rdi 208 | \\xor %rbp, %rbp 209 | \\call *(%rbx) 210 | 211 | // pop the trapframe back into place 212 | \\pop %rax 213 | \\pop %rbx 214 | \\pop %rcx 215 | \\pop %rdx 216 | \\pop %rsi 217 | \\pop %rdi 218 | \\pop %rbp 219 | \\pop %r8 220 | \\pop %r9 221 | \\pop %r10 222 | \\pop %r11 223 | \\pop %r12 224 | \\pop %r13 225 | \\pop %r14 226 | \\pop %r15 227 | \\add $16, %rsp 228 | 229 | // swap back to user gs (if needed) 230 | \\cmpq $0x3b, 8(%rsp) 231 | \\jne 1f 232 | \\swapgs 233 | 234 | // and away we go :-) 235 | \\1: 236 | \\iretq 237 | : 238 | : [vec_off] "i" (@as(u64, vec) * 8), 239 | ); 240 | } 241 | }.stub; 242 | // zig fmt: on 243 | } 244 | -------------------------------------------------------------------------------- /src/arch/x86_64/io.zig: -------------------------------------------------------------------------------- 1 | pub inline fn read(comptime T: type, port: u16) T { 2 | return switch (T) { 3 | u8 => asm volatile("inb %[port], %[ret]" 4 | : [ret] "={al}"(-> u8), 5 | : [port] "N{dx}"(port), 6 | ), 7 | 8 | u16 => asm volatile("inw %[port], %[ret]" 9 | : [ret] "={al}"(-> u16), 10 | : [port] "N{dx}"(port), 11 | ), 12 | 13 | u32 => asm volatile("inl %[port], %[ret]" 14 | : [ret] "={eax}"(-> u32), 15 | : [port] "N{dx}"(port), 16 | ), 17 | 18 | else => unreachable, 19 | }; 20 | } 21 | 22 | pub inline fn write(comptime T: type, port: u16, value: T) void { 23 | switch (T) { 24 | u8 => asm volatile("outb %[value], %[port]" 25 | : 26 | : [value] "{al}"(value), 27 | [port] "N{dx}"(port), 28 | ), 29 | 30 | u16 => asm volatile("outw %[value], %[port]" 31 | : 32 | : [value] "{al}"(value), 33 | [port] "N{dx}"(port), 34 | ), 35 | 36 | u32 => asm volatile("outl %[value], %[port]" 37 | : 38 | : [value] "{eax}"(value), 39 | [port] "N{dx}"(port), 40 | ), 41 | 42 | else => unreachable, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/arch/x86_64/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const arch = @import("../x86_64.zig"); 4 | const gdt = @import("gdt.zig"); 5 | const interrupt = @import("interrupt.zig"); 6 | const framebuffer = @import("../../framebuffer.zig"); 7 | const ydin = @import("../../main.zig"); 8 | const writer = @import("../../writer.zig"); 9 | 10 | pub fn start() callconv(.C) void { 11 | framebuffer.init(); 12 | writer.init(); 13 | gdt.init(); 14 | interrupt.init(); 15 | ydin.main() catch unreachable; 16 | arch.halt(); 17 | } 18 | -------------------------------------------------------------------------------- /src/arch/x86_64/sched.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const interrupt = @import("interrupt.zig"); 3 | const arch = @import("../x86_64.zig"); 4 | const lapic = @import("apic/lapic.zig"); 5 | //const proc = @import("proc.zig"); 6 | const smp = @import("smp.zig"); 7 | const pmm = @import("../../mm/pmm.zig"); 8 | const vmm = @import("vmm.zig"); 9 | const slab = @import("../../mm/slab.zig"); 10 | 11 | pub const Thread = struct { 12 | id: usize = 0, 13 | link: Node, 14 | context: interrupt.Frame, 15 | kernel_stack: u64, 16 | //proc: *proc.Process, 17 | }; 18 | 19 | pub const Node = struct { 20 | next: ?*Node = undefined, 21 | }; 22 | 23 | pub fn Queue(comptime T: type, comptime member_name: []const u8) type { 24 | return struct { 25 | head: ?*Node = null, 26 | tail: ?*Node = null, 27 | lock: arch.Spinlock = .{}, 28 | 29 | fn refToNode(ref: *T) *Node { 30 | return &@field(ref, member_name); 31 | } 32 | 33 | fn nodeToRef(node: *Node) *T { 34 | return @fieldParentPtr(member_name, node); 35 | } 36 | 37 | pub fn enqueue(self: *@This(), node: *T) void { 38 | self.lock.acq(); 39 | defer self.lock.rel(); 40 | 41 | const hook = refToNode(node); 42 | hook.next = null; 43 | 44 | if (self.tail) |tail_nonnull| { 45 | tail_nonnull.next = hook; 46 | self.tail = hook; 47 | } else { 48 | std.debug.assert(self.head == null); 49 | self.head = hook; 50 | self.tail = hook; 51 | } 52 | } 53 | 54 | pub fn dequeue(self: *@This()) ?*T { 55 | self.lock.acq(); 56 | defer self.lock.rel(); 57 | 58 | if (self.head) |head_nonnull| { 59 | if (head_nonnull.next) |next| { 60 | self.head = next; 61 | } else { 62 | self.head = null; 63 | self.tail = null; 64 | } 65 | return nodeToRef(head_nonnull); 66 | } 67 | return null; 68 | } 69 | }; 70 | } 71 | 72 | pub const TIMER_VECTOR = 0x30; 73 | var thread_list = Queue(Thread, "link"){}; 74 | var sched_lock = arch.Spinlock{}; 75 | 76 | pub fn exit() noreturn { 77 | smp.getCoreInfo().cur_thread = null; 78 | lapic.oneshot(TIMER_VECTOR, 1); 79 | 80 | while (true) {} 81 | } 82 | 83 | pub fn createKernelStack() ?u64 { 84 | if (pmm.alloc(1)) |page| { 85 | return (@intFromPtr(page) + std.mem.page_size) + pmm.hhdm_response.offset; 86 | } else { 87 | return null; 88 | } 89 | } 90 | 91 | fn getNextThread() *Thread { 92 | sched_lock.acq(); 93 | defer sched_lock.rel(); 94 | 95 | if (thread_list.dequeue()) |elem| { 96 | return elem; 97 | } else { 98 | // set a new timer for later 99 | sched_lock.rel(); 100 | lapic.submitEoi(TIMER_VECTOR); 101 | lapic.oneshot(TIMER_VECTOR, 20); 102 | 103 | vmm.pagemap.load(); 104 | arch.enableInterrupts(); 105 | 106 | while (true) {} 107 | } 108 | } 109 | 110 | pub fn reschedule(frame: *interrupt.Frame) callconv(.C) void { 111 | if (smp.getCoreInfo().cur_thread) |old_thread| { 112 | old_thread.context = frame.*; 113 | smp.getCoreInfo().current_thread = null; 114 | 115 | sched_lock.acq(); 116 | thread_list.enqueue(old_thread); 117 | sched_lock.rel(); 118 | } 119 | 120 | var thread = getNextThread(); 121 | smp.getCoreInfo().current_thread = thread; 122 | smp.getCoreInfo().tss.rsp0 = thread.kernel_stack; 123 | 124 | frame.* = thread.context; 125 | thread.proc.pagemap.load(); 126 | 127 | lapic.submitEoi(TIMER_VECTOR); 128 | lapic.oneshot(TIMER_VECTOR, 20); 129 | } 130 | 131 | pub fn spawnKernelThread(func: *const fn (u64) noreturn, arg: ?u64) !*Thread { 132 | var thread = slab.allocator.create(Thread); 133 | errdefer slab.allocator.destroy(thread); 134 | 135 | thread.kernel_stack = createKernelStack() orelse return error.OutOfMemory; 136 | thread.context = std.mem.zeroes(interrupt.Frame); 137 | //thread.proc = &proc.kernel_process; 138 | 139 | thread.context.rip = @intFromPtr(func); 140 | thread.context.rsp = thread.kernel_stack; 141 | thread.context.ss = 0x30; 142 | thread.context.cs = 0x28; 143 | thread.context.rflags = 0x202; 144 | 145 | if (arg) |elem| { 146 | thread.context.rdi = elem; 147 | } 148 | 149 | sched_lock.acq(); 150 | thread_list.enqueue(thread); 151 | sched_lock.rel(); 152 | 153 | return thread; 154 | } 155 | 156 | pub fn init() !void { 157 | smp.getCoreInfo().tss.ist1 = createKernelStack() orelse return error.OutOfMemory; 158 | 159 | lapic.oneshot(TIMER_VECTOR, 20); 160 | arch.enableInterrupts(); 161 | } 162 | -------------------------------------------------------------------------------- /src/arch/x86_64/serial.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const io = @import("io.zig"); 3 | 4 | port: u16 = 0x3f8, 5 | 6 | const INTERRUPT_OFFSET: u16 = 1; 7 | const FIFO_CONTROL_OFFSET: u16 = 2; 8 | const LINE_CONTROL_OFFSET: u16 = 3; 9 | const MODEM_CONTROL_OFFSET: u16 = 4; 10 | const LINE_STATUS_OFFSET: u16 = 5; 11 | 12 | const Self = @This(); 13 | pub const Error = error{}; 14 | 15 | /// Initialize the serial port. 16 | pub fn init(self: Self) void { 17 | // Disable all interupts. 18 | io.write(u8, self.port + INTERRUPT_OFFSET, 0x00); 19 | 20 | // Enable DLAB. 21 | io.write(u8, self.port + LINE_CONTROL_OFFSET, 0x80); 22 | 23 | // Set maximum speed to 115200 baud by configuring DLL 24 | // and DLM. 25 | io.write(u8, self.port, 0x01); 26 | io.write(u8, self.port + INTERRUPT_OFFSET, 0); 27 | 28 | // Disable DLAB and set data word length to 8 bits. 29 | io.write(u8, self.port + LINE_CONTROL_OFFSET, 0x03); 30 | 31 | // Enable FIFO, clear TX/RX queues, and set a 14-byte 32 | // interrupt threshold. 33 | io.write(u8, self.port + FIFO_CONTROL_OFFSET, 0xc7); 34 | 35 | // Mark data terminal ready, signal request to send and 36 | // enable auxilliary output #2. (used as interrupt line 37 | // for the CPU) 38 | io.write(u8, self.port + MODEM_CONTROL_OFFSET, 0x0b); 39 | 40 | // Enable interrupts. 41 | io.write(u8, self.port + INTERRUPT_OFFSET, 0x01); 42 | } 43 | 44 | /// Sends a byte on the serial port. 45 | pub fn writeByte(self: Self, byte: u8) void { 46 | switch (byte) { 47 | 0x7f => { 48 | io.write(u8, self.port, 0x7f); 49 | io.write(u8, self.port, ' '); 50 | io.write(u8, self.port, 0x7f); 51 | }, 52 | 0x0a => { 53 | io.write(u8, self.port, 0x0d); 54 | io.write(u8, self.port, 0x0a); 55 | }, 56 | else => io.write(u8, self.port, byte), 57 | } 58 | } 59 | 60 | /// Sends bytes to the serial port. 61 | pub fn write(self: Self, bytes: []const u8) Error!usize { 62 | for (bytes) |byte| 63 | writeByte(self, byte); 64 | 65 | return bytes.len; 66 | } 67 | 68 | /// Receives a byte on the serial port. 69 | pub fn read(self: Self) u8 { 70 | return io.read(u8, self.port); 71 | } 72 | -------------------------------------------------------------------------------- /src/arch/x86_64/smp.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arch = @import("../x86_64.zig"); 3 | const limine = @import("limine"); 4 | const pmm = @import("../../mm/pmm.zig"); 5 | const vmm = @import("vmm.zig"); 6 | const slab = @import("../../mm/slab.zig"); 7 | const gdt = @import("gdt.zig"); 8 | const interrupt = @import("interrupt.zig"); 9 | const sched = @import("sched.zig"); 10 | const acpi = @import("acpi.zig"); 11 | const apic = @import("apic.zig"); 12 | const cpu = @import("cpu.zig"); 13 | const log = std.log.scoped(.smp); 14 | 15 | pub export var smp_request: limine.Smp.Request = .{}; 16 | pub var smp_response: limine.Smp.Response = undefined; 17 | 18 | pub const CoreInfo = struct { 19 | processor_id: u32, 20 | lapic_id: u32, 21 | ticks_per_ms: u64 = 0, 22 | user_stack: u64 = 0, 23 | tss: gdt.Tss = .{}, 24 | is_bsp: bool = false, 25 | current_thread: ?*sched.Thread = null, 26 | }; 27 | 28 | var booted_cores = std.atomic.Value(u16).init(1); 29 | 30 | pub fn isBsp() bool { 31 | if (arch.rdmsr(0xc0000101) == 0) 32 | return true; 33 | 34 | return getCoreInfo().is_bsp; 35 | } 36 | 37 | pub inline fn getCoreInfo() *CoreInfo { 38 | return @ptrFromInt(arch.rdmsr(0xc0000101)); 39 | } 40 | 41 | pub inline fn setCoreInfo(core_info: *CoreInfo) void { 42 | arch.wrmsr(0xc0000101, @intFromPtr(core_info)); 43 | } 44 | 45 | fn createCoreInfo(cpu_info: *limine.Smp.Cpu) void { 46 | const core_info = slab.allocator.create(CoreInfo) catch unreachable; 47 | core_info.* = .{ 48 | .processor_id = cpu_info.processor_id, 49 | .lapic_id = cpu_info.lapic_id, 50 | }; 51 | setCoreInfo(core_info); 52 | } 53 | 54 | pub export fn smpEntry(cpu_info: *limine.Smp.Cpu) callconv(.C) noreturn { 55 | vmm.pagemap.load(); 56 | createCoreInfo(cpu_info); 57 | gdt.init(); 58 | interrupt.init(); 59 | cpu.init(); 60 | apic.lapic.enable(); 61 | 62 | getCoreInfo().tss.rsp0 = sched.createKernelStack().?; 63 | getCoreInfo().tss.init(); 64 | getCoreInfo().tss.flush(); 65 | 66 | _ = booted_cores.fetchAdd(1, .monotonic); 67 | sched.init() catch unreachable; 68 | 69 | while (true) {} 70 | } 71 | 72 | pub fn init() !void { 73 | if (smp_request.response) |smp| { 74 | log.debug("Detected {} CPUs.", .{smp.cpu_count}); 75 | 76 | for (smp.getCpus()) |cpu_info| { 77 | if (cpu_info.lapic_id == smp.bsp_lapic_id) { 78 | createCoreInfo(cpu_info); 79 | getCoreInfo().is_bsp = true; 80 | 81 | getCoreInfo().tss.rsp0 = sched.createKernelStack().?; 82 | getCoreInfo().tss.init(); 83 | getCoreInfo().tss.flush(); 84 | 85 | cpu.init(); 86 | apic.lapic.enable(); 87 | 88 | continue; 89 | } 90 | 91 | cpu_info.goto = &smpEntry; 92 | } 93 | 94 | while (booted_cores.load(.monotonic) != smp.cpu_count) {} 95 | log.info("All CPUs are online!", .{}); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/arch/x86_64/vmm.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const pmm = @import("../../mm/pmm.zig"); 4 | const slab = @import("../../mm/slab.zig"); 5 | const log = std.log.scoped(.vmm); 6 | 7 | pub export var kernel_address_request: limine.KernelAddress.Request = .{}; 8 | 9 | pub const CacheMode = enum(u4) { 10 | Uncached, 11 | WriteCombining, 12 | WriteProtect, 13 | WriteBack, 14 | }; 15 | 16 | pub const PageFlags = struct { 17 | read: bool = false, 18 | write: bool = false, 19 | exec: bool = false, 20 | user: bool = false, 21 | cache_type: CacheMode = .WriteBack, 22 | }; 23 | 24 | pub var pagemap = Pagemap{}; 25 | 26 | pub fn init() void { 27 | pagemap.root = @intFromPtr(pmm.alloc(1)); 28 | 29 | var page_flags = PageFlags{ 30 | .read = true, 31 | .write = true, 32 | .exec = true, 33 | }; 34 | 35 | if (kernel_address_request.response) |kernel_address| { 36 | const pbase: usize = kernel_address.physical_base; 37 | const vbase: usize = kernel_address.virtual_base; 38 | 39 | var i: usize = 0; 40 | while (i < (0x400 * 0x1000)) : (i += 0x1000) 41 | pagemap.mapPage(page_flags, vbase + i, pbase + i, false); 42 | 43 | i = 0; 44 | page_flags.exec = false; 45 | while (i < (0x800 * 0x200000)) : (i += 0x200000) 46 | pagemap.mapPage(page_flags, i + pmm.hhdm_response.offset, i, true); 47 | } 48 | 49 | for (pmm.memmap_response.getEntries()) |ent| { 50 | if (ent.base + ent.length < 0x800 * 0x200000) { 51 | continue; 52 | } 53 | 54 | const base: usize = std.mem.alignBackward(u64, ent.base, 0x200000); 55 | var i: usize = 0; 56 | 57 | while (i < std.mem.alignForward(u64, ent.length, 0x200000)) : (i += 0x200000) 58 | pagemap.mapPage(page_flags, (base + i) + pmm.hhdm_response.offset, base + i, true); 59 | } 60 | 61 | pagemap.load(); 62 | } 63 | 64 | pub const Pagemap = struct { 65 | root: u64 = undefined, 66 | 67 | pub fn load(self: *Pagemap) void { 68 | asm volatile ("mov %[root], %%cr3" 69 | : 70 | : [root] "r" (self.root), 71 | : "memory" 72 | ); 73 | } 74 | 75 | pub fn save(self: *Pagemap) void { 76 | self.root = asm volatile ("mov %%cr3, %[old_cr3]" 77 | : [old_cr3] "=r" (-> u64), 78 | : 79 | : "memory" 80 | ); 81 | } 82 | 83 | pub fn mapPage(self: *Pagemap, flags: PageFlags, virt: u64, phys: u64, huge: bool) void { 84 | var root: ?[*]u64 = @as([*]u64, @ptrFromInt(self.root + pmm.hhdm_response.offset)); 85 | 86 | const indices: [4]u64 = [_]u64{ 87 | genIndex(virt, 39), genIndex(virt, 30), 88 | genIndex(virt, 21), genIndex(virt, 12), 89 | }; 90 | 91 | root = getNextLevel(root.?, indices[0], true); 92 | if (root == null) return; 93 | 94 | root = getNextLevel(root.?, indices[1], true); 95 | if (root == null) return; 96 | 97 | if (huge) 98 | root.?[indices[2]] = createPte(flags, phys, true) 99 | else { 100 | root = getNextLevel(root.?, indices[2], true); 101 | root.?[indices[3]] = createPte(flags, phys, false); 102 | } 103 | } 104 | 105 | pub fn unmapPage(self: *Pagemap, virt: u64) void { 106 | var root: ?[*]u64 = @as([*]u64, @ptrFromInt(self.root + pmm.hhdm_response.offset)); 107 | 108 | const indices: [4]u64 = [_]u64{ 109 | genIndex(virt, 39), genIndex(virt, 30), 110 | genIndex(virt, 21), genIndex(virt, 12), 111 | }; 112 | 113 | root = getNextLevel(root.?, indices[0], false); 114 | if (root == null) return; 115 | 116 | root = getNextLevel(root.?, indices[1], false); 117 | if (root == null) return; 118 | 119 | if ((root.?[indices[2]] & (1 << 7)) != 0) 120 | root.?[indices[2]] &= ~@as(u64, 1) 121 | else if (getNextLevel(root.?, indices[2], false)) |final_root| 122 | final_root[indices[3]] &= ~@as(u64, 1); 123 | 124 | invalidatePage(virt); 125 | } 126 | }; 127 | 128 | inline fn genIndex(virt: u64, comptime shift: usize) u64 { 129 | return ((virt & (0x1ff << shift)) >> shift); 130 | } 131 | 132 | fn getNextLevel(level: [*]u64, index: usize, create: bool) ?[*]u64 { 133 | if ((level[index] & 1) == 0) { 134 | if (!create) return null; 135 | 136 | if (pmm.alloc(1)) |table_ptr| { 137 | level[index] = @intFromPtr(table_ptr); 138 | level[index] |= 0b111; 139 | } else return null; 140 | } 141 | 142 | return @as([*]u64, @ptrFromInt((level[index] & ~@as(u64, 0x1ff)) + pmm.hhdm_response.offset)); 143 | } 144 | 145 | fn createPte(flags: PageFlags, phys_ptr: u64, huge: bool) u64 { 146 | var result: u64 = 1; 147 | const pat_bit: u64 = if (huge) (1 << 12) else (1 << 7); 148 | 149 | if (flags.write) result |= (1 << 1); 150 | if (!flags.exec) result |= (1 << 63); 151 | if (flags.user) result |= (1 << 2); 152 | if (huge) result |= (1 << 7); 153 | 154 | switch (flags.cache_type) { 155 | .Uncached => { 156 | result |= (1 << 4) | (1 << 3); 157 | result &= ~pat_bit; 158 | }, 159 | .WriteCombining => result |= pat_bit | (1 << 4) | (1 << 3), 160 | .WriteProtect => { 161 | result |= pat_bit | (1 << 4); 162 | result &= ~@as(u64, 1 << 3); 163 | }, 164 | else => {}, 165 | } 166 | 167 | result |= phys_ptr; 168 | return result; 169 | } 170 | 171 | pub inline fn invalidatePage(addr: u64) void { 172 | asm volatile ("invlpg (%[virt])" 173 | : 174 | : [virt] "r" (addr), 175 | : "memory" 176 | ); 177 | } 178 | -------------------------------------------------------------------------------- /src/boot/limine.cfg: -------------------------------------------------------------------------------- 1 | TIMEOUT=0 2 | VERBOSE=yes 3 | 4 | :Kora 5 | PROTOCOL=limine 6 | KERNEL_PATH=boot:///vmydin 7 | KASLR=yes 8 | -------------------------------------------------------------------------------- /src/framebuffer.zig: -------------------------------------------------------------------------------- 1 | const limine = @import("limine"); 2 | const log = @import("std").log.scoped(.framebuffer); 3 | 4 | pub export var framebuffer_request: limine.Framebuffer.Request = .{}; 5 | pub var framebuffer_response: limine.Framebuffer.Response = undefined; 6 | 7 | /// Address to the framebuffer 8 | pub var address: u64 = 0; 9 | /// Width of the framebuffer in pixels 10 | pub var width: u64 = 0; 11 | /// Height of the framebuffer in pixels 12 | pub var height: u64 = 0; 13 | /// Pitch of the framebuffer in bytes 14 | pub var pitch: u64 = 0; 15 | /// Bits per pixel of the framebuffer 16 | pub var bpp: u16 = 0; 17 | pub var memory_model: MemoryModel = .Rgb; 18 | pub var red_mask_size: u8 = 0; 19 | pub var red_mask_shift: u8 = 0; 20 | pub var green_mask_size: u8 = 0; 21 | pub var green_mask_shift: u8 = 0; 22 | pub var blue_mask_size: u8 = 0; 23 | pub var blue_mask_shift: u8 = 0; 24 | pub var edid_size: u64 = 0; 25 | pub var edid: ?[]const u8 = null; 26 | 27 | pub fn init() void { 28 | if (framebuffer_request.response) |framebuffer| { 29 | framebuffer_response = framebuffer.*; 30 | 31 | if (framebuffer_response.framebuffer_count > 0) { 32 | address = framebuffer.getFramebuffers()[0].address; 33 | width = framebuffer.getFramebuffers()[0].width; 34 | height = framebuffer.getFramebuffers()[0].height; 35 | pitch = framebuffer.getFramebuffers()[0].pitch; 36 | bpp = framebuffer.getFramebuffers()[0].bpp; 37 | memory_model = @as(MemoryModel, @enumFromInt(@intFromEnum(framebuffer.getFramebuffers()[0].memory_model))); 38 | red_mask_size = framebuffer.getFramebuffers()[0].red_mask_size; 39 | red_mask_shift = framebuffer.getFramebuffers()[0].red_mask_shift; 40 | green_mask_size = framebuffer.getFramebuffers()[0].green_mask_size; 41 | green_mask_shift = framebuffer.getFramebuffers()[0].green_mask_shift; 42 | blue_mask_size = framebuffer.getFramebuffers()[0].blue_mask_size; 43 | blue_mask_shift = framebuffer.getFramebuffers()[0].blue_mask_shift; 44 | edid_size = framebuffer.getFramebuffers()[0].edid_size; 45 | edid = framebuffer.getFramebuffers()[0].getEdid(); 46 | } 47 | } 48 | } 49 | 50 | /// Returns a slice of the `address` pointer. 51 | pub fn getSlice(comptime T: type) []T { 52 | return @as([*]T, @ptrFromInt(address))[0 .. width * height]; 53 | } 54 | 55 | pub fn putPixel(x: usize, y: usize, color: u32) !void { 56 | switch (bpp) { 57 | 24 => getSlice(u24)[x + width * y] = @as(u24, @truncate(color)), 58 | 32 => getSlice(u32)[x + width * y] = color, 59 | else => return error.UnsupportedBpp, 60 | } 61 | } 62 | 63 | pub const MemoryModel = enum(u8) { 64 | Rgb = 1, 65 | _, 66 | }; 67 | -------------------------------------------------------------------------------- /src/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | PHDRS { 4 | null PT_NULL FLAGS(0); 5 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)); 6 | rodata PT_LOAD FLAGS((1 << 2)); 7 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)); 8 | } 9 | 10 | SECTIONS { 11 | . = 0xffffffff80000000; 12 | 13 | .text : { 14 | *(.text .text.*) 15 | } :text 16 | 17 | . += CONSTANT(MAXPAGESIZE); 18 | 19 | .rodata : { 20 | *(.rodata .rodata.*) 21 | } :rodata 22 | 23 | . += CONSTANT(MAXPAGESIZE); 24 | 25 | .data : { 26 | *(.data .data.*) 27 | } :data 28 | 29 | .bss : { 30 | *(COMMON) 31 | *(.bss .bss.*) 32 | } :data 33 | 34 | /DISCARD/ :{ 35 | *(.note .note.*) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const build_options = @import("build_options"); 4 | const arch = @import("arch.zig"); 5 | const pmm = @import("mm/pmm.zig"); 6 | const slab = @import("mm/slab.zig"); 7 | const psf = @import("psf.zig"); 8 | const writer = @import("writer.zig"); 9 | const uacpi = @import("acpi.zig"); 10 | pub const panic = @import("panic.zig").panic; 11 | 12 | var log_lock = arch.Spinlock{}; 13 | fn logFn(comptime level: std.log.Level, comptime scope: @Type(.EnumLiteral), comptime format: []const u8, args: anytype) void { 14 | log_lock.lock(); 15 | defer log_lock.unlock(); 16 | 17 | const scope_prefix = if (scope == .default) "main" else @tagName(scope); 18 | const prefix = "\x1b[32m[ydin:" ++ scope_prefix ++ "] " ++ switch (level) { 19 | .err => "\x1b[31merror", 20 | .warn => "\x1b[33mwarning", 21 | .info => "\x1b[36minfo", 22 | .debug => "\x1b[90mdebug", 23 | } ++ ": \x1b[0m"; 24 | writer.writer.print(prefix ++ format ++ "\n", args) catch unreachable; 25 | } 26 | pub const std_options = std.Options{ .logFn = logFn }; 27 | 28 | comptime { 29 | @export(arch.main.start, .{ .name = "_start", .linkage = .strong }); 30 | _ = uacpi; 31 | } 32 | 33 | pub fn main() !void { 34 | std.log.info("\x1b[94mKora\x1b[0m version {s}", .{build_options.version}); 35 | std.log.info("Compiled with Zig v{}", .{builtin.zig_version}); 36 | std.log.info("All your {s} are belong to us.", .{"codebase"}); 37 | pmm.init(); 38 | arch.vmm.init(); 39 | arch.acpi.init(); 40 | try arch.smp.init(); 41 | //var params = uacpi.uacpi.uacpi_init_params{ 42 | // .rsdp = arch.acpi.rsdp_response.address, 43 | // .log_level = uacpi.uacpi.UACPI_LOG_INFO, 44 | // .flags = uacpi.uacpi.UACPI_FLAG_NO_ACPI_MODE, 45 | //}; 46 | //_ = uacpi.uacpi.uacpi_initialize(¶ms); 47 | 48 | //@panic("test"); 49 | } 50 | -------------------------------------------------------------------------------- /src/math.zig: -------------------------------------------------------------------------------- 1 | pub fn min(a: usize, b: usize) usize { 2 | return if (a < b) a else b; 3 | } 4 | 5 | pub fn max(a: usize, b: usize) usize { 6 | return if (a > b) a else b; 7 | } 8 | 9 | pub fn divRoundup(a: usize, b: usize) usize { 10 | return (a + (b - 1)) / b; 11 | } 12 | 13 | pub fn alignUp(a: usize, b: usize) usize { 14 | return divRoundup(a, b) * b; 15 | } 16 | 17 | pub fn alignDown(a: usize, b: usize) usize { 18 | return (a / b) * b; 19 | } 20 | -------------------------------------------------------------------------------- /src/mm/pmm.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const arch = @import("../arch.zig"); 4 | const slab = @import("slab.zig"); 5 | const log = std.log.scoped(.pmm); 6 | 7 | const Bitmap = struct { 8 | bits: [*]u8, 9 | size: usize, 10 | 11 | pub fn set(self: Bitmap, bit: u64) void { 12 | self.bits[bit / 8] |= @as(u8, 1) << @as(u3, @truncate(bit % 8)); 13 | } 14 | 15 | pub fn unset(self: Bitmap, bit: u64) void { 16 | self.bits[bit / 8] &= ~(@as(u8, 1) << @as(u3, @truncate(bit % 8))); 17 | } 18 | 19 | pub fn check(self: Bitmap, bit: u64) bool { 20 | return (self.bits[bit / 8] & (@as(u8, 1) << @as(u3, @truncate(bit % 8)))) != 0; 21 | } 22 | }; 23 | 24 | var bitmap: Bitmap = undefined; 25 | var page_count: usize = 0; 26 | var last_used_index: usize = 0; 27 | var used_pages: usize = 0; 28 | var lock = arch.Spinlock{}; 29 | 30 | pub export var memmap_request: limine.MemoryMap.Request = .{}; 31 | pub var memmap_response: limine.MemoryMap.Response = undefined; 32 | pub export var hhdm_request: limine.Hhdm.Request = .{}; 33 | pub var hhdm_response: limine.Hhdm.Response = undefined; 34 | 35 | pub fn init() void { 36 | if (memmap_request.response) |memmap| { 37 | memmap_response = memmap.*; 38 | } 39 | 40 | if (hhdm_request.response) |hhdm| { 41 | hhdm_response = hhdm.*; 42 | } 43 | 44 | var highest_memory: u64 = 0; 45 | const entries = memmap_response.getEntries(); 46 | 47 | // Calculate how big should the memory map be. 48 | log.debug("Memory map layout:", .{}); 49 | for (entries) |entry| { 50 | log.debug(" base=0x{x:0>16}, length=0x{x:0>16}, type={s}", .{ entry.base, entry.length, @tagName(entry.type) }); 51 | if (entry.type != .Usable and entry.type != .BootloaderReclaimable) 52 | continue; 53 | 54 | const top = entry.base + entry.length; 55 | if (top > highest_memory) 56 | highest_memory = top; 57 | } 58 | 59 | // Calculate the needed size for the bitmap in bytes and align it to page size. 60 | page_count = highest_memory / std.mem.page_size; 61 | used_pages = page_count; 62 | bitmap.size = std.mem.alignForward(usize, page_count / 8, std.mem.page_size); 63 | 64 | log.debug("Used pages: {}", .{used_pages}); 65 | log.debug("Bitmap size: {} KB", .{bitmap.size / 1024}); 66 | 67 | // Find a hole for the bitmap in the memory map 68 | for (entries) |entry| { 69 | if (entry.type != .Usable) continue; 70 | if (entry.length >= bitmap.size) { 71 | bitmap.bits = @as([*]u8, @ptrFromInt(entry.base + hhdm_response.offset)); 72 | 73 | // Initialize the bitmap to 1 74 | @memset(bitmap.bits[0..bitmap.size], 0xff); 75 | 76 | entry.length -= bitmap.size; 77 | entry.base += bitmap.size; 78 | 79 | break; 80 | } 81 | } 82 | 83 | // Populate free bitmap entries according to the memory map. 84 | for (entries) |entry| { 85 | if (entry.type != .Usable) continue; 86 | 87 | var i: usize = 0; 88 | while (i < entry.length) : (i += std.mem.page_size) 89 | bitmap.unset((entry.base + i) / std.mem.page_size); 90 | } 91 | 92 | slab.init(); 93 | } 94 | 95 | fn inner_alloc(count: usize, limit: usize) ?[*]u8 { 96 | var p: usize = 0; 97 | while (last_used_index < limit) { 98 | if (!bitmap.check(last_used_index)) { 99 | last_used_index += 1; 100 | p += 1; 101 | 102 | if (p == count) { 103 | const page = last_used_index - count; 104 | 105 | var i: usize = 0; 106 | while (i < last_used_index) : (i += 1) 107 | bitmap.set(i); 108 | 109 | return @as([*]u8, @ptrFromInt(page * std.mem.page_size)); 110 | } 111 | } else { 112 | last_used_index += 1; 113 | p = 0; 114 | } 115 | } 116 | 117 | return null; 118 | } 119 | 120 | pub fn allocNz(count: usize) ?[*]u8 { 121 | lock.lock(); 122 | defer lock.unlock(); 123 | 124 | const last = last_used_index; 125 | var ret = inner_alloc(count, page_count); 126 | 127 | if (ret == null) { 128 | last_used_index = 0; 129 | ret = inner_alloc(count, last); 130 | if (ret == null) { 131 | @panic("Allocated memory is null"); 132 | } 133 | } 134 | 135 | used_pages += count; 136 | return ret; 137 | } 138 | 139 | pub fn alloc(count: usize) ?[*]u8 { 140 | const ret = allocNz(count); 141 | var ptr = @as([*]u8, @ptrFromInt(@intFromPtr(ret.?) + hhdm_response.offset)); 142 | 143 | var i: usize = 0; 144 | while (i < (count * std.mem.page_size) / 8) : (i += 1) 145 | ptr[i] = 0; 146 | 147 | return ret; 148 | } 149 | 150 | pub fn free(buf: ?[*]u8, count: usize) void { 151 | lock.lock(); 152 | defer lock.unlock(); 153 | 154 | const page = @intFromPtr(buf.?) / std.mem.page_size; 155 | 156 | var i: usize = 0; 157 | while (i < page + count) : (i += 1) 158 | bitmap.unset(i); 159 | 160 | used_pages -= count; 161 | } 162 | -------------------------------------------------------------------------------- /src/mm/slab.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arch = @import("../arch.zig"); 3 | const pmm = @import("pmm.zig"); 4 | const math = @import("../math.zig"); 5 | const Allocator = std.mem.Allocator; 6 | 7 | pub const Slab = struct { 8 | lock: arch.Spinlock, 9 | first_free: [*]u8, 10 | entry_size: u64, 11 | 12 | const Header = struct { slab: *Slab }; 13 | 14 | pub fn init(self: *Slab, entry_size: u64) void { 15 | self.entry_size = entry_size; 16 | self.first_free = @as([*]u8, @ptrFromInt(@intFromPtr(pmm.allocNz(1)) + pmm.hhdm_response.offset)); 17 | 18 | const size = std.mem.page_size - math.alignUp(@sizeOf(Header), entry_size); 19 | var slab_ptr = @as(*Header, @ptrCast(@alignCast(self.first_free))); 20 | slab_ptr.slab = self; 21 | self.first_free = @as([*]u8, @ptrFromInt(@intFromPtr(self.first_free) + math.alignUp(@sizeOf(Header), entry_size))); 22 | 23 | var array = @as([*]*u8, @ptrCast(@alignCast(self.first_free))); 24 | const max = size / entry_size - 1; 25 | const fact = entry_size / @sizeOf(*u8); 26 | 27 | var i: u64 = 0; 28 | while (i < max) : (i += 1) { 29 | array[i * fact] = @as(*u8, @ptrCast(&array[(i + 1) * fact])); 30 | } 31 | 32 | array[max * fact] = undefined; 33 | } 34 | 35 | pub fn alloc(self: *Slab) ?[*]u8 { 36 | self.lock.lock(); 37 | defer self.lock.unlock(); 38 | 39 | if (self.first_free == undefined) { 40 | self.init(self.entry_size); 41 | } 42 | 43 | var old_free = self.first_free; 44 | self.first_free = old_free; 45 | 46 | @memset(old_free[0..self.entry_size], 0); 47 | 48 | return old_free; 49 | } 50 | 51 | pub fn free(self: *Slab, ptr: ?[*]u8) void { 52 | self.lock.lock(); 53 | defer self.lock.unlock(); 54 | 55 | if (ptr == null) 56 | return; 57 | 58 | var new_head = ptr.?; 59 | new_head = self.first_free; 60 | 61 | self.first_free = new_head; 62 | } 63 | }; 64 | 65 | var slabs: [10]Slab = undefined; 66 | 67 | pub fn init() void { 68 | slabs[0].init(8); 69 | slabs[1].init(16); 70 | slabs[2].init(24); 71 | slabs[3].init(32); 72 | slabs[4].init(48); 73 | slabs[5].init(64); 74 | slabs[6].init(128); 75 | slabs[7].init(256); 76 | slabs[8].init(512); 77 | slabs[9].init(1024); 78 | } 79 | 80 | const AllocMetadata = struct { 81 | pages: usize, 82 | size: usize, 83 | }; 84 | 85 | fn slabFor(len: usize) *Slab { 86 | var i: u64 = 0; 87 | while (i < slabs.len) : (i += 1) { 88 | var slab = slabs[i]; 89 | if (slab.entry_size >= len) 90 | return &slab; 91 | } 92 | 93 | return undefined; 94 | } 95 | 96 | pub fn alloc(_: *anyopaque, len: usize, _: u8, _: usize) ?[*]u8 { 97 | var slab = slabFor(len); 98 | if (slab != undefined) 99 | return slab.alloc(); 100 | 101 | const page_count = math.divRoundup(len, std.mem.page_size); 102 | var ret = pmm.alloc(page_count + 1); 103 | if (ret == null) 104 | return null; 105 | 106 | ret = @as([*]u8, @ptrFromInt(@intFromPtr(ret.?) + pmm.hhdm_response.offset)); 107 | const metadata = @as(*AllocMetadata, @ptrCast(@alignCast(ret.?))); 108 | 109 | metadata.pages = page_count; 110 | metadata.size = len; 111 | 112 | return @as([*]u8, @ptrFromInt(@intFromPtr(ret) + std.mem.page_size)); 113 | } 114 | 115 | pub fn _resize(buf: []u8, new_size: usize) []u8 { 116 | if (buf.ptr == undefined) 117 | return alloc(undefined, new_size, 0, 0).?[0..new_size]; 118 | 119 | if ((@intFromPtr(buf.ptr) & 0xfff) == 0) { 120 | var metadata = @as(*AllocMetadata, @ptrFromInt(@intFromPtr(buf.ptr) - std.mem.page_size)); 121 | if (math.divRoundup(metadata.size, std.mem.page_size) == math.divRoundup(new_size, std.mem.page_size)) { 122 | metadata.size = new_size; 123 | return buf; 124 | } 125 | 126 | var new_buf = alloc(undefined, new_size, 0, 0); 127 | if (new_buf == null) 128 | return undefined; 129 | 130 | if (metadata.size > new_size) 131 | @memcpy(new_buf.?[0..new_size], buf.ptr) 132 | else 133 | @memcpy(new_buf.?[0..metadata.size], buf.ptr); 134 | 135 | free(undefined, buf, 0, 0); 136 | if (metadata.size > new_size) 137 | return new_buf.?[0..new_size] 138 | else 139 | return new_buf.?[0..metadata.size]; 140 | } 141 | 142 | const header = @as(*Slab.Header, @ptrFromInt(@intFromPtr(buf.ptr) & ~@as(u16, @intCast(0xfff)))); 143 | var slab = header.slab; 144 | 145 | if (new_size > slab.entry_size) { 146 | var new_buf = alloc(undefined, new_size, 0, 0); 147 | if (new_buf == null) 148 | return undefined; 149 | 150 | @memcpy(new_buf.?[0..slab.entry_size], buf.ptr); 151 | slab.free(buf.ptr); 152 | return buf; 153 | } 154 | 155 | return buf; 156 | } 157 | 158 | pub fn resize(_: *anyopaque, buf: []u8, _: u8, new_len: usize, _: usize) bool { 159 | var new_buf = _resize(buf, new_len); 160 | if (new_buf.ptr != undefined) { 161 | @memcpy(new_buf.ptr[0..new_len], buf.ptr); 162 | return true; 163 | } 164 | 165 | return false; 166 | } 167 | 168 | pub fn free(_: *anyopaque, buf: []u8, _: u8, _: usize) void { 169 | if (buf.ptr == undefined) 170 | return; 171 | 172 | if ((@intFromPtr(buf.ptr) & 0xfff) == 0) { 173 | const metadata = @as(*AllocMetadata, @ptrFromInt(@intFromPtr(buf.ptr) - std.mem.page_size)); 174 | pmm.free(@as([*]u8, @ptrFromInt(@intFromPtr(metadata) - pmm.hhdm_response.offset)), metadata.pages + 1); 175 | return; 176 | } 177 | 178 | const header = @as(*Slab.Header, @ptrFromInt(@intFromPtr(buf.ptr) & ~@as(u16, @intCast(0xfff)))); 179 | var slab = header.slab; 180 | slab.free(buf.ptr); 181 | } 182 | 183 | pub var allocator = Allocator{ 184 | .ptr = undefined, 185 | .vtable = &.{ 186 | .alloc = alloc, 187 | .resize = resize, 188 | .free = free, 189 | }, 190 | }; 191 | -------------------------------------------------------------------------------- /src/mmio.zig: -------------------------------------------------------------------------------- 1 | pub fn read(comptime T: type, address: u64) T { 2 | @fence(.seq_cst); 3 | return @as(*volatile T, @ptrFromInt(address)).*; 4 | } 5 | 6 | pub fn write(comptime T: type, address: u64, value: T) void { 7 | @fence(.seq_cst); 8 | @as(*volatile T, @ptrFromInt(address)).* = value; 9 | } 10 | -------------------------------------------------------------------------------- /src/panic.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const limine = @import("limine"); 4 | const arch = @import("arch.zig"); 5 | 6 | pub export var kernel_file_request: limine.KernelFile.Request = .{}; 7 | 8 | var debug_allocator_bytes: [16 * 1024 * 1024]u8 = undefined; 9 | var debug_allocator = std.heap.FixedBufferAllocator.init(debug_allocator_bytes[0..]); 10 | var debug_info: ?std.dwarf.DwarfInfo = null; 11 | 12 | pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, return_address: ?usize) noreturn { 13 | std.log.err("Kernel panic: {s}", .{message}); 14 | 15 | _ = return_address; 16 | //var stack_iterator = std.debug.StackIterator.init(return_address orelse @returnAddress(), @frameAddress()); 17 | //if (builtin.mode == .Debug) init() catch |err| 18 | // std.log.err("Failed to initialize debug info: {!}", .{err}); 19 | // 20 | //std.log.err("Stack trace:", .{}); 21 | //while (stack_iterator.next()) |address| 22 | // if (address != 0) 23 | // if (builtin.mode == .Debug) 24 | // printSymbol(address) 25 | // else 26 | // std.log.err(" 0x{x:0>16}", .{address}); 27 | 28 | std.log.err("System halted.", .{}); 29 | arch.halt(); 30 | 31 | unreachable; 32 | } 33 | 34 | fn init() !void { 35 | if (debug_info != null) return; 36 | errdefer debug_info = null; 37 | 38 | if (kernel_file_request.response) |response| { 39 | const kernel_file = @as([*]const u8, @ptrFromInt(response.kernel_file.base)); 40 | 41 | var sections = std.dwarf.DwarfInfo.null_section_array; 42 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_info)] = try getSectionSlice(kernel_file, ".debug_info"); 43 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_abbrev)] = try getSectionSlice(kernel_file, ".debug_abbrev"); 44 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_str)] = try getSectionSlice(kernel_file, ".debug_str"); 45 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_line)] = try getSectionSlice(kernel_file, ".debug_line"); 46 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_ranges)] = try getSectionSlice(kernel_file, ".debug_ranges"); 47 | 48 | debug_info = .{ 49 | .endian = .little, 50 | .is_macho = false, 51 | .sections = sections, 52 | }; 53 | 54 | try std.dwarf.openDwarfDebugInfo(&debug_info.?, debug_allocator.allocator()); 55 | } else { 56 | return error.NoKernelFile; 57 | } 58 | } 59 | 60 | fn printSymbol(address: u64) void { 61 | var symbol_name: []const u8 = ""; 62 | 63 | if (debug_info) |*info| brk: { 64 | if (info.getSymbolName(address)) |name| symbol_name = name; 65 | 66 | const compile_unit = info.findCompileUnit(address) catch break :brk; 67 | const line_info = info.getLineNumberInfo(debug_allocator.allocator(), compile_unit.*, address) catch break :brk; 68 | 69 | std.log.err(" 0x{x:0>16}: {s} at {s}:{d}:{d}", .{ address, symbol_name, line_info.file_name, line_info.line, line_info.column }); 70 | } 71 | 72 | std.log.err(" 0x{x:0>16}: {s} at ???:?:?", .{ address, symbol_name }); 73 | } 74 | 75 | fn getSectionData(elf: [*]const u8, shdr: []const u8) []const u8 { 76 | const offset = std.mem.readInt(u64, shdr[24..][0..8], .little); 77 | const size = std.mem.readInt(u64, shdr[32..][0..8], .little); 78 | 79 | return elf[offset .. offset + size]; 80 | } 81 | 82 | fn getSectionName(names: []const u8, shdr: []const u8) ?[]const u8 { 83 | const offset = std.mem.readInt(u32, shdr[0..][0..4], .little); 84 | const len = std.mem.indexOf(u8, names[offset..], "\x00") orelse return null; 85 | 86 | return names[offset .. offset + len]; 87 | } 88 | 89 | fn getShdr(elf: [*]const u8, idx: usize) []const u8 { 90 | const sh_offset = std.mem.readInt(u64, elf[40 .. 40 + 8], .little); 91 | const sh_entsize = std.mem.readInt(u16, elf[58 .. 58 + 2], .little); 92 | const off = sh_offset + sh_entsize * idx; 93 | 94 | return elf[off .. off + sh_entsize]; 95 | } 96 | 97 | fn getSectionAddress(elf: [*]const u8, section_name: []const u8) !u64 { 98 | const sh_strndx = std.mem.readInt(u16, elf[62 .. 62 + 2], .little); 99 | const sh_num = std.mem.readInt(u16, elf[60 .. 60 + 2], .little); 100 | 101 | if (sh_strndx > sh_num) return error.ShstrndxOutOfRange; 102 | 103 | const section_names = getSectionData(elf, getShdr(elf, sh_strndx)); 104 | 105 | var i: usize = 0; 106 | while (i < sh_num) : (i += 1) { 107 | const header = getShdr(elf, i); 108 | 109 | if (std.mem.eql(u8, getSectionName(section_names, header) orelse continue, section_name)) 110 | return std.mem.readInt(u64, header[24..][0..8], .little); 111 | } 112 | 113 | return error.SectionNotFound; 114 | } 115 | 116 | fn getSectionSlice(elf: [*]const u8, section_name: []const u8) !std.dwarf.DwarfInfo.Section { 117 | const sh_strndx = std.mem.readInt(u16, elf[62 .. 62 + 2], .little); 118 | const sh_num = std.mem.readInt(u16, elf[60 .. 60 + 2], .little); 119 | 120 | if (sh_strndx > sh_num) return error.ShstrndxOutOfRange; 121 | 122 | const section_names = getSectionData(elf, getShdr(elf, sh_strndx)); 123 | 124 | var i: usize = 0; 125 | while (i < sh_num) : (i += 1) { 126 | const header = getShdr(elf, i); 127 | 128 | if (std.mem.eql(u8, getSectionName(section_names, header) orelse continue, section_name)) { 129 | const data = getSectionData(elf, header); 130 | return .{ .data = data, .owned = false }; 131 | } 132 | } 133 | 134 | return error.SectionNotFound; 135 | } 136 | -------------------------------------------------------------------------------- /src/psf.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | // TODO: Add support for Unicode on PSF1 4 | pub const Psf1 = struct { 5 | pub const Header = struct { 6 | magic: u16 = 0x0436, 7 | flags: u8, 8 | height: u8, 9 | }; 10 | 11 | data: []const u8, 12 | header: *const Header, 13 | length: usize, 14 | 15 | pub fn init(font: []const u8) !Psf1 { 16 | if (font.len < @sizeOf(Header)) 17 | return error.OutOfBounds; 18 | 19 | var ret: Psf1 = undefined; 20 | 21 | ret.data = font; 22 | ret.header = @as(*const Header, @ptrCast(@alignCast(ret.data))); 23 | if (ret.header.magic != 0x0436) 24 | return error.InvalidMagic; 25 | 26 | ret.length = if (ret.header.flags == 1) 512 else 256; 27 | 28 | const last_glyph_offset = @sizeOf(Header) + @as(usize, @intCast(ret.header.height)) * ret.length; 29 | if (font.len < last_glyph_offset) 30 | return error.OutOfBounds; 31 | 32 | return ret; 33 | } 34 | 35 | pub fn getChar(self: *const Psf1, char: u8) ![]const u8 { 36 | return getIndex(self, char); 37 | } 38 | 39 | pub fn getIndex(self: *const Psf1, index: usize) ![]const u8 { 40 | if (index >= self.length) 41 | return error.OutOfBounds; 42 | 43 | const offset = @sizeOf(Header) + index * self.header.height; 44 | return self.data[offset..(offset + self.header.height)]; 45 | } 46 | }; 47 | 48 | pub const Psf2 = struct { 49 | pub const Header = struct { 50 | magic: u32 = 0x864ab572, 51 | version: u32, 52 | header_size: u32, 53 | flags: u32, 54 | length: u32, 55 | glyph_size: u32, 56 | height: u32, 57 | width: u32, 58 | }; 59 | 60 | data: []const u8, 61 | header: *align(1) const Header, 62 | unicode: std.StringArrayHashMap(usize), 63 | 64 | pub fn init(font: []const u8, allocator: std.mem.Allocator) !Psf2 { 65 | var ret: Psf2 = undefined; 66 | 67 | ret.data = font; 68 | ret.header = @as(*align(1) const Header, @ptrCast(ret.data)); 69 | if (ret.header.magic != 0x864ab572) 70 | return error.InvalidMagic; 71 | 72 | ret.unicode = std.StringArrayHashMap(usize).init(allocator); 73 | 74 | const last_glyph_offset = ret.header.header_size + ret.header.glyph_size * ret.header.length; 75 | if (font.len < last_glyph_offset) 76 | return error.OutOfBounds; 77 | 78 | if (ret.header.flags == 1) { 79 | const table = ret.data[last_glyph_offset..]; 80 | var table_iter = std.mem.splitScalar(u8, table, 0xFF); 81 | var i: usize = 0; 82 | while (table_iter.next()) |entry| : (i += 1) { 83 | var j: usize = 0; 84 | while (j < entry.len) : (j += 1) { 85 | const len: usize = std.unicode.utf8ByteSequenceLength(entry[j]) catch continue; 86 | const key = entry[j..][0..len]; 87 | try ret.unicode.put(key, i); 88 | j += len; 89 | } 90 | } 91 | } 92 | 93 | return ret; 94 | } 95 | 96 | pub fn deinit(self: *Psf2) void { 97 | self.unicode.deinit(); 98 | } 99 | 100 | pub fn getChar(self: *const Psf2, char: u21) ![]const u8 { 101 | var buf = [1]u8{0} ** 4; 102 | const len: usize = try std.unicode.utf8Encode(char, buf[0..]); 103 | return self.getUnicode(buf[0..len]); 104 | } 105 | 106 | pub fn getUnicode(self: *const Psf2, sequence: []u8) ![]const u8 { 107 | const index: usize = self.unicode.get(sequence) orelse if (std.ascii.isASCII(@as(u8, @intCast(try std.unicode.utf8Decode(sequence)))) and sequence.len == 1) 108 | sequence[0] 109 | else 110 | undefined; 111 | 112 | return self.getIndex(index); 113 | } 114 | 115 | pub fn getIndex(self: *const Psf2, index: usize) ![]const u8 { 116 | if (index >= self.header.length) 117 | return error.OutOfBounds; 118 | 119 | const offset = self.header.header_size + index * self.header.glyph_size; 120 | return self.data[offset..(offset + self.header.glyph_size)]; 121 | } 122 | }; 123 | -------------------------------------------------------------------------------- /src/terminal.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arch = @import("arch.zig"); 3 | const psf = @import("psf.zig"); 4 | -------------------------------------------------------------------------------- /src/uacpi/uacpi_libc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define UACPI_PRIx64 "lx" 6 | #define UACPI_PRIX64 "lX" 7 | #define UACPI_PRIu64 "lu" 8 | 9 | #define PRIx64 UACPI_PRIx64 10 | #define PRIX64 UACPI_PRIX64 11 | #define PRIu64 UACPI_PRIu64 12 | 13 | void *_memcpy(void *dest, const void* src, size_t size); 14 | void *_memset(void *dest, int src, size_t size); 15 | int _memcmp(const void *src1, const void *src2, size_t size); 16 | void *_memmove(void *dest, const void* src, size_t size); 17 | int _strncmp(const char *src1, const char *src2, size_t size); 18 | int _strcmp(const char *src1, const char *src2); 19 | size_t _strnlen(const char *src, size_t size); 20 | size_t _strlen(const char *src); 21 | 22 | #define uacpi_memcpy _memcpy 23 | #define uacpi_memset _memset 24 | #define uacpi_memcmp _memcmp 25 | #define uacpi_memmove _memmove 26 | #define uacpi_strncmp _strncmp 27 | #define uacpi_strcmp _strcmp 28 | #define uacpi_strnlen _strnlen 29 | #define uacpi_strlen _strlen 30 | 31 | #define uacpi_offsetof(t, m) ((uintptr_t)(&((t*)0)->m)) -------------------------------------------------------------------------------- /src/uacpi/uacpi_libc.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub export fn _memcpy(dest: [*]u8, src: [*]u8, size: usize) callconv(.C) [*]u8 { 4 | @memcpy(dest[0..size], src[0..size]); 5 | return dest; 6 | } 7 | 8 | pub export fn _memset(dest: [*c]u8, value: c_int, size: usize) callconv(.C) [*c]u8 { 9 | @memset(dest[0..size], @intCast(value)); 10 | return dest; 11 | } 12 | 13 | pub export fn _memcmp(src1: [*c]const u8, src2: [*c]const u8, size: usize) callconv(.C) c_int { 14 | return @intFromBool(std.mem.eql(u8, src1[0..size], src2[0..size])); 15 | } 16 | 17 | pub export fn _memmove(dest: [*]u8, src: [*]u8, size: usize) callconv(.C) [*]u8 { 18 | @memcpy(dest, src[0..size]); 19 | @memset(src[0..size], 0); 20 | return dest; 21 | } 22 | 23 | pub export fn _strncmp(src1: [*c]const u8, src2: [*c]const u8, length: usize) callconv(.C) c_int { 24 | return @intFromBool(std.mem.eql(u8, src1[0..length], src2[0..length])); 25 | } 26 | 27 | pub export fn _strcmp(src1: [*c]const u8, src2: [*c]const u8) callconv(.C) c_int { 28 | return @intFromBool(std.mem.eql(u8, std.mem.span(src1), std.mem.span(src2))); 29 | } 30 | 31 | pub export fn _strnlen(src: [*c]const u8, size: usize) callconv(.C) usize { 32 | return src[0..size].len; 33 | } 34 | 35 | pub export fn _strlen(src: [*c]const u8) callconv(.C) usize { 36 | return std.mem.span(src).len; 37 | } 38 | -------------------------------------------------------------------------------- /src/utils.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn readU32(value: []const u8) u32 { 4 | return std.mem.bigToNative(u32, @as(*const u32, @ptrCast(@alignCast(value.ptr))).*); 5 | } 6 | 7 | pub fn firstReg(address_cells: u32, value: []const u8) !u64 { 8 | if (value.len % @sizeOf(u32) != 0) { 9 | return error.BadStructure; 10 | } 11 | const big_endian_cells: []const u32 = @as([*]const u32, @ptrCast(@alignCast(value.ptr)))[0 .. value.len / @sizeOf(u32)]; 12 | if (address_cells == 1) { 13 | return std.mem.bigToNative(u32, big_endian_cells[0]); 14 | } else if (address_cells == 2) { 15 | return @as(u64, std.mem.bigToNative(u32, big_endian_cells[0])) << 32 | std.mem.bigToNative(u32, big_endian_cells[1]); 16 | } 17 | return error.UnsupportedCells; 18 | } 19 | -------------------------------------------------------------------------------- /src/writer.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const arch = @import("arch.zig"); 4 | 5 | var lock = arch.Spinlock{}; 6 | 7 | const DefaultWriter = switch (builtin.cpu.arch) { 8 | .x86_64 => @import("arch/x86_64/serial.zig"), 9 | .aarch64 => @import("arch/aarch64/serial.zig").Serial, 10 | else => unreachable, 11 | }; 12 | 13 | pub fn init() void { 14 | lock.lock(); 15 | defer lock.unlock(); 16 | 17 | const default_writer = switch (builtin.cpu.arch) { 18 | .x86_64 => DefaultWriter{}, 19 | .aarch64 => DefaultWriter() catch unreachable, 20 | else => unreachable, 21 | }; 22 | default_writer.init(); 23 | } 24 | 25 | pub fn write(_: *anyopaque, bytes: []const u8) anyerror!usize { 26 | lock.lock(); 27 | defer lock.unlock(); 28 | 29 | const default_writer = switch (builtin.cpu.arch) { 30 | .x86_64 => DefaultWriter{}, 31 | .aarch64 => try DefaultWriter(), 32 | else => unreachable, 33 | }; 34 | return default_writer.write(bytes); 35 | } 36 | 37 | pub const writer = std.io.Writer( 38 | *anyopaque, 39 | anyerror, 40 | write, 41 | ){ .context = undefined }; 42 | --------------------------------------------------------------------------------