initial commit
This commit is contained in:
commit
207c775cf5
3
.gitignore
vendored
Executable file
3
.gitignore
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
/.vscode
|
||||
/.zig-cache
|
||||
/zig-out
|
||||
1
.zigversion
Normal file
1
.zigversion
Normal file
@ -0,0 +1 @@
|
||||
0.14.0
|
||||
500
build.zig
Executable file
500
build.zig
Executable file
@ -0,0 +1,500 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const arch =
|
||||
b.option(std.Target.Cpu.Arch, "arch", "The target kernel architecture")
|
||||
orelse builtin.cpu.arch;
|
||||
|
||||
var query: std.Target.Query = .{
|
||||
.cpu_arch = arch,
|
||||
.os_tag = .freestanding,
|
||||
.abi = .none,
|
||||
};
|
||||
var code_model: std.builtin.CodeModel = .default;
|
||||
|
||||
switch (arch) {
|
||||
.aarch64 => {
|
||||
const Feature = std.Target.aarch64.Feature;
|
||||
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.fp_armv8));
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.crypto));
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.neon));
|
||||
},
|
||||
.loongarch64 => {
|
||||
const Feature = std.Target.loongarch.Feature;
|
||||
|
||||
query.cpu_features_add.addFeature(@intFromEnum(Feature.f));
|
||||
query.cpu_features_add.addFeature(@intFromEnum(Feature.d));
|
||||
},
|
||||
.riscv64 => {
|
||||
const Feature = std.Target.riscv.Feature;
|
||||
|
||||
query.cpu_features_add.addFeature(@intFromEnum(Feature.f));
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.d));
|
||||
},
|
||||
.x86_64 => {
|
||||
const Feature = std.Target.x86.Feature;
|
||||
|
||||
query.cpu_features_add.addFeature(@intFromEnum(Feature.soft_float));
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.mmx));
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.sse));
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.sse2));
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.avx));
|
||||
query.cpu_features_sub.addFeature(@intFromEnum(Feature.avx2));
|
||||
code_model = .kernel;
|
||||
},
|
||||
else => std.debug.panic("Unsupported architecture: {s}", .{@tagName(arch)}),
|
||||
}
|
||||
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
// means any target is allowed, and the default is native. Other options
|
||||
// for restricting supported target set are available.
|
||||
const target = b.resolveTargetQuery(query);
|
||||
|
||||
// Standard optimization options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const kernel_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.code_model = code_model,
|
||||
.red_zone = false,
|
||||
});
|
||||
|
||||
const kernel = b.addExecutable(.{
|
||||
.name = "kernel",
|
||||
// We will also create a module for our other entry point, 'main.zig'.
|
||||
.root_module = kernel_mod,
|
||||
});
|
||||
|
||||
// Disable LTO. This prevents Limine requests from being optimized away.
|
||||
kernel.want_lto = false;
|
||||
|
||||
// Add kernel dependencies.
|
||||
try resolveDependencies(b, kernel_mod);
|
||||
|
||||
// Set the linker script.
|
||||
const linker_script = try generateLinkerScript(b, arch);
|
||||
kernel.setLinkerScript(linker_script);
|
||||
|
||||
// This declares intent for the executable to be installed into the
|
||||
// standard location when the user invokes the "install" step (the default
|
||||
// step when running `zig build`).
|
||||
b.installArtifact(kernel);
|
||||
|
||||
const img = buildBootableImage(b, kernel.getEmittedBin(), arch);
|
||||
const run_cmd = generateRunCommand(b, img, arch);
|
||||
|
||||
// By making the run step depend on the install step, it will be run from the
|
||||
// installation directory rather than directly from within the cache directory.
|
||||
// This is not necessary, however, if the application depends on other installed
|
||||
// files, this ensures they will be present and in the expected location.
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
// This allows the user to pass arguments to the application in the build
|
||||
// command itself, like this: `zig build run -- arg1 arg2 etc`
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
// This creates a build step. It will be visible in the `zig build --help` menu,
|
||||
// and can be selected like this: `zig build run`
|
||||
// This will evaluate the `run` step rather than the default, which is "install".
|
||||
const run_step = b.step("run", "Run the kernel");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
// Creates a step for unit testing. This only builds the test executable
|
||||
// but does not run it.
|
||||
const exe_unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||
|
||||
// Similar to creating the run step earlier, this exposes a `test` step to
|
||||
// the `zig build --help` menu, providing a way for the user to request
|
||||
// running the unit tests.
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&run_exe_unit_tests.step);
|
||||
}
|
||||
|
||||
fn buildBootableImage(b: *std.Build, kernel: std.Build.LazyPath, arch: std.Target.Cpu.Arch) std.Build.LazyPath {
|
||||
// Add Limine to the dependency tree.
|
||||
const limine = b.dependency("limine", .{});
|
||||
const rootfs = b.addNamedWriteFiles("rootfs");
|
||||
|
||||
// Build the bootloader from source.
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "limine",
|
||||
.target = b.resolveTargetQuery(.{}),
|
||||
.optimize = .ReleaseSafe,
|
||||
});
|
||||
exe.addCSourceFile(.{
|
||||
.file = limine.path("limine.c"),
|
||||
.flags = &.{"-std=c99"},
|
||||
});
|
||||
exe.linkLibC();
|
||||
|
||||
_ = rootfs.addCopyFile(b.path("lib/limine/limine.conf"), "boot/limine/limine.conf");
|
||||
_ = rootfs.addCopyFile(kernel, "boot/kernel");
|
||||
|
||||
const run_cmd = b.addSystemCommand(switch (arch) {
|
||||
.aarch64 => block: {
|
||||
_ = rootfs.addCopyFile(limine.path("limine-uefi-cd.bin"), "boot/limine/limine-uefi-cd.bin");
|
||||
_ = rootfs.addCopyFile(limine.path("BOOTAA64.EFI"), "EFI/BOOT/BOOTAA64.EFI");
|
||||
|
||||
break :block &.{
|
||||
"xorriso", "-as", "mkisofs",
|
||||
"-R", "-r", "-J",
|
||||
"-hfsplus",
|
||||
"-apm-block-size", "2048",
|
||||
"--efi-boot", "boot/limine/limine-uefi-cd.bin",
|
||||
"-efi-boot-part",
|
||||
"--efi-boot-image",
|
||||
"--protective-msdos-label",
|
||||
};
|
||||
},
|
||||
.loongarch64 => block: {
|
||||
_ = rootfs.addCopyFile(limine.path("limine-uefi-cd.bin"), "boot/limine/limine-uefi-cd.bin");
|
||||
_ = rootfs.addCopyFile(limine.path("BOOTLOONGARCH64.EFI"), "EFI/BOOT/BOOTLOONGARCH64.EFI");
|
||||
|
||||
break :block &.{
|
||||
"xorriso", "-as", "mkisofs",
|
||||
"-R", "-r", "-J",
|
||||
"-hfsplus",
|
||||
"-apm-block-size", "2048",
|
||||
"--efi-boot", "boot/limine/limine-uefi-cd.bin",
|
||||
"-efi-boot-part",
|
||||
"--efi-boot-image",
|
||||
"--protective-msdos-label",
|
||||
};
|
||||
},
|
||||
.riscv64 => block: {
|
||||
_ = rootfs.addCopyFile(limine.path("limine-uefi-cd.bin"), "boot/limine/limine-uefi-cd.bin");
|
||||
_ = rootfs.addCopyFile(limine.path("BOOTRISCV64.EFI"), "EFI/BOOT/BOOTRISCV64.EFI");
|
||||
|
||||
break :block &.{
|
||||
"xorriso", "-as", "mkisofs",
|
||||
"-R", "-r", "-J",
|
||||
"-hfsplus",
|
||||
"-apm-block-size", "2048",
|
||||
"--efi-boot", "boot/limine/limine-uefi-cd.bin",
|
||||
"-efi-boot-part",
|
||||
"--efi-boot-image",
|
||||
"--protective-msdos-label",
|
||||
};
|
||||
},
|
||||
.x86_64 => block: {
|
||||
_ = rootfs.addCopyFile(limine.path("limine-bios.sys"), "boot/limine/limine-bios.sys");
|
||||
_ = rootfs.addCopyFile(limine.path("limine-bios-cd.bin"), "boot/limine/limine-bios-cd.bin");
|
||||
_ = rootfs.addCopyFile(limine.path("limine-uefi-cd.bin"), "boot/limine/limine-uefi-cd.bin");
|
||||
_ = rootfs.addCopyFile(limine.path("BOOTX64.EFI"), "EFI/BOOT/BOOTX64.EFI");
|
||||
_ = rootfs.addCopyFile(limine.path("BOOTIA32.EFI"), "EFI/BOOT/BOOTIA32.EFI");
|
||||
|
||||
break :block &.{
|
||||
"xorriso", "-as", "mkisofs",
|
||||
"-R", "-r", "-J",
|
||||
"-b", "boot/limine/limine-bios-cd.bin",
|
||||
"-no-emul-boot",
|
||||
"-boot-load-size", "4",
|
||||
"-boot-info-table",
|
||||
"-hfsplus",
|
||||
"-apm-block-size", "2048",
|
||||
"--efi-boot", "boot/limine/limine-uefi-cd.bin",
|
||||
"-efi-boot-part",
|
||||
"--efi-boot-image",
|
||||
"--protective-msdos-label",
|
||||
};
|
||||
},
|
||||
else => unreachable,
|
||||
});
|
||||
|
||||
run_cmd.step.dependOn(&rootfs.step);
|
||||
run_cmd.addDirectoryArg(rootfs.getDirectory());
|
||||
run_cmd.addArg("-o");
|
||||
// Retrieve the bootable image from command.
|
||||
const image = run_cmd.addOutputFileArg("bootable.iso");
|
||||
|
||||
// Install Limine stage 1 and 2 for legacy BIOS boot.
|
||||
// This is only required on x86_64, but we are deploying for all anyway.
|
||||
const deploy = b.addRunArtifact(exe);
|
||||
deploy.step.dependOn(&run_cmd.step);
|
||||
deploy.addArg("bios-install");
|
||||
deploy.addFileArg(image);
|
||||
|
||||
const step = b.step("image", "Generate a bootable ISO image");
|
||||
step.dependOn(&deploy.step);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
fn generateLinkerScript(b: *std.Build, arch: std.Target.Cpu.Arch) !std.Build.LazyPath {
|
||||
const path = "linker.ld";
|
||||
const data = try std.mem.join(b.allocator, "\n", &.{
|
||||
switch (arch) {
|
||||
.aarch64 => "OUTPUT_FORMAT(elf64-littleaarch64)",
|
||||
.loongarch64 => "OUTPUT_FORMAT(elf64-loongarch)",
|
||||
.riscv64 => "OUTPUT_FORMAT(elf64-littleriscv)",
|
||||
.x86_64 => "OUTPUT_FORMAT(elf64-x86-64)",
|
||||
else => unreachable,
|
||||
},
|
||||
\\ENTRY(_start)
|
||||
\\
|
||||
\\/* Define the program headers we want so the bootloader gives us the right */
|
||||
\\/* MMU permissions; this also allows us to exert more control over the linking */
|
||||
\\/* process. */
|
||||
\\PHDRS
|
||||
\\{
|
||||
\\ text PT_LOAD;
|
||||
\\ rodata PT_LOAD;
|
||||
\\ data PT_LOAD;
|
||||
\\}
|
||||
\\
|
||||
\\SECTIONS
|
||||
\\{
|
||||
\\ /* We want to be placed in the topmost 2GiB of the address space, for optimisations */
|
||||
\\ /* and because that is what the Limine spec mandates. */
|
||||
\\ /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */
|
||||
\\ /* that is the beginning of the region. */
|
||||
\\ . = 0xffffffff80000000;
|
||||
\\
|
||||
\\ .text : {
|
||||
\\ *(.text .text.*)
|
||||
\\ } :text
|
||||
\\
|
||||
\\ /* Move to the next memory page for .rodata */
|
||||
\\ . = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
\\
|
||||
\\ .rodata : {
|
||||
\\ *(.rodata .rodata.*)
|
||||
\\ } :rodata
|
||||
\\
|
||||
\\ /* Move to the next memory page for .data */
|
||||
\\ . = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
\\
|
||||
\\ .data : {
|
||||
\\ *(.data .data.*)
|
||||
\\
|
||||
\\ /* Place the sections that contain the Limine requests as part of the .data */
|
||||
\\ /* output section. */
|
||||
\\ KEEP(*(.requests_start_marker))
|
||||
\\ KEEP(*(.requests))
|
||||
\\ KEEP(*(.requests_end_marker))
|
||||
\\ } :data
|
||||
\\
|
||||
\\ /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */
|
||||
\\ /* unnecessary zeros will be written to the binary. */
|
||||
\\ /* If you need, for example, .init_array and .fini_array, those should be placed */
|
||||
\\ /* above this. */
|
||||
\\ .bss : {
|
||||
\\ *(.bss .bss.*)
|
||||
\\ *(COMMON)
|
||||
\\ } :data
|
||||
\\
|
||||
\\ /* Discard .note.* and .eh_frame* since they may cause issues on some hosts. */
|
||||
\\ /DISCARD/ : {
|
||||
\\ *(.eh_frame*)
|
||||
\\ *(.note .note.*)
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
const script = b.addWriteFile(path, data);
|
||||
b.getInstallStep().dependOn(&script.step);
|
||||
|
||||
return script.getDirectory().path(b, path);
|
||||
}
|
||||
|
||||
fn generateRunCommand(b: *std.Build, image: std.Build.LazyPath, arch: std.Target.Cpu.Arch) *std.Build.Step.Run {
|
||||
// Add edk2_ovmf to the dependency tree
|
||||
const edk2 = b.dependency("edk2", .{});
|
||||
|
||||
const fs = b.addWriteFiles();
|
||||
var code: std.Build.LazyPath = undefined;
|
||||
var vars: std.Build.LazyPath = undefined;
|
||||
|
||||
// This *creates* a Run step in the build graph, to be executed when another
|
||||
// step is evaluated that depends on it. The next line below will establish
|
||||
// such a dependency.
|
||||
const run_cmd: *std.Build.Step.Run = switch (arch) {
|
||||
.aarch64 => block: {
|
||||
code = fs.addCopyFile(edk2.path("bin/RELEASEAARCH64_QEMU_EFI.fd"), "code.fd");
|
||||
vars = fs.addCopyFile(edk2.path("bin/RELEASEAARCH64_QEMU_VARS.fd"), "vars.fd");
|
||||
|
||||
const dd1_cmd = b.addSystemCommand(&.{"dd", "if=/dev/zero", "bs=1", "count=0", "seek=67108864"});
|
||||
dd1_cmd.addPrefixedFileArg("of=", code);
|
||||
dd1_cmd.step.dependOn(&fs.step);
|
||||
|
||||
const dd2_cmd = b.addSystemCommand(&.{"dd", "if=/dev/zero", "bs=1", "count=0", "seek=67108864"});
|
||||
dd2_cmd.addPrefixedFileArg("of=", vars);
|
||||
dd2_cmd.step.dependOn(&fs.step);
|
||||
|
||||
const run_cmd = b.addSystemCommand(&.{
|
||||
"qemu-system-aarch64",
|
||||
"-M", "virt",
|
||||
"-cpu", "cortex-a72",
|
||||
"-device", "ramfb",
|
||||
"-device", "qemu-xhci",
|
||||
"-device", "usb-kbd",
|
||||
"-device", "usb-mouse",
|
||||
"-serial", "stdio",
|
||||
});
|
||||
run_cmd.step.dependOn(&dd1_cmd.step);
|
||||
run_cmd.step.dependOn(&dd2_cmd.step);
|
||||
|
||||
break :block run_cmd;
|
||||
},
|
||||
.loongarch64 => block: {
|
||||
code = fs.addCopyFile(edk2.path("bin/RELEASELOONGARCH64_QEMU_EFI.fd"), "code.fd");
|
||||
vars = fs.addCopyFile(edk2.path("bin/RELEASELOONGARCH64_QEMU_VARS.fd"), "vars.fd");
|
||||
|
||||
const dd1_cmd = b.addSystemCommand(&.{"dd", "if=/dev/zero", "bs=1", "count=0", "seek=5242880"});
|
||||
dd1_cmd.addPrefixedFileArg("of=", code);
|
||||
dd1_cmd.step.dependOn(&fs.step);
|
||||
|
||||
const dd2_cmd = b.addSystemCommand(&.{"dd", "if=/dev/zero", "bs=1", "count=0", "seek=5242880"});
|
||||
dd2_cmd.addPrefixedFileArg("of=", vars);
|
||||
dd2_cmd.step.dependOn(&fs.step);
|
||||
|
||||
const run_cmd = b.addSystemCommand(&.{
|
||||
"qemu-system-loongarch64",
|
||||
"-M", "virt",
|
||||
"-cpu", "la464",
|
||||
"-device", "ramfb",
|
||||
"-device", "qemu-xhci",
|
||||
"-device", "usb-kbd",
|
||||
"-device", "usb-mouse",
|
||||
"-serial", "stdio",
|
||||
});
|
||||
run_cmd.step.dependOn(&dd1_cmd.step);
|
||||
run_cmd.step.dependOn(&dd2_cmd.step);
|
||||
|
||||
break :block run_cmd;
|
||||
},
|
||||
.riscv64 => block: {
|
||||
code = fs.addCopyFile(edk2.path("bin/RELEASERISCV64_VIRT_CODE.fd"), "code.fd");
|
||||
vars = fs.addCopyFile(edk2.path("bin/RELEASERISCV64_VIRT_VARS.fd"), "vars.fd");
|
||||
|
||||
const dd1_cmd = b.addSystemCommand(&.{"dd", "if=/dev/zero", "bs=1", "count=0", "seek=33554432"});
|
||||
dd1_cmd.addPrefixedFileArg("of=", code);
|
||||
dd1_cmd.step.dependOn(&fs.step);
|
||||
|
||||
const dd2_cmd = b.addSystemCommand(&.{"dd", "if=/dev/zero", "bs=1", "count=0", "seek=33554432"});
|
||||
dd2_cmd.addPrefixedFileArg("of=", vars);
|
||||
dd2_cmd.step.dependOn(&fs.step);
|
||||
|
||||
const run_cmd = b.addSystemCommand(&.{
|
||||
"qemu-system-riscv64",
|
||||
"-M", "virt",
|
||||
"-cpu", "rv64",
|
||||
"-device", "ramfb",
|
||||
"-device", "qemu-xhci",
|
||||
"-device", "usb-kbd",
|
||||
"-device", "usb-mouse",
|
||||
"-serial", "stdio",
|
||||
});
|
||||
run_cmd.step.dependOn(&dd1_cmd.step);
|
||||
run_cmd.step.dependOn(&dd2_cmd.step);
|
||||
|
||||
break :block run_cmd;
|
||||
},
|
||||
.x86_64 => block: {
|
||||
code = edk2.path("bin/RELEASEX64_OVMF_CODE.fd");
|
||||
vars = edk2.path("bin/RELEASEX64_OVMF_VARS.fd");
|
||||
|
||||
break :block b.addSystemCommand(&.{
|
||||
"qemu-system-x86_64",
|
||||
"-M", "q35",
|
||||
"-device", "isa-debug-exit,iobase=0xf4,iosize=0x04",
|
||||
"-serial", "stdio",
|
||||
"-rtc", "clock=vm",
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
run_cmd.addArg("-drive");
|
||||
run_cmd.addPrefixedFileArg("if=pflash,unit=0,format=raw,readonly=on,file=", code);
|
||||
|
||||
run_cmd.addArg("-drive");
|
||||
run_cmd.addPrefixedFileArg("if=pflash,unit=1,format=raw,file=", vars);
|
||||
|
||||
run_cmd.addArg("-cdrom");
|
||||
run_cmd.addFileArg(image);
|
||||
|
||||
return run_cmd;
|
||||
}
|
||||
|
||||
fn resolveDependencies(b: *std.Build, kernel: *std.Build.Module) !void {
|
||||
const libk = block: {
|
||||
const file = b.path("lib/kernel/kernel.zig");
|
||||
const mod = b.addModule("kernel", .{
|
||||
.root_source_file = file,
|
||||
.optimize = kernel.optimize,
|
||||
.target = kernel.resolved_target
|
||||
});
|
||||
|
||||
break :block mod;
|
||||
};
|
||||
|
||||
const limine = block: {
|
||||
const file = b.path("lib/limine/limine.zig");
|
||||
const mod = b.createModule(.{ .root_source_file = file });
|
||||
|
||||
break :block mod;
|
||||
};
|
||||
|
||||
const uacpi = block: {
|
||||
const dependency = b.dependency("uacpi", .{});
|
||||
const file = b.path("lib/uacpi/uacpi.zig");
|
||||
const mod = b.createModule(.{ .root_source_file = file });
|
||||
|
||||
var flags: std.ArrayList([]const u8) = .init(b.allocator);
|
||||
for (b.debug_log_scopes) |scope| {
|
||||
if (std.mem.eql(u8, scope, "uacpi")) {
|
||||
try flags.append("-DUACPI_DEFAULT_LOG_LEVEL=UACPI_LOG_TRACE");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mod.addIncludePath(dependency.path("include"));
|
||||
mod.addCSourceFiles(.{
|
||||
.root = dependency.path("source"),
|
||||
.files = &.{
|
||||
"default_handlers.c",
|
||||
"event.c",
|
||||
"interpreter.c",
|
||||
"io.c",
|
||||
"mutex.c",
|
||||
"namespace.c",
|
||||
"notify.c",
|
||||
"opcodes.c",
|
||||
"opregion.c",
|
||||
"osi.c",
|
||||
"registers.c",
|
||||
"resources.c",
|
||||
"shareable.c",
|
||||
"sleep.c",
|
||||
"stdlib.c",
|
||||
"tables.c",
|
||||
"types.c",
|
||||
"uacpi.c",
|
||||
"utilities.c",
|
||||
},
|
||||
.flags = try flags.toOwnedSlice(),
|
||||
});
|
||||
|
||||
break :block mod;
|
||||
};
|
||||
|
||||
kernel.addImport("libk", libk);
|
||||
kernel.addImport("uacpi", uacpi);
|
||||
kernel.addImport("limine", limine);
|
||||
}
|
||||
61
build.zig.zon
Executable file
61
build.zig.zon
Executable file
@ -0,0 +1,61 @@
|
||||
.{
|
||||
// This is the default name used by packages depending on this one. For
|
||||
// example, when a user runs `zig fetch --save <url>`, this field is used
|
||||
// as the key in the `dependencies` table. Although the user can choose a
|
||||
// different name, most users will stick with this provided value.
|
||||
//
|
||||
// It is redundant to include "zig" in this name because it is already
|
||||
// within the Zig package namespace.
|
||||
.name = .kernel,
|
||||
|
||||
// This is a [Semantic Version](https://semver.org/).
|
||||
// In a future version of Zig it will be used for package deduplication.
|
||||
.version = "0.0.0",
|
||||
|
||||
// Together with name, this represents a globally unique package
|
||||
// identifier. This field is generated by the Zig toolchain when the
|
||||
// package is first created, and then *never changes*. This allows
|
||||
// unambiguous detection of one package being an updated version of
|
||||
// another.
|
||||
//
|
||||
// When forking a Zig project, this id should be regenerated (delete the
|
||||
// field and run `zig build`) if the upstream project is still maintained.
|
||||
// Otherwise, the fork is *hostile*, attempting to take control over the
|
||||
// original project's identity. Thus it is recommended to leave the comment
|
||||
// on the following line intact, so that it shows up in code reviews that
|
||||
// modify the field.
|
||||
.fingerprint = 0x5dd29aabeb5697e0,
|
||||
|
||||
// This field is optional.
|
||||
// This is currently advisory only; Zig does not yet do anything
|
||||
// with this value.
|
||||
.minimum_zig_version = "0.14.0",
|
||||
|
||||
// This field is optional.
|
||||
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
||||
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
||||
// Once all dependencies are fetched, `zig build` no longer requires
|
||||
// internet connectivity.
|
||||
.dependencies = .{
|
||||
.edk2 = .{
|
||||
.url = "https://github.com/retrage/edk2-nightly/archive/9eaa0a48ce51a4bd98941c6cf6d3dec0ffebc2a9.tar.gz",
|
||||
.hash = "1220c7bb96f5e6d0b1f54e26fa5b053e41ef570184f55b81e9fda795fe5a8463083c",
|
||||
},
|
||||
.uacpi = .{
|
||||
.url = "https://github.com/uACPI/uACPI/archive/refs/tags/1.0.1.tar.gz",
|
||||
.hash = "12200468fb671c04225f04d87e0fe1131e49ccd153e870be6e858dc80e39cb4e18df",
|
||||
},
|
||||
.limine = .{
|
||||
.url = "https://github.com/limine-bootloader/limine/archive/refs/heads/v8.x-binary.tar.gz",
|
||||
.hash = "1220412bffeb555ae2aa1a6b18b204adf3cabbcf6c94505d513d851d9884c3a4a7e0",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
// For example...
|
||||
//"LICENSE",
|
||||
//"README.md",
|
||||
},
|
||||
}
|
||||
59
lib/kernel/io/IoMap.zig
Normal file
59
lib/kernel/io/IoMap.zig
Normal file
@ -0,0 +1,59 @@
|
||||
pub fn IoMap(comptime T: type) type {
|
||||
return struct {
|
||||
pub const Permission = enum {
|
||||
readonly,
|
||||
writeonly,
|
||||
all,
|
||||
};
|
||||
|
||||
pub const Error = error {
|
||||
NotAvailable,
|
||||
};
|
||||
|
||||
pub const Pin = struct {
|
||||
kind: type = T,
|
||||
perm: Permission,
|
||||
base: usize,
|
||||
};
|
||||
|
||||
pub fn Schematics(comptime U: type) type {
|
||||
return struct {
|
||||
impl: T,
|
||||
init: bool = false,
|
||||
|
||||
pub fn open(self: *@This(), address: anytype) void {
|
||||
self.impl = T.open(address);
|
||||
self.init = true;
|
||||
}
|
||||
|
||||
pub fn read(self: *@This(), comptime name: []const u8) Error!@field(U{}, name).kind {
|
||||
if (@field(U{}, name).perm == .writeonly) {
|
||||
@compileError("attempted to perform a read operation on an unreadable field '" ++ name ++ "'");
|
||||
}
|
||||
if (self.init == false) {
|
||||
return error.NotAvailable;
|
||||
}
|
||||
const offset = @field(U{}, name).base;
|
||||
const address = self.impl.address + offset;
|
||||
|
||||
var io = T.open(@intCast(address));
|
||||
return io.read();
|
||||
}
|
||||
|
||||
pub fn write(self: *@This(), comptime name: []const u8, value: @field(U{}, name).kind) Error!void {
|
||||
if (@field(U{}, name).perm == .readonly) {
|
||||
@compileError("attempted to peform a write operation on an unwritable field '" ++ name ++ "'");
|
||||
}
|
||||
if (self.init == false) {
|
||||
return error.NotAvailable;
|
||||
}
|
||||
const offset = @field(U{}, name).base;
|
||||
const address = self.impl.address + offset;
|
||||
|
||||
var io = T.open(@intCast(address));
|
||||
io.write(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
47
lib/kernel/io/io.zig
Normal file
47
lib/kernel/io/io.zig
Normal file
@ -0,0 +1,47 @@
|
||||
pub const IoMap = @import("IoMap.zig").IoMap;
|
||||
|
||||
pub fn Mmio(comptime T: type) type {
|
||||
return struct {
|
||||
address: usize,
|
||||
|
||||
pub fn open(address: usize) @This() {
|
||||
return .{ .address = address };
|
||||
}
|
||||
|
||||
pub fn read(self: *@This()) T {
|
||||
return @as(*T, @ptrFromInt(self.address)).*;
|
||||
}
|
||||
|
||||
pub fn write(self: *@This(), data: T) void {
|
||||
@as(*T, @ptrFromInt(self.address)).* = data;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn ReadOnly(comptime T: type) type {
|
||||
return struct {
|
||||
inner: T,
|
||||
|
||||
pub inline fn open(address: anytype) @This() {
|
||||
return .{ .inner = .open(address) };
|
||||
}
|
||||
|
||||
pub inline fn read(self: *@This()) void {
|
||||
return self.inner.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn WriteOnly(comptime T: type) type {
|
||||
return struct {
|
||||
inner: T,
|
||||
|
||||
pub inline fn open(address: anytype) @This() {
|
||||
return .{ .inner = .open(address) };
|
||||
}
|
||||
|
||||
pub inline fn write(self: *@This(), value: anytype) void {
|
||||
self.inner.write(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
5
lib/kernel/kernel.zig
Normal file
5
lib/kernel/kernel.zig
Normal file
@ -0,0 +1,5 @@
|
||||
pub const io = @import("io/io.zig");
|
||||
pub const utils = @import("utils/utils.zig");
|
||||
pub const object = @import("object.zig");
|
||||
pub const machine = @import("machine/machine.zig");
|
||||
pub const syscall = @import("syscall.zig");
|
||||
1
lib/kernel/machine/machine.zig
Normal file
1
lib/kernel/machine/machine.zig
Normal file
@ -0,0 +1 @@
|
||||
pub const serial = @import("serial/serial.zig");
|
||||
104
lib/kernel/machine/serial/16550.zig
Executable file
104
lib/kernel/machine/serial/16550.zig
Executable file
@ -0,0 +1,104 @@
|
||||
const std = @import("std");
|
||||
const libk = @import("../../kernel.zig");
|
||||
|
||||
const IoMap = libk.io.IoMap;
|
||||
const BitFields = libk.utils.BitFields;
|
||||
|
||||
const IntEnFlags = BitFields(packed struct(u4) {
|
||||
received : bool = false,
|
||||
sent : bool = false,
|
||||
errored : bool = false,
|
||||
status_change : bool = false,
|
||||
});
|
||||
|
||||
const LineStsFlags = BitFields(packed struct(u8) {
|
||||
input_full : bool = false,
|
||||
padding_0 : u4 = 0,
|
||||
output_empty : bool = false,
|
||||
padding_1 : u2 = 0,
|
||||
});
|
||||
|
||||
pub fn SerialPort(comptime Io: type) type {
|
||||
return struct {
|
||||
io: IoMap(Io).Schematics(struct {
|
||||
/// Data port
|
||||
data : IoMap(u8).Pin = .{ .perm = .all, .base = 0 },
|
||||
/// Interrupt enable port
|
||||
int_en : IoMap(u8).Pin = .{ .perm = .writeonly, .base = 1 },
|
||||
/// Fifo control port
|
||||
fifo_ctrl : IoMap(u8).Pin = .{ .perm = .writeonly, .base = 2 },
|
||||
/// Line control port
|
||||
line_ctrl : IoMap(u8).Pin = .{ .perm = .writeonly, .base = 3 },
|
||||
/// Modem control port
|
||||
modem_ctrl : IoMap(u8).Pin = .{ .perm = .writeonly, .base = 4 },
|
||||
/// Line status port
|
||||
line_sts : IoMap(u8).Pin = .{ .perm = .readonly, .base = 5 },
|
||||
}),
|
||||
|
||||
pub fn open(self: *@This(), address: anytype) void {
|
||||
self.io.open(address);
|
||||
}
|
||||
|
||||
pub fn init(self: *@This()) !void {
|
||||
// Disable interrupts
|
||||
try self.io.write("int_en", IntEnFlags.init(.{}).bits());
|
||||
|
||||
// Enable DLAB
|
||||
try self.io.write("line_ctrl", 0x80);
|
||||
|
||||
// Set maximum speed to 38400 bps by configuring DLL and DLM
|
||||
try self.io.write("data", 0x03);
|
||||
try self.io.write("int_en", IntEnFlags.init(.{}).bits());
|
||||
|
||||
// Disable DLAB and set data word length to 8 bits
|
||||
try self.io.write("line_ctrl", 0x03);
|
||||
|
||||
// Enable FIFO, clear TX/RX queues and
|
||||
// set interrupt watermark at 14 bytes
|
||||
try self.io.write("fifo_ctrl", 0xC7);
|
||||
|
||||
// Mark data terminal ready, signal request to send
|
||||
// and enable auxilliary output #2 (used as interrupt line for CPU)
|
||||
try self.io.write("modem_ctrl", 0x0B);
|
||||
|
||||
// Enable interrupts
|
||||
try self.io.write("int_en", IntEnFlags.init(.{ .received = true }).bits());
|
||||
}
|
||||
|
||||
pub fn write(self: *@This(), data: []const u8) void {
|
||||
for (data) |byte| {
|
||||
switch (byte) {
|
||||
8, 0x7f => {
|
||||
self.send( 8 ) catch return;
|
||||
self.send(' ') catch return;
|
||||
self.send( 8 ) catch return;
|
||||
},
|
||||
else => {
|
||||
self.send(byte) catch return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(self: *@This(), data: u8) !void {
|
||||
while (!(try self.lineSts()).fields.output_empty) {
|
||||
std.atomic.spinLoopHint();
|
||||
}
|
||||
try self.io.write("data", data);
|
||||
}
|
||||
|
||||
pub fn receive(self: *@This()) !u8 {
|
||||
while (!(try self.lineSts()).fields.input_full) {
|
||||
std.atomic.spinLoopHint();
|
||||
}
|
||||
return try self.io.read("data");
|
||||
}
|
||||
|
||||
inline fn lineSts(self: *@This()) !LineStsFlags {
|
||||
const data = try self.io.read("line_sts");
|
||||
const result = LineStsFlags.fromBitsTruncate(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
168
lib/kernel/machine/serial/pl011.zig
Executable file
168
lib/kernel/machine/serial/pl011.zig
Executable file
@ -0,0 +1,168 @@
|
||||
const std = @import("std");
|
||||
const libk = @import("../../kernel.zig");
|
||||
|
||||
const Mmio = libk.io.Mmio;
|
||||
const IoMap = libk.io.IoMap;
|
||||
const BitFields = libk.utils.BitFields;
|
||||
|
||||
const Flags = BitFields(packed struct(u9) {
|
||||
/// Clear to send
|
||||
cts : bool = false,
|
||||
/// Data set ready
|
||||
dsr : bool = false,
|
||||
/// UART busy transmitting data
|
||||
dcd : bool = false,
|
||||
/// Receive FIFO is empty
|
||||
busy : bool = false,
|
||||
/// Transmit FIFO is empty
|
||||
rxfe : bool = false,
|
||||
/// Trasnmit FIFO is full
|
||||
txff : bool = false,
|
||||
/// Receive FIFO is full
|
||||
rxff : bool = false,
|
||||
/// Transmit FIFO is empty
|
||||
txfe : bool = false,
|
||||
/// Ring indicator
|
||||
ri : bool = false,
|
||||
});
|
||||
|
||||
const ReceiveStatus = BitFields(packed struct(u4) {
|
||||
/// Framing error
|
||||
fe: bool = false,
|
||||
/// Parity error
|
||||
pe: bool = false,
|
||||
/// Break error
|
||||
be: bool = false,
|
||||
/// Overrun error
|
||||
oe: bool = false,
|
||||
});
|
||||
|
||||
const Control = BitFields(packed struct(u16) {
|
||||
/// UART Enable
|
||||
uarten : bool = false,
|
||||
/// Serial InfraRed (SIR) Enable
|
||||
siren : bool = false,
|
||||
/// Serial InfraRed (SIR) Low-power
|
||||
sirlp : bool = false,
|
||||
/// Loopback Enable
|
||||
lbe : bool = false,
|
||||
/// Bits 6:3 reserved
|
||||
reserved : u4 = 0,
|
||||
/// Transmit Enable
|
||||
txe : bool = false,
|
||||
/// Receive Enable
|
||||
rxe : bool = false,
|
||||
/// Data Trasmit Ready
|
||||
dtr : bool = false,
|
||||
/// Request to Send
|
||||
rts : bool = false,
|
||||
/// Complement of nUARTOut1
|
||||
out_1 : bool = false,
|
||||
/// Complement of nUARTOut2
|
||||
out_2 : bool = false,
|
||||
/// Request To Send (RTS) Hardware Flow Control Enable
|
||||
rtsen : bool = false,
|
||||
/// Clear To Send (CTS) Hardware Flow Control Enable
|
||||
ctsen : bool = false,
|
||||
});
|
||||
|
||||
pub const SerialPort = struct {
|
||||
io: IoMap(Mmio(u32)).Schematics(struct {
|
||||
/// Data Register
|
||||
uartdr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x00 },
|
||||
/// Receive status / error clear
|
||||
uartrsr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x04 },
|
||||
/// Flag register
|
||||
uartfr : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x18 },
|
||||
/// IrDA Low power counter register
|
||||
uartilpr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x20 },
|
||||
/// Integer baud rate
|
||||
uartibrd : IoMap(u32).Pin = .{ .perm = .all, .base = 0x24 },
|
||||
/// Fractional baud rate
|
||||
uartfbrd : IoMap(u32).Pin = .{ .perm = .all, .base = 0x28 },
|
||||
/// Line control
|
||||
uartlcr_h : IoMap(u32).Pin = .{ .perm = .all, .base = 0x2C },
|
||||
/// Control
|
||||
uartcr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x30 },
|
||||
/// Interrupt fifo level select
|
||||
uartifls : IoMap(u32).Pin = .{ .perm = .all, .base = 0x34 },
|
||||
/// Interrupt mask set/clear
|
||||
uartimsc : IoMap(u32).Pin = .{ .perm = .all, .base = 0x38 },
|
||||
/// Raw interrupt status
|
||||
uartris : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x3C },
|
||||
/// Masked interrupt status
|
||||
uartmis : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x40 },
|
||||
/// Interrupt clear
|
||||
uarticr : IoMap(u32).Pin = .{ .perm = .writeonly, .base = 0x44 },
|
||||
/// Dma control
|
||||
uartdmacr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x48 },
|
||||
}),
|
||||
|
||||
pub fn open(self: *@This(), address: usize) void {
|
||||
self.io.open(address);
|
||||
}
|
||||
|
||||
pub fn init(self: *@This(), clock: u32, baud_rate: u32) !void {
|
||||
const bits = try self.io.read("uartcr");
|
||||
var ctrl = Control.fromBitsTruncate(bits);
|
||||
|
||||
// Disable UART
|
||||
ctrl.fields.uarten = false;
|
||||
try self.io.write("uartcr", ctrl.bits());
|
||||
|
||||
// Program integer baud rate
|
||||
const divisor = (clock << 2) / baud_rate;
|
||||
try self.io.write("uartibrd", divisor >> 6);
|
||||
|
||||
// Program fractional baud rate
|
||||
try self.io.write("uartfbrd", divisor & 0x3F);
|
||||
|
||||
// Clear errors
|
||||
const status = ReceiveStatus.init(.{});
|
||||
try self.io.write("uartrsr", status.bits());
|
||||
|
||||
// Enable UART
|
||||
const data = Control.init(.{
|
||||
.rxe = true,
|
||||
.txe = true,
|
||||
.uarten = true,
|
||||
});
|
||||
try self.io.write("uartcr", data.bits());
|
||||
}
|
||||
|
||||
pub fn write(self: *@This(), data: []const u8) void {
|
||||
for (data) |byte| {
|
||||
switch (byte) {
|
||||
8, 0x7f => {
|
||||
self.send( 8 ) catch return;
|
||||
self.send(' ') catch return;
|
||||
self.send( 8 ) catch return;
|
||||
},
|
||||
else => {
|
||||
self.send(byte) catch return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(self: *@This(), data: u32) !void {
|
||||
while ((try self.flags()).fields.txff) {
|
||||
std.atomic.spinLoopHint();
|
||||
}
|
||||
try self.io.write("uartdr", data);
|
||||
}
|
||||
|
||||
pub fn receive(self: *@This()) !u32 {
|
||||
while ((try self.flags()).fields.rxff) {
|
||||
std.atomic.spinLoopHint();
|
||||
}
|
||||
return try self.io.read("uartdr");
|
||||
}
|
||||
|
||||
inline fn flags(self: *@This()) !Flags {
|
||||
const data = try self.io.read("uartfr");
|
||||
const result = Flags.fromBitsTruncate(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
2
lib/kernel/machine/serial/serial.zig
Normal file
2
lib/kernel/machine/serial/serial.zig
Normal file
@ -0,0 +1,2 @@
|
||||
pub const uart_16550 = @import("16550.zig");
|
||||
pub const uart_pl011 = @import("pl011.zig");
|
||||
11
lib/kernel/object.zig
Normal file
11
lib/kernel/object.zig
Normal file
@ -0,0 +1,11 @@
|
||||
pub const ObjectType = enum {
|
||||
untyped,
|
||||
endpoint,
|
||||
notification,
|
||||
cnode,
|
||||
tcb,
|
||||
schedctx,
|
||||
reply,
|
||||
interrupt,
|
||||
vspace,
|
||||
};
|
||||
0
lib/kernel/syscall.zig
Normal file
0
lib/kernel/syscall.zig
Normal file
30
lib/kernel/utils/BitFields.zig
Normal file
30
lib/kernel/utils/BitFields.zig
Normal file
@ -0,0 +1,30 @@
|
||||
pub fn BitFields(comptime T: type) type {
|
||||
return packed struct {
|
||||
pub const Self = T;
|
||||
pub const Bits =
|
||||
if (@typeInfo(T).@"struct".backing_integer) |U| U
|
||||
else @compileError("T must be a packed struct");
|
||||
|
||||
fields: Self,
|
||||
|
||||
pub inline fn init(x: Self) @This() {
|
||||
return .{ .fields = x };
|
||||
}
|
||||
|
||||
pub inline fn fromBits(value: Bits) @This() {
|
||||
return .init(@bitCast(value));
|
||||
}
|
||||
|
||||
pub inline fn fromBitsTruncate(value: anytype) @This() {
|
||||
return fromBits(@truncate(value));
|
||||
}
|
||||
|
||||
pub inline fn bits(self: @This()) Bits {
|
||||
return @bitCast(self);
|
||||
}
|
||||
|
||||
pub inline fn cast(self: @This(), comptime Int: type) Int {
|
||||
return @intCast(bits(self));
|
||||
}
|
||||
};
|
||||
}
|
||||
12
lib/kernel/utils/utils.zig
Normal file
12
lib/kernel/utils/utils.zig
Normal file
@ -0,0 +1,12 @@
|
||||
pub const BitFields = @import("BitFields.zig").BitFields;
|
||||
|
||||
pub inline fn alignDown(address: usize, alignment: usize) usize {
|
||||
return address & ~(alignment - 1);
|
||||
}
|
||||
|
||||
pub inline fn alignUp(address: usize, alignment: usize) usize {
|
||||
const mask = alignment - 1;
|
||||
if (address & mask == 0) return address;
|
||||
|
||||
return (address | mask) + 1;
|
||||
}
|
||||
10
lib/limine/limine.conf
Executable file
10
lib/limine/limine.conf
Executable file
@ -0,0 +1,10 @@
|
||||
# Timeout in seconds that Limine will use before automatically booting.
|
||||
timeout: 0
|
||||
|
||||
# The entry name that will be displayed in the boot menu.
|
||||
/Tangerine
|
||||
# We use the Limine boot protocol.
|
||||
protocol: limine
|
||||
|
||||
# Path to the kernel to boot. boot():/ represents the partition on which limine.conf is located.
|
||||
kernel_path: boot():/boot/kernel
|
||||
281
lib/limine/limine.zig
Executable file
281
lib/limine/limine.zig
Executable file
@ -0,0 +1,281 @@
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn magic(a: u64, b: u64) [4]u64 {
|
||||
return .{ 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, a, b };
|
||||
}
|
||||
|
||||
pub const BaseRevision = extern struct {
|
||||
id: [2]u64 = .{ 0xf9562b2d5c95a6c8, 0x6a7b384944536bdc },
|
||||
revision: u64,
|
||||
|
||||
pub fn is_supported(self: *const volatile @This()) bool {
|
||||
return self.revision == 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub const BootloaderInfo = struct {
|
||||
pub const Request = extern struct {
|
||||
id: [4]u64 = magic(0xf55038d8e2a1202f, 0x279426fcf5f59740),
|
||||
revision: u64 = 0,
|
||||
response: ?*Response = null,
|
||||
};
|
||||
|
||||
pub const Response = extern struct {
|
||||
revision: u64,
|
||||
name: [*:0]u8,
|
||||
version: [*:0]u8,
|
||||
};
|
||||
};
|
||||
|
||||
pub const BootTime = struct {
|
||||
pub const Request = extern struct {
|
||||
id: [4]u64 = magic(0x502746e184c088aa, 0xfbc5ec83e6327893),
|
||||
revision: u64 = 0,
|
||||
response: ?*Response = null,
|
||||
};
|
||||
|
||||
pub const Response = extern struct {
|
||||
revision: u64,
|
||||
boot_time: i64,
|
||||
};
|
||||
};
|
||||
|
||||
pub const File = extern struct {
|
||||
revision: u64,
|
||||
address: [*]u8,
|
||||
size: u64,
|
||||
path: [*:0]u8,
|
||||
cmdline: [*:0]u8,
|
||||
media_type: MediaType,
|
||||
unused: u32,
|
||||
tftp_ip: u32,
|
||||
tftp_port: u32,
|
||||
partition_index: u32,
|
||||
mbr_disk_id: u32,
|
||||
gpt_disk_uuid: Uuid,
|
||||
gpt_part_uuid: Uuid,
|
||||
part_uuid: Uuid,
|
||||
|
||||
pub const Uuid = extern struct {
|
||||
a: u32,
|
||||
b: u16,
|
||||
c: u16,
|
||||
d: [8]u8,
|
||||
};
|
||||
|
||||
pub const MediaType = enum(u32) {
|
||||
generic = 0,
|
||||
optical = 1,
|
||||
tftp = 2,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Framebuffer = extern struct {
|
||||
address: [*]u8,
|
||||
width: u64,
|
||||
height: u64,
|
||||
pitch: u64,
|
||||
bpp: u16,
|
||||
memory_model: MemoryModel,
|
||||
red_mask_size: u8,
|
||||
red_mask_shift: u8,
|
||||
green_mask_size: u8,
|
||||
green_mask_shift: u8,
|
||||
blue_mask_size: u8,
|
||||
blue_mask_shift: u8,
|
||||
unused: [7]u8,
|
||||
edid_size: u64,
|
||||
edid: ?[*]u8,
|
||||
|
||||
// Response revision 1
|
||||
mode_count: u64,
|
||||
modes: [*]*VideoMode,
|
||||
|
||||
pub const MemoryModel = enum(u8) {
|
||||
rgb = 1,
|
||||
};
|
||||
|
||||
pub const VideoMode = extern struct {
|
||||
pitch: u64,
|
||||
width: u64,
|
||||
height: u64,
|
||||
bpp: u16,
|
||||
memory_model: MemoryModel,
|
||||
red_mask_size: u8,
|
||||
red_mask_shift: u8,
|
||||
green_mask_size: u8,
|
||||
green_mask_shift: u8,
|
||||
blue_mask_size: u8,
|
||||
blue_mask_shift: u8,
|
||||
};
|
||||
|
||||
pub const Request = extern struct {
|
||||
id: [4]u64 = magic(0x9d5827dcd881dd75, 0xa3148604f6fab11b),
|
||||
revision: u64 = 1,
|
||||
response: ?*Response = null
|
||||
};
|
||||
|
||||
pub const Response = extern struct {
|
||||
revision: u64,
|
||||
framebuffer_count: u64,
|
||||
framebuffers: [*]*Framebuffer,
|
||||
|
||||
pub inline fn getFramebuffers(self: *@This()) []*Framebuffer {
|
||||
return self.framebuffers[0..self.framebuffer_count];
|
||||
}
|
||||
};
|
||||
|
||||
pub inline fn getEdid(self: *@This()) ?[]u8 {
|
||||
if (self.edid) |edid| {
|
||||
return edid[0..self.edid_size];
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn getModes(self: *@This()) []*VideoMode {
|
||||
return self.modes[0..self.mode_count];
|
||||
}
|
||||
};
|
||||
|
||||
pub const Hhdm = struct {
|
||||
pub const Request = extern struct {
|
||||
id: [4]u64 = magic(0x48dcf1cb8ad2b852, 0x63984e959a98244b),
|
||||
revision: u64 = 0,
|
||||
response: ?*Response = null,
|
||||
};
|
||||
|
||||
pub const Response = extern struct {
|
||||
revision: u64,
|
||||
offset: u64,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Rsdp = struct {
|
||||
pub const Request = extern struct {
|
||||
id: [4]u64 = magic(0xc5e77b6b397e7b43, 0x27637845accdcf3c),
|
||||
revision: u64 = 0,
|
||||
response: ?*Response = null,
|
||||
};
|
||||
|
||||
pub const Response = extern struct {
|
||||
revision: u64,
|
||||
address: u64,
|
||||
};
|
||||
};
|
||||
|
||||
pub const MemoryMap = struct {
|
||||
pub const Request = extern struct {
|
||||
id: [4]u64 = magic(0x67cf3d9d378a806f, 0xe304acdfc50c3c62),
|
||||
revision: u64 = 0,
|
||||
response: ?*Response = null,
|
||||
};
|
||||
|
||||
pub const Response = extern struct {
|
||||
revision: u64,
|
||||
entry_count: u64,
|
||||
entries: [*]*Entry,
|
||||
|
||||
pub inline fn getEntries(self: *@This()) []*Entry {
|
||||
return self.entries[0..self.entry_count];
|
||||
}
|
||||
};
|
||||
|
||||
pub const Entry = extern struct {
|
||||
base: u64,
|
||||
length: u64,
|
||||
type: EntryType,
|
||||
};
|
||||
|
||||
pub const EntryType = enum(u64) {
|
||||
usable = 0,
|
||||
reserved = 1,
|
||||
acpi_reclaimable = 2,
|
||||
acpi_nvs = 3,
|
||||
bad_memory = 4,
|
||||
bootloader_reclaimable = 5,
|
||||
kernel_and_modules = 6,
|
||||
framebuffer = 7,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Paging = struct {
|
||||
pub const Mode = switch (builtin.cpu.arch) {
|
||||
.aarch64, .x86_64 => enum(u64) {
|
||||
four_level,
|
||||
five_level,
|
||||
|
||||
pub inline fn default() Mode {
|
||||
return Mode.four_level;
|
||||
}
|
||||
|
||||
pub inline fn max() Mode {
|
||||
return Mode.five_level;
|
||||
}
|
||||
|
||||
pub inline fn min() Mode {
|
||||
return Mode.four_level;
|
||||
}
|
||||
},
|
||||
.loongarch64 => enum(u64) {
|
||||
four_level,
|
||||
|
||||
pub inline fn default() Mode {
|
||||
return Mode.four_level;
|
||||
}
|
||||
|
||||
pub inline fn max() Mode {
|
||||
return Mode.four_level;
|
||||
}
|
||||
|
||||
pub inline fn min() Mode {
|
||||
return Mode.four_level;
|
||||
}
|
||||
},
|
||||
.riscv64 => enum(u64) {
|
||||
sv39,
|
||||
sv48,
|
||||
sv57,
|
||||
|
||||
pub inline fn default() Mode {
|
||||
return Mode.sv48;
|
||||
}
|
||||
|
||||
pub inline fn max() Mode {
|
||||
return Mode.sv57;
|
||||
}
|
||||
|
||||
pub inline fn min() Mode {
|
||||
return Mode.sv39;
|
||||
}
|
||||
},
|
||||
else => |x| @compileError("Unsupported architecture: " ++ @tagName(x)),
|
||||
};
|
||||
|
||||
pub const Request = extern struct {
|
||||
id: [4]u64 = magic(0x95c1a0edab0944cb, 0xa4e5cb3842f7488a),
|
||||
revision: u64 = 0,
|
||||
response: ?*Response = null,
|
||||
mode: Mode = .default(),
|
||||
|
||||
// Revision 1+
|
||||
max_mode: Mode = .max(),
|
||||
min_mode: Mode = .min(),
|
||||
};
|
||||
|
||||
pub const Response = extern struct {
|
||||
revision: u64,
|
||||
mode: Mode
|
||||
};
|
||||
};
|
||||
|
||||
pub const DeviceTreeBlob = struct {
|
||||
pub const Request = extern struct {
|
||||
id: [4]u64 = magic(0xb40ddb48fb54bac7, 0x545081493f81ffb7),
|
||||
revision: u64 = 0,
|
||||
response: ?*Response = null,
|
||||
};
|
||||
|
||||
pub const Response = extern struct {
|
||||
revision: u64,
|
||||
pointer: ?*anyopaque,
|
||||
};
|
||||
};
|
||||
75
lib/uacpi/uacpi.zig
Normal file
75
lib/uacpi/uacpi.zig
Normal file
@ -0,0 +1,75 @@
|
||||
const C = @cImport({
|
||||
@cInclude("uacpi/event.h");
|
||||
@cInclude("uacpi/io.h");
|
||||
@cInclude("uacpi/namespace.h");
|
||||
@cInclude("uacpi/notify.h");
|
||||
@cInclude("uacpi/opregion.h");
|
||||
@cInclude("uacpi/osi.h");
|
||||
@cInclude("uacpi/resources.h");
|
||||
@cInclude("uacpi/sleep.h");
|
||||
@cInclude("uacpi/status.h");
|
||||
@cInclude("uacpi/tables.h");
|
||||
@cInclude("uacpi/types.h");
|
||||
@cInclude("uacpi/uacpi.h");
|
||||
@cInclude("uacpi/utilities.h");
|
||||
});
|
||||
|
||||
pub const Status = enum(C.uacpi_status) {
|
||||
ok = C.UACPI_STATUS_OK,
|
||||
mapping_failed = C.UACPI_STATUS_MAPPING_FAILED,
|
||||
out_of_memory = C.UACPI_STATUS_OUT_OF_MEMORY,
|
||||
bad_checksum = C.UACPI_STATUS_BAD_CHECKSUM,
|
||||
invalid_signature = C.UACPI_STATUS_INVALID_SIGNATURE,
|
||||
invalid_table_length = C.UACPI_STATUS_INVALID_TABLE_LENGTH,
|
||||
not_found = C.UACPI_STATUS_NOT_FOUND,
|
||||
invalid_argument = C.UACPI_STATUS_INVALID_ARGUMENT,
|
||||
unimplemented = C.UACPI_STATUS_UNIMPLEMENTED,
|
||||
already_exists = C.UACPI_STATUS_ALREADY_EXISTS,
|
||||
internal_error = C.UACPI_STATUS_INTERNAL_ERROR,
|
||||
type_mismatch = C.UACPI_STATUS_TYPE_MISMATCH,
|
||||
init_level_mismatch = C.UACPI_STATUS_INIT_LEVEL_MISMATCH,
|
||||
namespace_node_dangling = C.UACPI_STATUS_NAMESPACE_NODE_DANGLING,
|
||||
no_handler = C.UACPI_STATUS_NO_HANDLER,
|
||||
no_resource_end_tag = C.UACPI_STATUS_NO_RESOURCE_END_TAG,
|
||||
compiled_out = C.UACPI_STATUS_COMPILED_OUT,
|
||||
hardware_timeout = C.UACPI_STATUS_HARDWARE_TIMEOUT,
|
||||
timeout = C.UACPI_STATUS_TIMEOUT,
|
||||
overridden = C.UACPI_STATUS_OVERRIDDEN,
|
||||
denied = C.UACPI_STATUS_DENIED,
|
||||
|
||||
// All errors that have bytecode-related origin should go here
|
||||
aml_undefined_reference = C.UACPI_STATUS_AML_UNDEFINED_REFERENCE,
|
||||
aml_invalid_namestring = C.UACPI_STATUS_AML_INVALID_NAMESTRING,
|
||||
aml_object_already_exists = C.UACPI_STATUS_AML_OBJECT_ALREADY_EXISTS,
|
||||
aml_invalid_opcode = C.UACPI_STATUS_AML_INVALID_OPCODE,
|
||||
aml_incompatible_object_type = C.UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE,
|
||||
aml_bad_encoding = C.UACPI_STATUS_AML_BAD_ENCODING,
|
||||
aml_out_of_bounds_index = C.UACPI_STATUS_AML_OUT_OF_BOUNDS_INDEX,
|
||||
aml_sync_level_too_high = C.UACPI_STATUS_AML_SYNC_LEVEL_TOO_HIGH,
|
||||
aml_invalid_resource = C.UACPI_STATUS_AML_INVALID_RESOURCE,
|
||||
aml_loop_timeout = C.UACPI_STATUS_AML_LOOP_TIMEOUT,
|
||||
aml_call_stack_depth_limit = C.UACPI_STATUS_AML_CALL_STACK_DEPTH_LIMIT,
|
||||
};
|
||||
|
||||
pub const InterruptResult = enum(C.uacpi_interrupt_ret) {
|
||||
not_handled = C.UACPI_INTERRUPT_NOT_HANDLED,
|
||||
handled = C.UACPI_INTERRUPT_HANDLED,
|
||||
};
|
||||
|
||||
pub const LogLevel = enum(C.uacpi_log_level) {
|
||||
debug = C.UACPI_LOG_DEBUG,
|
||||
err = C.UACPI_LOG_ERROR,
|
||||
info = C.UACPI_LOG_INFO,
|
||||
trace = C.UACPI_LOG_TRACE,
|
||||
warn = C.UACPI_LOG_WARN,
|
||||
};
|
||||
|
||||
pub const InterruptHandler = *const fn(Handle) callconv(.C) InterruptResult;
|
||||
|
||||
pub const Handle = C.uacpi_handle;
|
||||
pub const CpuFlags = C.uacpi_cpu_flags;
|
||||
pub const PhysAddr = C.uacpi_phys_addr;
|
||||
pub const ThreadId = C.uacpi_thread_id;
|
||||
pub const WorkType = C.uacpi_work_type;
|
||||
pub const PciAddress = C.uacpi_pci_address;
|
||||
pub const WorkHandler = C.uacpi_work_handler;
|
||||
6
src/arch/aarch64/aarch64.zig
Executable file
6
src/arch/aarch64/aarch64.zig
Executable file
@ -0,0 +1,6 @@
|
||||
pub const debug = @import("debug.zig");
|
||||
pub const machine = @import("machine/machine.zig");
|
||||
|
||||
pub inline fn halt() void {
|
||||
asm volatile ("wfi");
|
||||
}
|
||||
16
src/arch/aarch64/debug.zig
Normal file
16
src/arch/aarch64/debug.zig
Normal file
@ -0,0 +1,16 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root");
|
||||
|
||||
pub const Error = error {};
|
||||
pub const Writer = std.io.GenericWriter(void, Error, write);
|
||||
|
||||
pub fn writer() Writer {
|
||||
return .{ .context = {} };
|
||||
}
|
||||
|
||||
fn write(_: void, data: []const u8) Error!usize {
|
||||
root.arch.machine.serial.COM1.write(data);
|
||||
root.machine.Console.Instance.write(data);
|
||||
|
||||
return data.len;
|
||||
}
|
||||
17
src/arch/aarch64/machine/machine.zig
Normal file
17
src/arch/aarch64/machine/machine.zig
Normal file
@ -0,0 +1,17 @@
|
||||
const root = @import("root");
|
||||
const limine = @import("limine");
|
||||
|
||||
pub const serial = @import("serial.zig");
|
||||
|
||||
export var device_tree_blob_request: limine.DeviceTreeBlob.Request = .{};
|
||||
|
||||
pub fn init() void {
|
||||
serial.init();
|
||||
|
||||
if (device_tree_blob_request.response) |res| {
|
||||
const pointer = res.pointer;
|
||||
root.debug.print("{*}", .{pointer});
|
||||
}
|
||||
|
||||
//root.main();
|
||||
}
|
||||
12
src/arch/aarch64/machine/serial.zig
Normal file
12
src/arch/aarch64/machine/serial.zig
Normal file
@ -0,0 +1,12 @@
|
||||
const root = @import("root");
|
||||
const libk = @import("libk");
|
||||
|
||||
const PhysicalAddress = root.machine.memory.PhysicalAddress;
|
||||
const SerialPort = libk.machine.serial.uart_pl011.SerialPort;
|
||||
|
||||
pub var COM1: SerialPort = undefined;
|
||||
|
||||
pub fn init() void {
|
||||
COM1.open(PhysicalAddress.init(0x0900_0000).toAddr());
|
||||
COM1.init(24000000, 115200) catch {};
|
||||
}
|
||||
7
src/arch/arch.zig
Normal file
7
src/arch/arch.zig
Normal file
@ -0,0 +1,7 @@
|
||||
pub usingnamespace switch (@import("builtin").cpu.arch) {
|
||||
.aarch64 => @import("aarch64/aarch64.zig"),
|
||||
.loongarch64 => @import("loong64/loong64.zig"),
|
||||
.riscv64 => @import("riscv64/riscv64.zig"),
|
||||
.x86_64 => @import("x86_64/x86_64.zig"),
|
||||
else => |arch| @compileError("unsupported architecture: " ++ arch),
|
||||
};
|
||||
16
src/arch/loong64/debug.zig
Normal file
16
src/arch/loong64/debug.zig
Normal file
@ -0,0 +1,16 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root");
|
||||
|
||||
pub const Error = error {};
|
||||
pub const Writer = std.io.GenericWriter(void, Error, write);
|
||||
|
||||
pub fn writer() Writer {
|
||||
return .{ .context = {} };
|
||||
}
|
||||
|
||||
fn write(_: void, data: []const u8) Error!usize {
|
||||
root.arch.machine.serial.COM1.write(data);
|
||||
root.machine.Console.Instance.write(data);
|
||||
|
||||
return data.len;
|
||||
}
|
||||
6
src/arch/loong64/loong64.zig
Executable file
6
src/arch/loong64/loong64.zig
Executable file
@ -0,0 +1,6 @@
|
||||
pub const debug = @import("debug.zig");
|
||||
pub const machine = @import("machine/machine.zig");
|
||||
|
||||
pub inline fn halt() void {
|
||||
asm volatile ("idle 0");
|
||||
}
|
||||
9
src/arch/loong64/machine/machine.zig
Normal file
9
src/arch/loong64/machine/machine.zig
Normal file
@ -0,0 +1,9 @@
|
||||
const root = @import("root");
|
||||
const limine = @import("limine");
|
||||
|
||||
pub const serial = @import("serial.zig");
|
||||
|
||||
pub fn init() void {
|
||||
serial.init();
|
||||
//root.main();
|
||||
}
|
||||
13
src/arch/loong64/machine/serial.zig
Normal file
13
src/arch/loong64/machine/serial.zig
Normal file
@ -0,0 +1,13 @@
|
||||
const root = @import("root");
|
||||
const libk = @import("libk");
|
||||
|
||||
const Mmio = libk.io.Mmio;
|
||||
const PhysicalAddress = root.machine.memory.PhysicalAddress;
|
||||
const SerialPort = libk.machine.serial.uart_16550.SerialPort;
|
||||
|
||||
pub var COM1: SerialPort(Mmio(u8)) = undefined;
|
||||
|
||||
pub fn init() void {
|
||||
COM1.open(PhysicalAddress.init(0x1fe0_01e0).toAddr());
|
||||
COM1.init() catch {};
|
||||
}
|
||||
16
src/arch/riscv64/debug.zig
Normal file
16
src/arch/riscv64/debug.zig
Normal file
@ -0,0 +1,16 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root");
|
||||
|
||||
pub const Error = error {};
|
||||
pub const Writer = std.io.GenericWriter(void, Error, write);
|
||||
|
||||
pub fn writer() Writer {
|
||||
return .{ .context = {} };
|
||||
}
|
||||
|
||||
fn write(_: void, data: []const u8) Error!usize {
|
||||
root.arch.machine.serial.COM1.write(data);
|
||||
root.machine.Console.Instance.write(data);
|
||||
|
||||
return data.len;
|
||||
}
|
||||
11
src/arch/riscv64/machine/machine.zig
Normal file
11
src/arch/riscv64/machine/machine.zig
Normal file
@ -0,0 +1,11 @@
|
||||
const root = @import("root");
|
||||
const limine = @import("limine");
|
||||
|
||||
pub const serial = @import("serial.zig");
|
||||
|
||||
export var device_tree_blob_request: limine.DeviceTreeBlob.Request = .{};
|
||||
|
||||
pub fn init() void {
|
||||
serial.init();
|
||||
//root.main();
|
||||
}
|
||||
13
src/arch/riscv64/machine/serial.zig
Normal file
13
src/arch/riscv64/machine/serial.zig
Normal file
@ -0,0 +1,13 @@
|
||||
const root = @import("root");
|
||||
const libk = @import("libk");
|
||||
|
||||
const Mmio = libk.io.Mmio;
|
||||
const PhysicalAddress = root.machine.memory.PhysicalAddress;
|
||||
const SerialPort = libk.machine.serial.uart_16550.SerialPort;
|
||||
|
||||
pub var COM1: SerialPort(Mmio(u8)) = undefined;
|
||||
|
||||
pub fn init() void {
|
||||
COM1.open(PhysicalAddress.init(0x1000_0000).toAddr());
|
||||
COM1.init() catch {};
|
||||
}
|
||||
6
src/arch/riscv64/riscv64.zig
Executable file
6
src/arch/riscv64/riscv64.zig
Executable file
@ -0,0 +1,6 @@
|
||||
pub const debug = @import("debug.zig");
|
||||
pub const machine = @import("machine/machine.zig");
|
||||
|
||||
pub inline fn halt() void {
|
||||
asm volatile ("wfi");
|
||||
}
|
||||
162
src/arch/x86_64/cpuid.zig
Normal file
162
src/arch/x86_64/cpuid.zig
Normal file
@ -0,0 +1,162 @@
|
||||
pub const Vendor = struct {
|
||||
id: [12]u8,
|
||||
|
||||
pub inline fn get() @This() {
|
||||
var ebx: u32 = 0;
|
||||
var ecx: u32 = 0;
|
||||
var edx: u32 = 0;
|
||||
|
||||
asm volatile (
|
||||
\\cpuid
|
||||
: [ebx] "={ebx}" (ebx),
|
||||
[ecx] "={ecx}" (ecx),
|
||||
[edx] "={edx}" (edx),
|
||||
: [code] "{eax}" ( 0),
|
||||
);
|
||||
|
||||
const triplet: [3]u32 = .{ebx, edx, ecx};
|
||||
const string: [12]u8 = @bitCast(triplet);
|
||||
|
||||
return .{ .id = string };
|
||||
}
|
||||
};
|
||||
|
||||
pub const Features = packed struct(u64) {
|
||||
sse3 : bool = false,
|
||||
pclmul : bool = false,
|
||||
dtes64 : bool = false,
|
||||
monitor : bool = false,
|
||||
ds_cpl : bool = false,
|
||||
vmx : bool = false,
|
||||
smx : bool = false,
|
||||
est : bool = false,
|
||||
tm2 : bool = false,
|
||||
ssse3 : bool = false,
|
||||
cid : bool = false,
|
||||
sdbg : bool = false,
|
||||
fma : bool = false,
|
||||
cx16 : bool = false,
|
||||
xtpr : bool = false,
|
||||
pdcm : bool = false,
|
||||
reserved_0 : u1 = 0,
|
||||
pcid : bool = false,
|
||||
dca : bool = false,
|
||||
sse4_1 : bool = false,
|
||||
sse4_2 : bool = false,
|
||||
x2apic : bool = false,
|
||||
movbe : bool = false,
|
||||
popcnt : bool = false,
|
||||
tsc_1 : bool = false,
|
||||
aes : bool = false,
|
||||
xsave : bool = false,
|
||||
osxsave : bool = false,
|
||||
avx : bool = false,
|
||||
f16c : bool = false,
|
||||
rdrand : bool = false,
|
||||
hypervisor : bool = false,
|
||||
fpu : bool = false,
|
||||
vme : bool = false,
|
||||
de : bool = false,
|
||||
pse : bool = false,
|
||||
tsc_2 : bool = false,
|
||||
msr : bool = false,
|
||||
pae : bool = false,
|
||||
mce : bool = false,
|
||||
cx8 : bool = false,
|
||||
apic : bool = false,
|
||||
reserved_1 : u1 = 0,
|
||||
sep : bool = false,
|
||||
mtrr : bool = false,
|
||||
pge : bool = false,
|
||||
mca : bool = false,
|
||||
cmov : bool = false,
|
||||
pat : bool = false,
|
||||
pse36 : bool = false,
|
||||
psn : bool = false,
|
||||
clflush : bool = false,
|
||||
reserved_2 : u1 = 0,
|
||||
ds : bool = false,
|
||||
acpi : bool = false,
|
||||
mmx : bool = false,
|
||||
fxsr : bool = false,
|
||||
sse : bool = false,
|
||||
sse2 : bool = false,
|
||||
ss : bool = false,
|
||||
htt : bool = false,
|
||||
tm : bool = false,
|
||||
ia64 : bool = false,
|
||||
pbe : bool = false,
|
||||
|
||||
pub inline fn get() @This() {
|
||||
var ecx: u32 = 0;
|
||||
var edx: u32 = 0;
|
||||
|
||||
asm volatile (
|
||||
\\cpuid
|
||||
: [ecx] "={ecx}" (ecx),
|
||||
[edx] "={edx}" (edx),
|
||||
: [code] "{eax}" ( 1),
|
||||
);
|
||||
|
||||
const array: [2]u32 = .{ecx, edx};
|
||||
const result: Features = @bitCast(array);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
pub const BusFrequency = struct {
|
||||
denominator: u32,
|
||||
numerator: u32,
|
||||
/// Core crystal clock frequency in Hz
|
||||
clock: u32,
|
||||
|
||||
pub inline fn get() @This() {
|
||||
var eax: u32 = 0;
|
||||
var ebx: u32 = 0;
|
||||
var ecx: u32 = 0;
|
||||
|
||||
asm volatile (
|
||||
\\cpuid
|
||||
: [eax] "={eax}" (eax),
|
||||
[ebx] "={ebx}" (ebx),
|
||||
[ecx] "={ecx}" (ecx),
|
||||
: [code] "{eax}" ( 21),
|
||||
);
|
||||
|
||||
return .{
|
||||
.denominator = eax,
|
||||
.numerator = ebx,
|
||||
.clock = ecx,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const CoreFrequency = struct {
|
||||
/// Core base frequency in MHz
|
||||
base: u16,
|
||||
/// Core maximum frequency in MHz
|
||||
maximum: u16,
|
||||
/// bus (reference) frequency in MHz
|
||||
bus: u16,
|
||||
|
||||
pub inline fn get() @This() {
|
||||
var eax: u32 = 0;
|
||||
var ebx: u32 = 0;
|
||||
var ecx: u32 = 0;
|
||||
|
||||
asm volatile (
|
||||
\\cpuid
|
||||
: [eax] "={eax}" (eax),
|
||||
[ebx] "={ebx}" (ebx),
|
||||
[ecx] "={ecx}" (ecx),
|
||||
: [code] "{eax}" ( 22),
|
||||
);
|
||||
|
||||
return .{
|
||||
.base = @truncate(eax),
|
||||
.maximum = @truncate(ebx),
|
||||
.bus = @truncate(ecx),
|
||||
};
|
||||
}
|
||||
};
|
||||
27
src/arch/x86_64/debug.zig
Normal file
27
src/arch/x86_64/debug.zig
Normal file
@ -0,0 +1,27 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root");
|
||||
const libk = @import("libk");
|
||||
|
||||
pub const QemuExitCode = enum(u16) {
|
||||
success = 0x10,
|
||||
failed = 0x11,
|
||||
};
|
||||
|
||||
pub fn exit(code: QemuExitCode) noreturn {
|
||||
root.arch.out(0x16, @intFromEnum(code));
|
||||
while (true) root.arch.halt();
|
||||
}
|
||||
|
||||
pub const Error = error{};
|
||||
pub const Writer = std.io.GenericWriter(void, Error, write);
|
||||
|
||||
pub fn writer() Writer {
|
||||
return .{ .context = {} };
|
||||
}
|
||||
|
||||
fn write(_: void, data: []const u8) Error!usize {
|
||||
root.arch.machine.serial.COM1.write(data);
|
||||
root.machine.Console.Instance.write(data);
|
||||
|
||||
return data.len;
|
||||
}
|
||||
267
src/arch/x86_64/interrupt/exception.zig
Normal file
267
src/arch/x86_64/interrupt/exception.zig
Normal file
@ -0,0 +1,267 @@
|
||||
const root = @import("root");
|
||||
|
||||
const Interrupt = root.arch.interrupt.Interrupt;
|
||||
const StackFrame = root.arch.interrupt.InterruptStackFrame;
|
||||
|
||||
const print = root.debug.print;
|
||||
const abort = root.debug.abort;
|
||||
|
||||
pub const DivisionError = Interrupt(struct {
|
||||
pub const Vector = 0x00;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: division error\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const Debug = Interrupt(struct {
|
||||
pub const Vector = 0x01;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: debug\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
}
|
||||
});
|
||||
|
||||
pub const NonMaskable = Interrupt(struct {
|
||||
pub const Vector = 0x02;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: non-maskable\n", .{});
|
||||
print("Stack frame: {}", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const Breakpoint = Interrupt(struct {
|
||||
pub const Vector = 0x03;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: breakpoint\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
}
|
||||
});
|
||||
|
||||
pub const Overflow = Interrupt(struct {
|
||||
pub const Vector = 0x04;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: overflow\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const BoundRangeExceeded = Interrupt(struct {
|
||||
pub const Vector = 0x05;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: bound range exceeded\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const InvalidOpcode = Interrupt(struct {
|
||||
pub const Vector = 0x06;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: invalid opcode\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const DeviceNotAvailable = Interrupt(struct {
|
||||
pub const Vector = 0x07;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: device not availble\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const DoubleFault = Interrupt(struct {
|
||||
pub const Vector = 0x08;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: double fault\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const InvalidTss = Interrupt(struct {
|
||||
pub const Vector = 0x0A;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: invalid TSS\n", .{});
|
||||
print("Error code: {}\n", .{stack.error_code});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const SegmentNotPresent = Interrupt(struct {
|
||||
pub const Vector = 0x0B;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: segment not present\n", .{});
|
||||
print("Error code: {}\n", .{stack.error_code});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const StackSegmentFault = Interrupt(struct {
|
||||
pub const Vector = 0x0C;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: stack segment fault\n", .{});
|
||||
print("Error code: {}\n", .{stack.error_code});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const GeneralProtectionFault = Interrupt(struct {
|
||||
pub const Vector = 0x0D;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: general protection fault\n", .{});
|
||||
print("Error code: {}\n", .{stack.error_code});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const PageFault = Interrupt(struct {
|
||||
pub const Vector = 0x0E;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: page fault\n", .{});
|
||||
print("Error code: 0x{x}\n", .{stack.error_code});
|
||||
print("Address: 0x{x}\n", .{root.arch.cr2()});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const x87FloatingPoint = Interrupt(struct {
|
||||
pub const Vector = 0x10;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: x86 floating point\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const AlignmentCheck = Interrupt(struct {
|
||||
pub const Vector = 0x11;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: alignment check\n", .{});
|
||||
print("Error code: {}\n", .{stack.error_code});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const MachineCheck = Interrupt(struct {
|
||||
pub const Vector = 0x12;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: machine check\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const SimdFloatingPoint = Interrupt(struct {
|
||||
pub const Vector = 0x13;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: SIMD floating point\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const Virtualization = Interrupt(struct {
|
||||
pub const Vector = 0x14;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: virtualization\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const ControlProtection = Interrupt(struct {
|
||||
pub const Vector = 0x15;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: control protection\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const HypervisorInjection = Interrupt(struct {
|
||||
pub const Vector = 0x1C;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: hypervisor injection\n", .{});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const VmmCommunication = Interrupt(struct {
|
||||
pub const Vector = 0x1D;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: VMM communication\n", .{});
|
||||
print("Error code: {}\n", .{stack.error_code});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
|
||||
pub const Security = Interrupt(struct {
|
||||
pub const Vector = 0x1E;
|
||||
pub const HasErrorCode = true;
|
||||
|
||||
pub fn handler(stack: *StackFrame) callconv(.C) void {
|
||||
print("Exception: security\n", .{});
|
||||
print("Error code: {}\n", .{stack.error_code});
|
||||
print("Stack frame: {}\n", .{stack});
|
||||
abort();
|
||||
}
|
||||
});
|
||||
7
src/arch/x86_64/interrupt/hardware.zig
Normal file
7
src/arch/x86_64/interrupt/hardware.zig
Normal file
@ -0,0 +1,7 @@
|
||||
pub inline fn enable() void {
|
||||
asm volatile ("sti");
|
||||
}
|
||||
|
||||
pub inline fn disable() void {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
229
src/arch/x86_64/interrupt/interrupt.zig
Normal file
229
src/arch/x86_64/interrupt/interrupt.zig
Normal file
@ -0,0 +1,229 @@
|
||||
const std = @import("std");
|
||||
const arch = @import("../x86_64.zig");
|
||||
const libk = @import("libk");
|
||||
|
||||
pub const exception = @import("exception.zig");
|
||||
pub const hardware = @import("hardware.zig");
|
||||
|
||||
const BitFields = libk.utils.BitFields;
|
||||
const PrivilegeLevel = arch.PrivilegeLevel;
|
||||
const CodeSegment = arch.segmentation.CodeSegment;
|
||||
const SegmentSelector = arch.segmentation.SegmentSelector;
|
||||
|
||||
pub const InterruptStackFrame = extern struct {
|
||||
r15: u64,
|
||||
r14: u64,
|
||||
r13: u64,
|
||||
r12: u64,
|
||||
r11: u64,
|
||||
r10: u64,
|
||||
r9: u64,
|
||||
r8: u64,
|
||||
rdi: u64,
|
||||
rsi: u64,
|
||||
rbp: u64,
|
||||
rdx: u64,
|
||||
rcx: u64,
|
||||
rbx: u64,
|
||||
rax: u64,
|
||||
|
||||
vector_number: u64,
|
||||
error_code: u64,
|
||||
|
||||
instruction_pointer: u64,
|
||||
code_segment: u64,
|
||||
cpu_flags: u64,
|
||||
stack_pointer: u64,
|
||||
stack_segment: u64,
|
||||
};
|
||||
|
||||
pub fn Interrupt(comptime T: type) type {
|
||||
return struct {
|
||||
pub fn entry() callconv(.naked) noreturn {
|
||||
asm volatile (
|
||||
\\.align 16
|
||||
);
|
||||
|
||||
// push a dummy error code if neccessary
|
||||
if (comptime !T.HasErrorCode) {
|
||||
asm volatile ("pushq $0");
|
||||
}
|
||||
|
||||
// store the vector number for interrupt dispatcher
|
||||
asm volatile (
|
||||
"pushq " ++ std.fmt.comptimePrint("{}", .{T.Vector})
|
||||
);
|
||||
|
||||
// jump to the stub
|
||||
asm volatile (
|
||||
\\jmp interruptCommonStub
|
||||
);
|
||||
}
|
||||
|
||||
pub fn register(selector: SegmentSelector) void {
|
||||
InterruptDescriptorTable.entries[T.Vector] = .init(selector, entry);
|
||||
if (@hasDecl(T, "handler")) attach();
|
||||
}
|
||||
|
||||
pub fn attach() void {
|
||||
Dispatcher.handlers[T.Vector] = T.handler;
|
||||
}
|
||||
|
||||
pub fn detach() void {
|
||||
Dispatcher.handlers[T.Vector] = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Dispatcher = struct {
|
||||
var handlers: [Limit]?*const Handler = undefined;
|
||||
|
||||
export fn dispatchInterruptHandler(isf: *InterruptStackFrame) callconv(.C) void {
|
||||
const target = handlers[isf.vector_number];
|
||||
const handler = target orelse default;
|
||||
|
||||
hardware.disable();
|
||||
handler(isf);
|
||||
hardware.enable();
|
||||
}
|
||||
|
||||
export fn interruptCommonStub() callconv(.naked) noreturn {
|
||||
asm volatile (
|
||||
\\push %rax
|
||||
\\push %rbx
|
||||
\\push %rcx
|
||||
\\push %rdx
|
||||
\\push %rbp
|
||||
\\push %rsi
|
||||
\\push %rdi
|
||||
\\push %r8
|
||||
\\push %r9
|
||||
\\push %r10
|
||||
\\push %r11
|
||||
\\push %r12
|
||||
\\push %r13
|
||||
\\push %r14
|
||||
\\push %r15
|
||||
\\mov %rsp, %rdi
|
||||
\\call dispatchInterruptHandler
|
||||
\\pop %r15
|
||||
\\pop %r14
|
||||
\\pop %r13
|
||||
\\pop %r12
|
||||
\\pop %r11
|
||||
\\pop %r10
|
||||
\\pop %r9
|
||||
\\pop %r8
|
||||
\\pop %rdi
|
||||
\\pop %rsi
|
||||
\\pop %rbp
|
||||
\\pop %rdx
|
||||
\\pop %rcx
|
||||
\\pop %rbx
|
||||
\\pop %rax
|
||||
\\add $16, %rsp
|
||||
\\iretq
|
||||
);
|
||||
}
|
||||
|
||||
fn default(isf: *InterruptStackFrame) callconv(.C) void {
|
||||
std.debug.panic("Unhandled Interrupt\n{}", .{isf});
|
||||
}
|
||||
};
|
||||
|
||||
pub const InterruptDescriptorTable = extern struct {
|
||||
var entries: [Limit]Entry = undefined;
|
||||
|
||||
pub const Entry = packed struct {
|
||||
offset_0: u16,
|
||||
selector: SegmentSelector,
|
||||
option: Option,
|
||||
offset_1: u16,
|
||||
offset_2: u32,
|
||||
reserved: u32,
|
||||
|
||||
pub const Option = BitFields(packed struct(u16) {
|
||||
stack_index : u3 = 0,
|
||||
reserved_0 : u5 = 0,
|
||||
trap_gate : bool = false,
|
||||
reserved_1 : u3 = 0b111,
|
||||
reserved_2 : u1 = 0,
|
||||
privilege_level : PrivilegeLevel,
|
||||
present : bool = false,
|
||||
});
|
||||
|
||||
pub fn init(selector: SegmentSelector, stub: *const Stub) @This() {
|
||||
const offset = @intFromPtr(stub);
|
||||
const option = Option.init(.{
|
||||
.privilege_level = .ring0,
|
||||
.present = true
|
||||
});
|
||||
|
||||
return .{
|
||||
.offset_0 = @truncate(offset),
|
||||
.selector = selector,
|
||||
.option = option,
|
||||
.offset_1 = @truncate(offset >> 16),
|
||||
.offset_2 = @truncate(offset >> 32),
|
||||
.reserved = 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn read() ?*@TypeOf(entries) {
|
||||
const pointer = arch.sidt();
|
||||
const table: ?*@TypeOf(entries) = @ptrFromInt(pointer.base);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
pub fn load() void {
|
||||
arch.lidt(.{
|
||||
.limit = @sizeOf(@TypeOf(entries)) - 1,
|
||||
.base = @intFromPtr(&entries)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pub const Stub = fn() callconv(.naked) noreturn;
|
||||
pub const Handler = fn(*InterruptStackFrame) callconv(.C) void;
|
||||
|
||||
pub const Limit: usize = 256;
|
||||
pub const RequestBase: usize = 32;
|
||||
|
||||
pub fn init() void {
|
||||
const selector = CodeSegment.get();
|
||||
defer InterruptDescriptorTable.load();
|
||||
|
||||
inline for (RequestBase..Limit) |i| {
|
||||
const Request = Interrupt(struct {
|
||||
pub const Vector = i;
|
||||
pub const HasErrorCode = false;
|
||||
});
|
||||
Request.register(selector);
|
||||
}
|
||||
|
||||
exception.DivisionError.register(selector);
|
||||
exception.Debug.register(selector);
|
||||
exception.NonMaskable.register(selector);
|
||||
exception.Breakpoint.register(selector);
|
||||
exception.Overflow.register(selector);
|
||||
exception.BoundRangeExceeded.register(selector);
|
||||
exception.InvalidOpcode.register(selector);
|
||||
exception.DeviceNotAvailable.register(selector);
|
||||
exception.DoubleFault.register(selector);
|
||||
exception.InvalidTss.register(selector);
|
||||
exception.SegmentNotPresent.register(selector);
|
||||
exception.StackSegmentFault.register(selector);
|
||||
exception.GeneralProtectionFault.register(selector);
|
||||
exception.PageFault.register(selector);
|
||||
exception.x87FloatingPoint.register(selector);
|
||||
exception.AlignmentCheck.register(selector);
|
||||
exception.MachineCheck.register(selector);
|
||||
exception.SimdFloatingPoint.register(selector);
|
||||
exception.Virtualization.register(selector);
|
||||
exception.ControlProtection.register(selector);
|
||||
exception.HypervisorInjection.register(selector);
|
||||
exception.VmmCommunication.register(selector);
|
||||
exception.Security.register(selector);
|
||||
}
|
||||
4
src/arch/x86_64/machine/apic/Apic.zig
Normal file
4
src/arch/x86_64/machine/apic/Apic.zig
Normal file
@ -0,0 +1,4 @@
|
||||
pub const ioApic = @import("ioApic.zig");
|
||||
pub const x2Apic = @import("x2Apic.zig");
|
||||
pub const xApic = @import("xApic.zig");
|
||||
|
||||
0
src/arch/x86_64/machine/apic/ioApic.zig
Normal file
0
src/arch/x86_64/machine/apic/ioApic.zig
Normal file
0
src/arch/x86_64/machine/apic/x2Apic.zig
Normal file
0
src/arch/x86_64/machine/apic/x2Apic.zig
Normal file
165
src/arch/x86_64/machine/apic/xApic.zig
Normal file
165
src/arch/x86_64/machine/apic/xApic.zig
Normal file
@ -0,0 +1,165 @@
|
||||
const Self = @This();
|
||||
const libk = @import("libk");
|
||||
const root = @import("root");
|
||||
|
||||
const Mmio = libk.io.Mmio;
|
||||
const IoMap = libk.io.IoMap;
|
||||
|
||||
const ModelSpecificRegister = root.arch.ModelSpecificRegister;
|
||||
const PhysicalAddress = root.machine.memory.PhysicalAddress;
|
||||
|
||||
pub const Register = packed struct(u64) {
|
||||
reserved_0 : u32,
|
||||
base : u19,
|
||||
enable : bool,
|
||||
reserved_1 : u2,
|
||||
bsp : bool,
|
||||
reserved_2 : u8,
|
||||
|
||||
pub fn read() Register {
|
||||
const register = ModelSpecificRegister.apic_base.read();
|
||||
const result = Register.fromBits(register);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn write(self: *Register) void {
|
||||
ModelSpecificRegister.apic_base.write(self.bits());
|
||||
}
|
||||
};
|
||||
|
||||
io: IoMap(Mmio(u32)).Schematics(struct {
|
||||
/// Local APIC ID Register
|
||||
id : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x020 },
|
||||
/// Local APIC Version Register
|
||||
version : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x030 },
|
||||
/// Task Priority Register
|
||||
tpr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x080 },
|
||||
/// EOI register
|
||||
eoi : IoMap(u32).Pin = .{ .perm = .writeonly, .base = 0x0B0 },
|
||||
/// Logical Destination Register
|
||||
ldr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x0D0 },
|
||||
/// Spurious Interrupt Vector Register
|
||||
svr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x0F0 },
|
||||
/// In-Service Register
|
||||
isr0 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x100 },
|
||||
/// ISR bits 63:32
|
||||
isr1 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x110 },
|
||||
/// ISR bits 95:64
|
||||
isr2 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x120 },
|
||||
/// ISR bits 127:96
|
||||
isr3 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x130 },
|
||||
/// ISR bits 159:128
|
||||
isr4 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x140 },
|
||||
/// ISR bits 191:160
|
||||
isr5 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x150 },
|
||||
/// ISR bits 223:192
|
||||
isr6 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x160 },
|
||||
/// ISR bits 255:224
|
||||
isr7 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x170 },
|
||||
/// Trigger Mode Register
|
||||
tmr0 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x180 },
|
||||
/// TMR bits 63:32
|
||||
tmr1 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x190 },
|
||||
/// TMR bits 95:64
|
||||
tmr2 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x1A0 },
|
||||
/// TMR bits 127:96
|
||||
tmr3 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x1B0 },
|
||||
/// TMR bits 159:128
|
||||
tmr4 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x1C0 },
|
||||
/// TMR bits 191:160
|
||||
tmr5 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x1D0 },
|
||||
/// TMR bits 223:192
|
||||
tmr6 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x1E0 },
|
||||
/// TMR bits 255:224
|
||||
tmr7 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x1F0 },
|
||||
/// Interrupt Request Register
|
||||
irr0 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x200 },
|
||||
/// IRR bits 63:32
|
||||
irr1 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x210 },
|
||||
/// IRR bits 95:64
|
||||
irr2 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x220 },
|
||||
/// IRR bits 127:96
|
||||
irr3 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x230 },
|
||||
/// IRR bits 159:128
|
||||
irr4 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x240 },
|
||||
/// IRR bits 191:160
|
||||
irr5 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x250 },
|
||||
/// IRR bits 223:192
|
||||
irr6 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x260 },
|
||||
/// IRR bits 255:224
|
||||
irr7 : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x270 },
|
||||
/// Error Status Register
|
||||
esr : IoMap(u32).Pin = .{ .perm = .all, .base = 0x280 },
|
||||
/// LVT CMCI Register
|
||||
lvt_cmci : IoMap(u32).Pin = .{ .perm = .all, .base = 0x2F0 },
|
||||
/// Interrupt Command Register
|
||||
icr0 : IoMap(u32).Pin = .{ .perm = .all, .base = 0x300 },
|
||||
/// Interrupt Command Register
|
||||
icr1 : IoMap(u32).Pin = .{ .perm = .all, .base = 0x310 },
|
||||
/// LVT Timer Register
|
||||
lvt_timer : IoMap(u32).Pin = .{ .perm = .all, .base = 0x320 },
|
||||
/// LVT Thermal Sensor Register
|
||||
lvt_thermal : IoMap(u32).Pin = .{ .perm = .all, .base = 0x330 },
|
||||
/// LVT Performance Monitoring Register
|
||||
lvt_pmi : IoMap(u32).Pin = .{ .perm = .all, .base = 0x340 },
|
||||
/// LVT LINT0 Register
|
||||
lvt_lint0 : IoMap(u32).Pin = .{ .perm = .all, .base = 0x350 },
|
||||
/// LVT LINT1 Register
|
||||
lvt_lint1 : IoMap(u32).Pin = .{ .perm = .all, .base = 0x360 },
|
||||
/// LVT Error Register
|
||||
lvt_error : IoMap(u32).Pin = .{ .perm = .all, .base = 0x370 },
|
||||
/// Initial Count Register
|
||||
timer_init_count : IoMap(u32).Pin = .{ .perm = .all, .base = 0x380 },
|
||||
/// Current Count Register
|
||||
timer_current_count : IoMap(u32).Pin = .{ .perm = .readonly, .base = 0x390 },
|
||||
/// Divide Configuration Register
|
||||
timer_div_conf : IoMap(u32).Pin = .{ .perm = .all, .base = 0x3E0 },
|
||||
}),
|
||||
|
||||
pub fn attach(self: *@This()) void {
|
||||
var register: Register = .read();
|
||||
const address = PhysicalAddress.init(register.base).toVirtual();
|
||||
|
||||
register.enable = true;
|
||||
register.write();
|
||||
|
||||
self.io.open(address.base);
|
||||
self.io.write("svr", 1 << 8 | 15) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn detach(_: *@This()) void {
|
||||
var register: Register = .read();
|
||||
register.enable = false;
|
||||
register.write();
|
||||
}
|
||||
|
||||
pub fn isBootstrapCore(self: *Self) bool {
|
||||
return self.register.bsp;
|
||||
}
|
||||
|
||||
pub fn getId(self: *Self) u32 {
|
||||
return self.io.read("id") catch unreachable;
|
||||
}
|
||||
|
||||
pub fn getLogicalId(self: *Self) u32 {
|
||||
return self.io.read("ldr") catch unreachable;
|
||||
}
|
||||
|
||||
pub fn getVersion(self: *Self) u32 {
|
||||
return self.io.read("version") catch unreachable;
|
||||
}
|
||||
|
||||
pub fn notify(self: *Self) void {
|
||||
self.io.write("eoi", 0);
|
||||
}
|
||||
|
||||
pub fn enableTsc(self: *Self, vector: u8) void {
|
||||
_ = self;
|
||||
_ = vector;
|
||||
// TODO
|
||||
}
|
||||
|
||||
pub fn setTsc(_: *Self, value: u64) void {
|
||||
ModelSpecificRegister.tsc_dead_line.write(value);
|
||||
}
|
||||
24
src/arch/x86_64/machine/cmos.zig
Normal file
24
src/arch/x86_64/machine/cmos.zig
Normal file
@ -0,0 +1,24 @@
|
||||
const arch = @import("../x86_64.zig");
|
||||
const libk = @import("libk");
|
||||
|
||||
const Pio = arch.Pio;
|
||||
const WriteOnly = libk.io.WriteOnly;
|
||||
|
||||
var command: WriteOnly(Pio(u8)) = .open(0x70);
|
||||
var data: Pio(u8) = .open(0x71);
|
||||
|
||||
pub const Register = enum(u8) {
|
||||
status_a = 0x0A,
|
||||
status_b = 0x0B,
|
||||
_,
|
||||
};
|
||||
|
||||
pub fn read(register: Register) u8 {
|
||||
command.write(0x80 | @intFromEnum(register));
|
||||
return data.read();
|
||||
}
|
||||
|
||||
pub fn write(register: Register, value: u8) void {
|
||||
command.write(0x80 | @intFromEnum(register));
|
||||
data.write(value);
|
||||
}
|
||||
36
src/arch/x86_64/machine/machine.zig
Executable file
36
src/arch/x86_64/machine/machine.zig
Executable file
@ -0,0 +1,36 @@
|
||||
const arch = @import("../x86_64.zig");
|
||||
const root = @import("root");
|
||||
const limine = @import("limine");
|
||||
|
||||
pub const pic = @import("pic.zig");
|
||||
pub const ps2 = @import("ps2.zig");
|
||||
pub const cmos = @import("cmos.zig");
|
||||
pub const time = @import("time.zig");
|
||||
pub const serial = @import("serial.zig");
|
||||
|
||||
pub const Apic = @import("apic/Apic.zig");
|
||||
|
||||
export var bootloader_info_request: limine.BootloaderInfo.Request = .{};
|
||||
export var paging_request: limine.Paging.Request = .{};
|
||||
|
||||
pub fn init() void {
|
||||
serial.init();
|
||||
|
||||
arch.segmentation.init();
|
||||
arch.interrupt.init();
|
||||
|
||||
if (paging_request.response) |res| {
|
||||
arch.paging.mode = res.mode;
|
||||
}
|
||||
|
||||
if (bootloader_info_request.response) |res| {
|
||||
root.debug.print("name: {s}\n", .{res.name});
|
||||
root.debug.print("version: {s}\n", .{res.version});
|
||||
}
|
||||
|
||||
arch.interrupt.hardware.enable();
|
||||
pic.ChainedPics.enable();
|
||||
time.init();
|
||||
|
||||
root.main();
|
||||
}
|
||||
113
src/arch/x86_64/machine/pic.zig
Normal file
113
src/arch/x86_64/machine/pic.zig
Normal file
@ -0,0 +1,113 @@
|
||||
const arch = @import("../x86_64.zig");
|
||||
const libk = @import("libk");
|
||||
|
||||
const Pio = arch.Pio;
|
||||
const Interrupt = arch.interrupt.Interrupt;
|
||||
const StackFrame = arch.interrupt.InterruptStackFrame;
|
||||
const WriteOnly = libk.io.WriteOnly;
|
||||
|
||||
pub const Command = enum(u8) {
|
||||
init = 0x11,
|
||||
end_of_interrupt = 0x20,
|
||||
};
|
||||
|
||||
pub const Mode = enum(u8) {
|
||||
@"8086" = 0x01,
|
||||
};
|
||||
|
||||
pub fn Pic(comptime Offset: u8) type {
|
||||
return struct {
|
||||
command: WriteOnly(Pio(u8)),
|
||||
data: Pio(u8),
|
||||
|
||||
pub inline fn init(self: *@This()) void {
|
||||
self.data.write(Offset);
|
||||
}
|
||||
|
||||
pub inline fn notify(self: *@This()) void {
|
||||
self.command.write(@intFromEnum(Command.end_of_interrupt));
|
||||
}
|
||||
|
||||
pub inline fn contains(_: *@This(), id: u8) bool {
|
||||
return Offset <= id and id < Offset + 8;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const ChainedPics = struct {
|
||||
var pic1: Pic(0x20) = .{
|
||||
.command = .open(0x20),
|
||||
.data = .open(0x21)
|
||||
};
|
||||
var pic2: Pic(0x28) = .{
|
||||
.command = .open(0xA0),
|
||||
.data = .open(0xA1)
|
||||
};
|
||||
|
||||
pub fn enable() void {
|
||||
Timer.attach();
|
||||
Keyboard.attach();
|
||||
|
||||
// Initiate initialization sequence
|
||||
pic1.command.write(@intFromEnum(Command.init));
|
||||
pic2.command.write(@intFromEnum(Command.init));
|
||||
|
||||
// Setup base offsets
|
||||
pic1.init();
|
||||
pic2.init();
|
||||
|
||||
// Configure PIC chaining
|
||||
pic1.data.write(4);
|
||||
pic2.data.write(2);
|
||||
|
||||
// Set mode
|
||||
pic1.data.write(@intFromEnum(Mode.@"8086"));
|
||||
pic2.data.write(@intFromEnum(Mode.@"8086"));
|
||||
|
||||
// Unmask interrupts
|
||||
pic1.data.write(0);
|
||||
pic2.data.write(0);
|
||||
|
||||
// Ack remaining interrupts
|
||||
pic1.notify();
|
||||
pic2.notify();
|
||||
}
|
||||
|
||||
pub fn disable() void {
|
||||
pic1.data.write(0xff);
|
||||
pic2.data.write(0xff);
|
||||
}
|
||||
|
||||
pub inline fn contains(id: u8) bool {
|
||||
return pic1.contains(id) or pic2.contains(id);
|
||||
}
|
||||
|
||||
pub inline fn notify(id: u8) void {
|
||||
if (contains(id)) {
|
||||
if (pic2.contains(id)) {
|
||||
pic2.notify();
|
||||
}
|
||||
pic1.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub const Timer = Interrupt(struct {
|
||||
pub const Vector = 0x20;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(_: *StackFrame) callconv(.C) void {
|
||||
arch.machine.time.Pit.tick();
|
||||
notify(Vector);
|
||||
}
|
||||
});
|
||||
|
||||
pub const Keyboard = Interrupt(struct {
|
||||
pub const Vector = 0x21;
|
||||
pub const HasErrorCode = false;
|
||||
|
||||
pub fn handler(_: *StackFrame) callconv(.C) void {
|
||||
arch.machine.ps2.Keyboard.read();
|
||||
notify(Vector);
|
||||
}
|
||||
});
|
||||
};
|
||||
9
src/arch/x86_64/machine/ps2.zig
Normal file
9
src/arch/x86_64/machine/ps2.zig
Normal file
@ -0,0 +1,9 @@
|
||||
const arch = @import("../x86_64.zig");
|
||||
|
||||
pub const Keyboard = struct {
|
||||
var data: arch.Pio(u8) = .open(0x60);
|
||||
|
||||
pub fn read() void {
|
||||
|
||||
}
|
||||
};
|
||||
20
src/arch/x86_64/machine/serial.zig
Normal file
20
src/arch/x86_64/machine/serial.zig
Normal file
@ -0,0 +1,20 @@
|
||||
const arch = @import("../x86_64.zig");
|
||||
const libk = @import("libk");
|
||||
|
||||
const Pio = arch.Pio;
|
||||
const SerialPort = libk.machine.serial.uart_16550.SerialPort;
|
||||
|
||||
pub var COM1: SerialPort(Pio(u8)) = undefined;
|
||||
pub var COM2: SerialPort(Pio(u8)) = undefined;
|
||||
pub var COM3: SerialPort(Pio(u8)) = undefined;
|
||||
|
||||
pub fn init() void {
|
||||
COM1.open(0x3f8);
|
||||
COM1.init() catch {};
|
||||
|
||||
COM2.open(0x2f8);
|
||||
COM2.init() catch {};
|
||||
|
||||
COM3.open(0x3e8);
|
||||
COM3.init() catch {};
|
||||
}
|
||||
171
src/arch/x86_64/machine/time.zig
Normal file
171
src/arch/x86_64/machine/time.zig
Normal file
@ -0,0 +1,171 @@
|
||||
const std = @import("std");
|
||||
const arch = @import("../x86_64.zig");
|
||||
const libk = @import("libk");
|
||||
|
||||
const Pio = arch.Pio;
|
||||
const WriteOnly = libk.io.WriteOnly;
|
||||
const BitFields = libk.utils.BitFields;
|
||||
|
||||
pub const Pit = struct {
|
||||
var command: WriteOnly(Pio(u8)) = .open(0x43);
|
||||
var data: Pio(u8) = .open(0x40);
|
||||
|
||||
const Frequency: f32 = 1.193182;
|
||||
|
||||
const Command = BitFields(packed struct(u8) {
|
||||
bcd: bool,
|
||||
mode: Mode,
|
||||
access: AccessMode,
|
||||
channel: Channel,
|
||||
});
|
||||
|
||||
const Mode = enum(u3) {
|
||||
default = 2,
|
||||
};
|
||||
|
||||
const Channel = enum(u2) {
|
||||
chan0 = 0,
|
||||
chan1 = 1,
|
||||
chan2 = 2,
|
||||
chan3 = 3
|
||||
};
|
||||
|
||||
const AccessMode = enum(u2) {
|
||||
default = 3,
|
||||
};
|
||||
|
||||
pub fn init() void {
|
||||
const byte = Command.init(.{
|
||||
.bcd = false,
|
||||
.mode = .default,
|
||||
.access = .default,
|
||||
.channel = .chan0,
|
||||
});
|
||||
|
||||
//
|
||||
const count: u16 = @round(Frequency * 1000);
|
||||
|
||||
// send command
|
||||
command.write(byte.bits());
|
||||
|
||||
data.write(@truncate(count));
|
||||
data.write(@truncate(count >> 8));
|
||||
}
|
||||
|
||||
pub fn tick() void {
|
||||
timestamp += 1;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Rtc = struct {
|
||||
var is_24hour_format: bool = false;
|
||||
var is_binary_format: bool = false;
|
||||
|
||||
pub const Register = enum(u8) {
|
||||
second = 0x00,
|
||||
minute = 0x02,
|
||||
hour = 0x04,
|
||||
day = 0x07,
|
||||
month = 0x08,
|
||||
year = 0x09,
|
||||
century = 0x20,
|
||||
};
|
||||
|
||||
pub fn init() void {
|
||||
const status = arch.machine.cmos.read(.status_b);
|
||||
is_24hour_format = status & 0x02 > 0;
|
||||
is_binary_format = status & 0x04 > 0;
|
||||
}
|
||||
|
||||
pub fn read(register: Register) u8 {
|
||||
const value = arch.machine.cmos.read(@enumFromInt(@intFromEnum(register)));
|
||||
return if (is_binary_format) value else convertBcdValue(value);
|
||||
}
|
||||
|
||||
pub fn instant() u64 {
|
||||
const century = read(.century);
|
||||
const day = read(.day);
|
||||
const minute = read(.minute);
|
||||
const second = read(.second);
|
||||
|
||||
var hour = read(.hour);
|
||||
var is_pm = false;
|
||||
|
||||
if (!is_24hour_format) {
|
||||
is_pm = hour & 0x80 > 0;
|
||||
hour &= 0x7F;
|
||||
}
|
||||
|
||||
if (!is_binary_format) {
|
||||
hour = convertBcdValue(hour);
|
||||
}
|
||||
|
||||
if (!is_24hour_format) {
|
||||
if (hour == 12) hour = 0;
|
||||
if (is_pm) hour += 12;
|
||||
}
|
||||
|
||||
var year: usize = read(.year);
|
||||
var month: usize = read(.month);
|
||||
|
||||
if (century > 0) { year += century * 100; }
|
||||
else { year += 2000; }
|
||||
|
||||
if (month > 2) { month -= 2; }
|
||||
else { month += 10; year -= 1; }
|
||||
|
||||
const days_since_epoch = (year / 4 - year / 100 + year / 400 + 367 * month / 12 + day) + year * 365 - 719_499;
|
||||
const hours_since_epoch = days_since_epoch * 24 + hour;
|
||||
const minutes_since_epoch = hours_since_epoch * 60 + minute;
|
||||
const seconds_since_epoch = minutes_since_epoch * 60 + second;
|
||||
|
||||
return seconds_since_epoch;
|
||||
}
|
||||
|
||||
pub fn getUnixTimestamp() usize {
|
||||
while (true) {
|
||||
while (isUpdateInProgress()) std.atomic.spinLoopHint();
|
||||
const t1 = instant();
|
||||
|
||||
if (isUpdateInProgress()) continue;
|
||||
const t2 = instant();
|
||||
|
||||
if (t1 == t2) return t1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setUnixTimestamp(value: usize) void {
|
||||
_ = value; // TODO
|
||||
}
|
||||
|
||||
fn isUpdateInProgress() bool {
|
||||
return arch.machine.cmos.read(.status_a) & 0x80 > 0;
|
||||
}
|
||||
|
||||
fn convertBcdValue(value: u8) u8 {
|
||||
return ((value & 0xF0) >> 1) + ((value & 0xF0) >> 3) + (value & 0x0F);
|
||||
}
|
||||
};
|
||||
|
||||
/// Unix Timestamp in milliseconds
|
||||
var timestamp: usize = 0;
|
||||
|
||||
pub fn init() void {
|
||||
Pit.init();
|
||||
Rtc.init();
|
||||
|
||||
timestamp += Rtc.getUnixTimestamp() * 1000;
|
||||
}
|
||||
|
||||
pub fn wait(millis: usize) void {
|
||||
const future = timestamp + millis;
|
||||
while (timestamp < future) std.atomic.spinLoopHint();
|
||||
}
|
||||
|
||||
pub fn getUnixTimestampMs() usize {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
pub fn setUnixTimestampMs(value: usize) void {
|
||||
timestamp = value;
|
||||
}
|
||||
156
src/arch/x86_64/paging.zig
Normal file
156
src/arch/x86_64/paging.zig
Normal file
@ -0,0 +1,156 @@
|
||||
const std = @import("std");
|
||||
const libk = @import("libk");
|
||||
const root = @import("root");
|
||||
const limine = @import("limine");
|
||||
|
||||
const alignUp = libk.utils.alignUp;
|
||||
const alignDown = libk.utils.alignDown;
|
||||
const BitFields = libk.utils.BitFields;
|
||||
|
||||
const PhysicalAddress = root.machine.memory.PhysicalAddress;
|
||||
const VirtualAddress = root.machine.memory.VirtualAddress;
|
||||
|
||||
pub var mode: ?limine.Paging.Mode = null;
|
||||
pub var frame_size: Size = .@"4KiB";
|
||||
|
||||
pub const Size = enum(usize) {
|
||||
@"1GiB" = 0x40000000,
|
||||
@"2MiB" = 0x200000,
|
||||
@"4KiB" = 0x1000,
|
||||
};
|
||||
|
||||
pub const PageTable = extern struct {
|
||||
entries: [512]EntryValue,
|
||||
|
||||
pub const EntryValue = BitFields(packed struct(u64) {
|
||||
present : bool = false,
|
||||
writable : bool = false,
|
||||
user_accessible : bool = false,
|
||||
write_through : bool = false,
|
||||
no_cache : bool = false,
|
||||
accessed : bool = false,
|
||||
dirty : bool = false,
|
||||
huge_page : bool = false,
|
||||
global : bool = false,
|
||||
bits : u50 = 0,
|
||||
protection_key : u4 = 0,
|
||||
no_execute : bool = false,
|
||||
});
|
||||
|
||||
pub fn Entry(comptime size: Size) type {
|
||||
return extern struct {
|
||||
value: EntryValue,
|
||||
|
||||
pub fn init(value: *EntryValue) *@This() {
|
||||
return @ptrCast(value);
|
||||
}
|
||||
|
||||
pub fn frame(self: *@This()) PhysicalAddress {
|
||||
const base = switch (size) {
|
||||
.@"1GiB" => self.value.bits() & 0x000f_ffff_ffff_e000,
|
||||
.@"2MiB" => self.value.bits() & 0x000f_ffff_ffff_e000,
|
||||
.@"4KiB" => self.value.bits() & 0x000f_ffff_ffff_f000,
|
||||
};
|
||||
return .init(alignDown(base, @intFromEnum(size)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get(self: *@This(), index: usize) *EntryValue {
|
||||
return &self.entries[index];
|
||||
}
|
||||
};
|
||||
|
||||
pub const Allocator = struct {
|
||||
var fallback = root.machine.memory.Allocator.allocator();
|
||||
var head4KiB: Node = undefined;
|
||||
var head2MiB: Node = undefined;
|
||||
var head1GiB: Node = undefined;
|
||||
|
||||
const Node = struct {
|
||||
next: ?*Node,
|
||||
};
|
||||
|
||||
pub fn allocator() std.mem.Allocator {
|
||||
return .{
|
||||
.ptr = undefined,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn alloc(_: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 {
|
||||
_ = len;
|
||||
_ = alignment;
|
||||
_ = ret_addr;
|
||||
return null;
|
||||
}
|
||||
|
||||
fn resize(_: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
|
||||
_ = memory;
|
||||
_ = alignment;
|
||||
_ = new_len;
|
||||
_ = ret_addr;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn remap(_: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
|
||||
_ = memory;
|
||||
_ = alignment;
|
||||
_ = new_len;
|
||||
_ = ret_addr;
|
||||
return null;
|
||||
}
|
||||
|
||||
fn free(_: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void {
|
||||
_ = memory;
|
||||
_ = alignment;
|
||||
_ = ret_addr;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn translate(virt: VirtualAddress) error{HugePage, NotPresent}!PhysicalAddress {
|
||||
const bits = [_]u6{0, 12, 21, 30, 39, 48};
|
||||
const page_table_address = root.arch.cr3();
|
||||
|
||||
var level: usize = if (mode == .five_level) 5 else 4;
|
||||
var address: PhysicalAddress = .init(page_table_address);
|
||||
|
||||
while (level > 0) : (level -= 1) {
|
||||
const table = address.cast(*PageTable);
|
||||
|
||||
const index: u9 = @truncate(virt.base >> bits[level]);
|
||||
const entry = table.get(index);
|
||||
|
||||
if (!entry.fields.present) {
|
||||
return error.NotPresent;
|
||||
}
|
||||
|
||||
if (entry.fields.huge_page) switch (level) {
|
||||
3 => {
|
||||
const phys = PageTable.Entry(Size.@"1GiB").init(entry).frame();
|
||||
const offset: u30 = @truncate(virt.base);
|
||||
|
||||
return .{ .base = phys.base + offset };
|
||||
},
|
||||
2 => {
|
||||
const phys = PageTable.Entry(Size.@"2MiB").init(entry).frame();
|
||||
const offset: u21 = @truncate(virt.base);
|
||||
|
||||
return .{ .base = phys.base + offset };
|
||||
},
|
||||
else => return error.HugePage,
|
||||
};
|
||||
|
||||
address = PageTable.Entry(Size.@"4KiB").init(entry).frame();
|
||||
}
|
||||
|
||||
const phys = address;
|
||||
const offset: u12 = @truncate(virt.base);
|
||||
|
||||
return .{ .base = phys.base + offset };
|
||||
}
|
||||
164
src/arch/x86_64/segmentation.zig
Normal file
164
src/arch/x86_64/segmentation.zig
Normal file
@ -0,0 +1,164 @@
|
||||
const std = @import("std");
|
||||
const libk = @import("libk");
|
||||
const root = @import("root");
|
||||
|
||||
const BitFields = libk.utils.BitFields;
|
||||
const PrivilegeLevel = root.arch.PrivilegeLevel;
|
||||
|
||||
pub const SegmentSelector = BitFields(packed struct(u16) {
|
||||
privilege_level : PrivilegeLevel,
|
||||
table_indicator : u2,
|
||||
index : u12
|
||||
});
|
||||
|
||||
pub const SegmentDescriptor = BitFields(packed struct(u64) {
|
||||
limit_0 : u16,
|
||||
base_0 : u24,
|
||||
access : u8,
|
||||
limit_1 : u4,
|
||||
flags : u4,
|
||||
base_1 : u8,
|
||||
});
|
||||
|
||||
pub const SystemSegmentDescriptor = extern struct {
|
||||
lower: SegmentDescriptor,
|
||||
upper: u64,
|
||||
|
||||
pub const Access = BitFields(packed struct(u8) {
|
||||
type : SegmentType,
|
||||
reserved : u1 = 0,
|
||||
privilege_level : PrivilegeLevel,
|
||||
present : bool = false,
|
||||
});
|
||||
|
||||
pub const Flags = BitFields(packed struct(u4) {
|
||||
granularity : bool = false,
|
||||
default_size : bool = false,
|
||||
long_mode : bool = false,
|
||||
reserved : u1 = 0,
|
||||
});
|
||||
|
||||
pub const SegmentType = enum(u4) {
|
||||
local_descriptor_table = 0x2,
|
||||
tss_available = 0x9,
|
||||
tss_busy = 0xb,
|
||||
};
|
||||
|
||||
pub fn init(limit: u32, base: u64, access: Access, flags: Flags) @This() {
|
||||
const lower: SegmentDescriptor = .init(.{
|
||||
.base_0 = @truncate(base),
|
||||
.base_1 = @truncate(base >> 24),
|
||||
.limit_0 = @truncate(limit),
|
||||
.limit_1 = @truncate(limit >> 16),
|
||||
.access = access.bits(),
|
||||
.flags = flags.bits(),
|
||||
});
|
||||
return .{
|
||||
.lower = lower,
|
||||
.upper = base >> 32,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn GlobalDescriptorTable(comptime M: usize) type {
|
||||
return extern struct {
|
||||
entries: ?*Entries = null,
|
||||
len: usize = 1,
|
||||
|
||||
pub const Max = M;
|
||||
pub const Entries = [Max]u64;
|
||||
|
||||
pub fn read(len: usize) @This() {
|
||||
const pointer = root.arch.sgdt();
|
||||
const entries: *Entries = @ptrFromInt(pointer.base);
|
||||
|
||||
return .{ .entries = entries, .len = len };
|
||||
}
|
||||
|
||||
pub fn append(self: *@This(), value: SystemSegmentDescriptor) void {
|
||||
self.push(value.lower.bits());
|
||||
self.push(value.upper);
|
||||
}
|
||||
|
||||
pub fn push(self: *@This(), entry: u64) void {
|
||||
if (self.len >= Max) {
|
||||
std.debug.panic("exceeded maximum GDT entry limit ({})", .{Max});
|
||||
}
|
||||
self.entries.?[self.len] = entry;
|
||||
self.len += 1;
|
||||
}
|
||||
|
||||
pub fn load(self: *@This()) void {
|
||||
root.arch.lgdt(.{
|
||||
.limit = @sizeOf(Entries) - 1,
|
||||
.base = @intFromPtr(self.entries.?)
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const TaskStateSegment = extern struct {
|
||||
reserved_0: u32 = 0,
|
||||
privilege_stack_table: [3]u64 = undefined,
|
||||
reserved_1: u64 = 0,
|
||||
interrupt_stack_table: [7]u64 = undefined,
|
||||
reserved_2: u64 = 0,
|
||||
reserved_3: u16 = 0,
|
||||
iomap_base: u16 = @sizeOf(@This()),
|
||||
|
||||
pub fn getDescriptor(self: *@This()) SystemSegmentDescriptor {
|
||||
const limit = @sizeOf(@This()) - 1;
|
||||
const base = @intFromPtr(self);
|
||||
|
||||
const access: SystemSegmentDescriptor.Access = .init(.{
|
||||
.type = .tss_available,
|
||||
.privilege_level = .ring0,
|
||||
.present = true
|
||||
});
|
||||
|
||||
return .init(limit, base, access, .init(.{}));
|
||||
}
|
||||
};
|
||||
|
||||
pub const CodeSegment = struct {
|
||||
pub inline fn set(selector: SegmentSelector) void {
|
||||
asm volatile (
|
||||
\\pushq %[a]
|
||||
\\leaq 1f(%rip), %rax
|
||||
\\pushq %rax
|
||||
\\lretq
|
||||
\\1:
|
||||
:
|
||||
: [a] "r" (selector.bits())
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn get() SegmentSelector {
|
||||
return asm volatile (
|
||||
\\mov %%cs, %[ret]
|
||||
: [ret] "=&r" (-> SegmentSelector),
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
pub var gdt: GlobalDescriptorTable(9) = .{};
|
||||
pub var tss: TaskStateSegment = .{};
|
||||
|
||||
pub fn init() void {
|
||||
// at this point, the GDT has been loaded with 7 entries;
|
||||
// see <https://github.com/limine-bootloader/limine/blob/v8.x/PROTOCOL.md#x86-64-1>
|
||||
gdt = .read(7);
|
||||
|
||||
// setup double fault IST
|
||||
tss.interrupt_stack_table[0] = block: {
|
||||
const size = 4096 * 5; // 20 kiB
|
||||
const stack = [_]u8{0} ** size;
|
||||
|
||||
break :block @intFromPtr(&stack);
|
||||
};
|
||||
|
||||
gdt.append(tss.getDescriptor());
|
||||
gdt.load();
|
||||
}
|
||||
181
src/arch/x86_64/x86_64.zig
Executable file
181
src/arch/x86_64/x86_64.zig
Executable file
@ -0,0 +1,181 @@
|
||||
const root = @import("root");
|
||||
|
||||
pub const cpuid = @import("cpuid.zig");
|
||||
pub const debug = @import("debug.zig");
|
||||
pub const paging = @import("paging.zig");
|
||||
pub const machine = @import("machine/machine.zig");
|
||||
pub const interrupt = @import("interrupt/interrupt.zig");
|
||||
pub const segmentation = @import("segmentation.zig");
|
||||
|
||||
pub const DescriptorTablePointer = packed struct {
|
||||
limit: u16,
|
||||
base: u64,
|
||||
};
|
||||
|
||||
pub const PrivilegeLevel = enum(u2) {
|
||||
ring0 = 0,
|
||||
ring1 = 1,
|
||||
ring2 = 2,
|
||||
ring3 = 3,
|
||||
};
|
||||
|
||||
pub const ModelSpecificRegister = enum(u32) {
|
||||
apic_base = 0x01B,
|
||||
tsc_dead_line = 0x6E0,
|
||||
|
||||
pub inline fn read(self: @This()) u64 {
|
||||
var lo: u32 = 0;
|
||||
var hi: u32 = 0;
|
||||
|
||||
asm volatile (
|
||||
\\rdmsr
|
||||
: [lo] "={eax}" (lo),
|
||||
[hi] "={edx}" (hi),
|
||||
: [msr] "{ecx}" (@intFromEnum(self))
|
||||
);
|
||||
|
||||
const array: [2]u32 = .{lo, hi};
|
||||
const result: u64 = @bitCast(array);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub inline fn write(self: @This(), value: u64) void {
|
||||
const lo: u32 = @truncate(value);
|
||||
const hi: u32 = @truncate(value >> 32);
|
||||
asm volatile (
|
||||
\\wrmsr
|
||||
:
|
||||
: [lo] "{eax}" (lo),
|
||||
[hi] "{edx}" (hi),
|
||||
[msr] "{ecx}" (@intFromEnum(self)),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn Pio(comptime T: type) type {
|
||||
return struct {
|
||||
address: u16,
|
||||
|
||||
pub fn open(port: u16) @This() {
|
||||
return .{ .address = port };
|
||||
}
|
||||
|
||||
pub fn read(self: *@This()) T {
|
||||
return in(T, self.address);
|
||||
}
|
||||
|
||||
pub fn write(self: *@This(), data: T) void {
|
||||
out(self.address, data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn halt() void {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
|
||||
pub inline fn int3() void {
|
||||
asm volatile ("int3");
|
||||
}
|
||||
|
||||
pub inline fn lidt(pointer: DescriptorTablePointer) void {
|
||||
asm volatile (
|
||||
\\lidt %[ptr]
|
||||
:
|
||||
: [ptr] "*p" (&pointer),
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn sidt() DescriptorTablePointer {
|
||||
var pointer
|
||||
: DescriptorTablePointer
|
||||
= undefined;
|
||||
|
||||
asm volatile (
|
||||
\\sidt %[ptr]
|
||||
: [ptr] "=m" (pointer)
|
||||
);
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
pub inline fn lgdt(pointer: DescriptorTablePointer) void {
|
||||
asm volatile (
|
||||
\\lgdt %[ptr]
|
||||
:
|
||||
: [ptr] "*p" (&pointer),
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn sgdt() DescriptorTablePointer {
|
||||
var pointer
|
||||
: DescriptorTablePointer
|
||||
= undefined;
|
||||
|
||||
asm volatile (
|
||||
\\sgdt %[ptr]
|
||||
: [ptr] "=m" (pointer)
|
||||
);
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
pub inline fn cr2() u64 {
|
||||
return asm volatile (
|
||||
\\mov %%cr2, %[ret]
|
||||
: [ret] "=&r" (-> u64)
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn cr3() u64 {
|
||||
return asm volatile (
|
||||
\\mov %%cr3, %[ret]
|
||||
: [ret] "=&r" (-> u64)
|
||||
);
|
||||
}
|
||||
|
||||
pub inline fn in(comptime T: type, port: u16) T {
|
||||
return switch (T) {
|
||||
u8 => asm volatile (
|
||||
\\inb %[port], %[ret]
|
||||
: [ret] "={al}" (-> T),
|
||||
: [port] "{dx}" (port),
|
||||
),
|
||||
u16 => asm volatile (
|
||||
\\inw %[port], %[ret]
|
||||
: [ret] "={ax}" (-> T),
|
||||
: [port] "{dx}" (port),
|
||||
),
|
||||
u32 => asm volatile (
|
||||
\\inl %[port], %[ret]
|
||||
: [ret] "={eax}" (-> T),
|
||||
: [port] "{dx}" (port),
|
||||
),
|
||||
else => @compileError("Invalid data type: " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn out(port: u16, data: anytype) void {
|
||||
switch (@TypeOf(data)) {
|
||||
u8 => asm volatile (
|
||||
\\outb %[data], %[port]
|
||||
:
|
||||
: [port] "{dx}" (port),
|
||||
[data] "{al}" (data),
|
||||
),
|
||||
u16 => asm volatile (
|
||||
\\outw %[data], %[port]
|
||||
:
|
||||
: [port] "{dx}" (port),
|
||||
[data] "{ax}" (data),
|
||||
),
|
||||
u32 => asm volatile (
|
||||
\\outl %[data], %[port]
|
||||
:
|
||||
: [port] "{dx}" (port),
|
||||
[data] "{eax}" (data),
|
||||
),
|
||||
else => |T| @compileError("Invalid data type: " ++ @typeName(T)),
|
||||
}
|
||||
}
|
||||
10
src/debug.zig
Executable file
10
src/debug.zig
Executable file
@ -0,0 +1,10 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root");
|
||||
|
||||
pub fn abort() noreturn {
|
||||
while(true) root.arch.halt();
|
||||
}
|
||||
|
||||
pub fn print(comptime fmt: []const u8, args: anytype) void {
|
||||
std.fmt.format(root.arch.debug.writer(), fmt, args) catch unreachable;
|
||||
}
|
||||
267
src/machine/acpi/acpi.zig
Normal file
267
src/machine/acpi/acpi.zig
Normal file
@ -0,0 +1,267 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root");
|
||||
const uacpi = @import("uacpi");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub var rsdp: ?usize = null;
|
||||
pub var boot_time: ?isize = null;
|
||||
|
||||
export fn uacpi_kernel_get_rsdp(out_rsdp_address: *uacpi.PhysAddr) uacpi.Status {
|
||||
if (rsdp) |address| {
|
||||
out_rsdp_address.* = address;
|
||||
return .ok;
|
||||
}
|
||||
return .internal_error;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_pci_device_open(address: uacpi.PciAddress, out_handle: *anyopaque) uacpi.Status {
|
||||
_ = address;
|
||||
_ = out_handle;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_pci_device_close(out_handle: *anyopaque) void {
|
||||
_ = out_handle;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_pci_read8(device: *anyopaque, offset: usize, value: *u8) uacpi.Status {
|
||||
_ = device;
|
||||
_ = offset;
|
||||
_ = value;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_pci_read16(device: *anyopaque, offset: usize, value: *u16) uacpi.Status {
|
||||
_ = device;
|
||||
_ = offset;
|
||||
_ = value;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_pci_read32(device: *anyopaque, offset: usize, value: *u32) uacpi.Status {
|
||||
_ = device;
|
||||
_ = offset;
|
||||
_ = value;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_pci_write8(device: *anyopaque, offset: usize, value: u8) uacpi.Status {
|
||||
_ = device;
|
||||
_ = offset;
|
||||
_ = value;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_pci_write16(device: *anyopaque, offset: usize, value: u16) uacpi.Status {
|
||||
_ = device;
|
||||
_ = offset;
|
||||
_ = value;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_pci_write32(device: *anyopaque, offset: usize, value: u32) uacpi.Status {
|
||||
_ = device;
|
||||
_ = offset;
|
||||
_ = value;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_io_map(base: u64, _: usize, out_handle: **anyopaque) uacpi.Status {
|
||||
if (builtin.cpu.arch == .x86_64) {
|
||||
out_handle.* = @ptrFromInt(base);
|
||||
return .ok;
|
||||
}
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_io_unmap(handle: *anyopaque) void {
|
||||
_ = handle;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_io_read8(handle: *anyopaque, offset: usize, value: *u8) uacpi.Status {
|
||||
if (builtin.cpu.arch == .x86_64) {
|
||||
const port: u16 = @truncate(@intFromPtr(handle) + offset);
|
||||
const output = root.arch.in(u8, port);
|
||||
|
||||
value.* = output;
|
||||
return .ok;
|
||||
}
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_io_read16(handle: *anyopaque, offset: usize, value: *u16) uacpi.Status {
|
||||
if (builtin.cpu.arch == .x86_64) {
|
||||
const port: u16 = @truncate(@intFromPtr(handle) + offset);
|
||||
const output = root.arch.in(u16, port);
|
||||
|
||||
value.* = output;
|
||||
return .ok;
|
||||
}
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_io_read32(handle: *anyopaque, offset: usize, value: *u32) uacpi.Status {
|
||||
if (builtin.cpu.arch == .x86_64) {
|
||||
const port: u16 = @truncate(@intFromPtr(handle) + offset);
|
||||
const output = root.arch.in(u32, port);
|
||||
|
||||
value.* = output;
|
||||
return .ok;
|
||||
}
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_io_write8(handle: *anyopaque, offset: usize, value: u8) uacpi.Status {
|
||||
if (builtin.cpu.arch == .x86_64) {
|
||||
root.arch.out(@truncate(@intFromPtr(handle) + offset), value);
|
||||
return .ok;
|
||||
}
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_io_write16(handle: *anyopaque, offset: usize, value: u16) uacpi.Status {
|
||||
if (builtin.cpu.arch == .x86_64) {
|
||||
root.arch.out(@truncate(@intFromPtr(handle) + offset), value);
|
||||
return .ok;
|
||||
}
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_io_write32(handle: *anyopaque, offset: usize, value: u32) uacpi.Status {
|
||||
if (builtin.cpu.arch == .x86_64) {
|
||||
root.arch.out(@truncate(@intFromPtr(handle) + offset), value);
|
||||
return .ok;
|
||||
}
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_map(addr: uacpi.PhysAddr, len: usize) [*]u8 {
|
||||
_ = addr;
|
||||
_ = len;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_unmap(addr: [*]u8, len: usize) void {
|
||||
_ = addr;
|
||||
_ = len;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_alloc(size: usize) ?[*]u8 {
|
||||
_ = size;
|
||||
return null;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_free(opt_mem: ?[*]u8) void {
|
||||
_ = opt_mem;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_log(uacpi_log_level: uacpi.LogLevel, msg: [*:0]const u8) void {
|
||||
_ = uacpi_log_level;
|
||||
_ = msg;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_get_nanoseconds_since_boot() u64 {
|
||||
const then: usize = if (boot_time != null and boot_time.? > 0) @intCast(boot_time.?) else 0;
|
||||
const now = root.arch.machine.time.Rtc.getUnixTimestamp();
|
||||
|
||||
return (now - then) * 1000000000;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_stall(usec: u8) void {
|
||||
_ = usec;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_sleep(msec: u64) void {
|
||||
_ = msec;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_create_mutex() *anyopaque {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_free_mutex(mutex: *anyopaque) void {
|
||||
_ = mutex;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_create_event() *anyopaque {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_free_event(handle: *anyopaque) void {
|
||||
_ = handle;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_get_thread_id() uacpi.ThreadId {
|
||||
return null;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_acquire_mutex(mutex: *anyopaque, timeout: u16) uacpi.Status {
|
||||
_ = mutex;
|
||||
_ = timeout;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_release_mutex(mutex: *anyopaque) void {
|
||||
_ = mutex;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_wait_for_event(handle: *anyopaque, timeout: u16) bool {
|
||||
_ = handle;
|
||||
_ = timeout;
|
||||
return false;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_signal_event(handle: *anyopaque) void {
|
||||
_ = handle;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_reset_event(handle: *anyopaque) void {
|
||||
_ = handle;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_handle_firmware_request(request: *const anyopaque) uacpi.Status {
|
||||
_ = request;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_install_interrupt_handler(irq: u32, handler: uacpi.InterruptHandler, ctx: *anyopaque, out_irq_handle: **anyopaque) uacpi.Status {
|
||||
_ = irq;
|
||||
_ = handler;
|
||||
_ = ctx;
|
||||
_ = out_irq_handle;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_uninstall_interrupt_handler(handler: uacpi.InterruptHandler, irq_handle: *anyopaque) uacpi.Status {
|
||||
_ = handler;
|
||||
_ = irq_handle;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_create_spinlock() *anyopaque {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_free_spinlock(spinlock: *anyopaque) void {
|
||||
_ = spinlock;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_lock_spinlock(spinlock: *anyopaque) uacpi.CpuFlags {
|
||||
_ = spinlock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_unlock_spinlock(spinlock: *anyopaque) void {
|
||||
_ = spinlock;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_schedule_work(work_type: uacpi.WorkType, handler: uacpi.WorkHandler, ctx: *anyopaque) uacpi.Status {
|
||||
_ = work_type;
|
||||
_ = handler;
|
||||
_ = ctx;
|
||||
return .unimplemented;
|
||||
}
|
||||
|
||||
export fn uacpi_kernel_wait_for_work_completion() void {
|
||||
std.debug.panic("uacpi: waiting for work completion\n", .{});
|
||||
}
|
||||
93
src/machine/framebuffer/Console.zig
Executable file
93
src/machine/framebuffer/Console.zig
Executable file
@ -0,0 +1,93 @@
|
||||
const root = @import("root");
|
||||
const limine = @import("limine");
|
||||
|
||||
const Mmio = root.machine.memory.Mmio;
|
||||
const Framebuffer = limine.Framebuffer;
|
||||
|
||||
const BitmapFont = struct {
|
||||
height: usize,
|
||||
width: usize,
|
||||
size: usize,
|
||||
data: []const u8,
|
||||
};
|
||||
|
||||
font: BitmapFont,
|
||||
framebuffer: *Framebuffer,
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
|
||||
pub const Color = u32;
|
||||
pub var Instance: @This() = undefined;
|
||||
|
||||
pub fn init(framebuffer: *Framebuffer) void {
|
||||
const font = BitmapFont {
|
||||
.height = 16,
|
||||
.width = 8,
|
||||
.size = 16,
|
||||
.data = @embedFile("bitmap.bin"),
|
||||
};
|
||||
|
||||
Instance = .{
|
||||
.font = font,
|
||||
.framebuffer = framebuffer,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn write(self: *@This(), data: []const u8) void {
|
||||
for (data) |byte| {
|
||||
// move to the next row (in full characters)
|
||||
if (byte == '\n' or self.x >= self.framebuffer.width) {
|
||||
self.y += self.font.height;
|
||||
self.x = 0;
|
||||
}
|
||||
|
||||
// scroll the screen by one row (in full characters)
|
||||
if (self.y >= self.framebuffer.height) {
|
||||
const height = self.framebuffer.height;
|
||||
const width = self.framebuffer.width;
|
||||
|
||||
const offset = self.font.height * width;
|
||||
const size = height * width - offset;
|
||||
|
||||
const address = self.framebuffer.address;
|
||||
const pointer: [*]Color = @ptrCast(@alignCast(address));
|
||||
|
||||
// transpose data
|
||||
for (0..size) |i| {
|
||||
pointer[i] = pointer[i + offset];
|
||||
}
|
||||
|
||||
// clear the rest of the screen
|
||||
for (size..size + offset) |i| {
|
||||
pointer[i] = 0;
|
||||
}
|
||||
|
||||
self.y = height - self.font.height;
|
||||
}
|
||||
|
||||
if (byte != '\n') {
|
||||
self.draw(byte, 0xffffffff);
|
||||
self.x += self.font.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(self: *@This(), char: u8, color: Color) void {
|
||||
const address = self.framebuffer.address;
|
||||
const pointer: [*]Color = @ptrCast(@alignCast(address));
|
||||
|
||||
const font = self.font.data[char * self.font.size..];
|
||||
const mask = [_]u8{128, 64, 32, 16, 8, 4, 2, 1};
|
||||
|
||||
var offset = self.y * self.framebuffer.width + self.x;
|
||||
|
||||
for (0..self.font.height) |row| {
|
||||
for (0..self.font.width) |col| {
|
||||
if (font[row] & mask[col] > 0) {
|
||||
pointer[col + offset] = color;
|
||||
}
|
||||
}
|
||||
// Move to the next row (in pixels)
|
||||
offset += self.framebuffer.width;
|
||||
}
|
||||
}
|
||||
BIN
src/machine/framebuffer/bitmap.bin
Executable file
BIN
src/machine/framebuffer/bitmap.bin
Executable file
Binary file not shown.
50
src/machine/machine.zig
Normal file
50
src/machine/machine.zig
Normal file
@ -0,0 +1,50 @@
|
||||
const std = @import("std");
|
||||
const root = @import("root");
|
||||
const limine = @import("limine");
|
||||
|
||||
pub const pci = @import("pci.zig");
|
||||
pub const acpi = @import("acpi/acpi.zig");
|
||||
pub const memory = @import("memory/memory.zig");
|
||||
pub const Console = @import("framebuffer/Console.zig");
|
||||
|
||||
export var base_revision: limine.BaseRevision = .{ .revision = 2 };
|
||||
export var framebuffer_request: limine.Framebuffer.Request = .{};
|
||||
export var memory_map_request: limine.MemoryMap.Request = .{};
|
||||
export var boot_time_request: limine.BootTime.Request = .{};
|
||||
export var hhdm_request: limine.Hhdm.Request = .{};
|
||||
export var rsdp_request: limine.Rsdp.Request = .{};
|
||||
|
||||
pub fn init() callconv(.C) noreturn {
|
||||
// Ensure the bootloader actually understands our base revision.
|
||||
if (!base_revision.is_supported()) {
|
||||
root.debug.abort();
|
||||
}
|
||||
|
||||
if (framebuffer_request.response) |res| {
|
||||
if (res.framebuffer_count > 0) {
|
||||
Console.init(res.getFramebuffers()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (rsdp_request.response) |res| {
|
||||
acpi.rsdp = res.address;
|
||||
}
|
||||
|
||||
if (boot_time_request.response) |res| {
|
||||
acpi.boot_time = res.boot_time;
|
||||
}
|
||||
|
||||
if (hhdm_request.response) |res| {
|
||||
memory.offset = res.offset;
|
||||
}
|
||||
|
||||
if (memory_map_request.response) |res| {
|
||||
memory.map = res.getEntries();
|
||||
}
|
||||
|
||||
memory.init();
|
||||
//memory.manager = .init();
|
||||
|
||||
root.arch.machine.init();
|
||||
std.debug.panic("System halted.\n", .{});
|
||||
}
|
||||
114
src/machine/memory/heap.zig
Normal file
114
src/machine/memory/heap.zig
Normal file
@ -0,0 +1,114 @@
|
||||
pub const Allocator = struct {
|
||||
var head: Node = .{
|
||||
.size = 0,
|
||||
.next = null,
|
||||
};
|
||||
|
||||
const Node = struct {
|
||||
size: usize,
|
||||
next: ?*Node,
|
||||
|
||||
fn start(self: *@This()) VirtAddress {
|
||||
return .{ .address = @intFromPtr(self) };
|
||||
}
|
||||
|
||||
fn end(self: *@This()) VirtAddress {
|
||||
return .{ .address = self.start().address + self.size };
|
||||
}
|
||||
};
|
||||
|
||||
const Layout = struct {
|
||||
alignment: usize,
|
||||
size: usize,
|
||||
|
||||
fn init(alignment: std.mem.Alignment, len: usize) @This() {
|
||||
const adjusted = alignment.max(.fromByteUnits(@alignOf(Node)));
|
||||
const mask = adjusted.toByteUnits() - 1;
|
||||
const size = (len + mask) & ~mask;
|
||||
|
||||
return .{
|
||||
.alignment = adjusted.toByteUnits(),
|
||||
.size = @max(size, @sizeOf(Node)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn append(address: VirtualAddress, size: usize) void {
|
||||
const base: usize = address.toAddr();
|
||||
const node: *Node = @ptrFromInt(base);
|
||||
|
||||
node.size = size;
|
||||
node.next = head.next;
|
||||
head.next = node;
|
||||
}
|
||||
|
||||
pub fn allocator() std.mem.Allocator {
|
||||
return .{
|
||||
.ptr = undefined,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn alloc(_: *anyopaque, len: usize, alignment: std.mem.Alignment, _: usize) ?[*]u8 {
|
||||
const layout: Layout = .init(alignment, len);
|
||||
var current: ?*Node = &head;
|
||||
|
||||
while (current.?.next != null) : (current = current.?.next) {
|
||||
const region = current.?.next.?;
|
||||
|
||||
// check if size too small
|
||||
if (region.size < layout.size) continue;
|
||||
const start = common.utils.alignUp(region.start().address, layout.alignment);
|
||||
|
||||
// check if size too large
|
||||
const added = @addWithOverflow(start, layout.size);
|
||||
const end = if (added[1] == 0) added[0] else continue;
|
||||
|
||||
// check if region too small
|
||||
const subed = @subWithOverflow(region.end().address, end);
|
||||
const excess = if (subed[1] == 0) subed[0] else continue;
|
||||
|
||||
// Try split the region into two when there's excessive space.
|
||||
// Go to the next node if the unobtained part is too small for another node.
|
||||
if (excess > 0) {
|
||||
if (excess < @sizeOf(Node)) continue;
|
||||
append(.{ .address = end }, excess);
|
||||
}
|
||||
|
||||
// remove region from the list
|
||||
current.?.next = region.next;
|
||||
|
||||
return @ptrFromInt(start);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn resize(_: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool {
|
||||
_ = memory;
|
||||
_ = alignment;
|
||||
_ = new_len;
|
||||
_ = ret_addr;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn remap(_: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
|
||||
_ = memory;
|
||||
_ = alignment;
|
||||
_ = new_len;
|
||||
_ = ret_addr;
|
||||
return null;
|
||||
}
|
||||
|
||||
fn free(_: *anyopaque, memory: []u8, alignment: std.mem.Alignment, _: usize) void {
|
||||
const layout: Layout = .init(alignment, memory.len);
|
||||
const address: VirtAddress = .{ .address = @intFromPtr(memory.ptr) };
|
||||
|
||||
append(address, layout.size);
|
||||
}
|
||||
};
|
||||
57
src/machine/memory/logical.zig
Normal file
57
src/machine/memory/logical.zig
Normal file
@ -0,0 +1,57 @@
|
||||
const root = @import("root");
|
||||
const libk = @import("libk");
|
||||
|
||||
const PhysicalAddress = root.machine.memory.PhysicalAddress;
|
||||
const VirtualAddress = root.machine.memory.VirtualAddress;
|
||||
|
||||
pub const MemoryManager = struct {
|
||||
head: ?*Node = null,
|
||||
|
||||
pub const Node = struct {
|
||||
base: *anyopaque,
|
||||
length: usize,
|
||||
flags: Flags,
|
||||
next: *@This(),
|
||||
};
|
||||
|
||||
pub const Flags = libk.utils.BitFields(packed struct(usize) {
|
||||
writable: bool,
|
||||
executable: bool,
|
||||
user_accessible: bool,
|
||||
});
|
||||
|
||||
pub fn init() @This() {
|
||||
|
||||
}
|
||||
|
||||
pub fn deinit() void {
|
||||
|
||||
}
|
||||
|
||||
pub fn allocate(self: *@This(), length: usize, flags: Flags) void {
|
||||
_ = self;
|
||||
_ = length;
|
||||
_ = flags;
|
||||
}
|
||||
|
||||
pub fn free(self: *@This(), address: *anyopaque) void {
|
||||
_ = self;
|
||||
_ = address;
|
||||
}
|
||||
|
||||
pub fn map(self: *@This(), virt: VirtualAddress, phys: PhysicalAddress, length: usize) void {
|
||||
_ = self;
|
||||
_ = phys;
|
||||
_ = virt;
|
||||
_ = length;
|
||||
}
|
||||
|
||||
fn insertNode(self: *@This(), address: VirtualAddress, length: usize) void {
|
||||
var current: ?*Node = self.head;
|
||||
var last: ?*Node = null;
|
||||
|
||||
while (current) |node| {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
123
src/machine/memory/memory.zig
Normal file
123
src/machine/memory/memory.zig
Normal file
@ -0,0 +1,123 @@
|
||||
const std = @import("std");
|
||||
const libk = @import("libk");
|
||||
const root = @import("root");
|
||||
const limine = @import("limine");
|
||||
|
||||
//pub const heap = @import("heap.zig");
|
||||
//pub const logical = @import("logical.zig");
|
||||
|
||||
pub var map: ?[]*limine.MemoryMap.Entry = null;
|
||||
pub var offset: ?usize = null;
|
||||
//pub var manager: logical.MemoryManager = .{};
|
||||
|
||||
pub const Error = error {
|
||||
NotAvailable,
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
pub const PhysicalAddress = struct {
|
||||
/// base address
|
||||
base: usize,
|
||||
|
||||
pub fn init(address: usize) @This() {
|
||||
return .{ .base = address };
|
||||
}
|
||||
|
||||
pub fn cast(self: *const @This(), comptime T: type) T {
|
||||
return self.toVirtual().cast(T);
|
||||
}
|
||||
|
||||
pub inline fn toVirtual(self: *const @This()) VirtualAddress {
|
||||
return .{ .base = self.base + offset.? };
|
||||
}
|
||||
};
|
||||
|
||||
pub const VirtualAddress = struct {
|
||||
/// base address
|
||||
base: usize,
|
||||
|
||||
pub fn init(address: usize) @This() {
|
||||
return .{ .base = address };
|
||||
}
|
||||
|
||||
pub fn cast(self: *const @This(), comptime T: type) T {
|
||||
return @ptrFromInt(self.base);
|
||||
}
|
||||
|
||||
pub inline fn toPhysical(self: *const @This()) PhysicalAddress {
|
||||
return .{ .base = self.base - offset.? };
|
||||
}
|
||||
};
|
||||
|
||||
pub var frames: ?[*]u8 = null;
|
||||
pub var length: usize = 0;
|
||||
|
||||
pub fn init() void {
|
||||
var total: usize = 0;
|
||||
var limit: usize = 0;
|
||||
|
||||
var max_size: usize = 0;
|
||||
var max_size_index: usize = 0;
|
||||
|
||||
while (limit < map.?.len) : (limit += 1) {
|
||||
const entry = map.?[limit];
|
||||
const size = entry.length;
|
||||
|
||||
if (entry.type == .usable and size > max_size) {
|
||||
max_size = size;
|
||||
max_size_index = limit;
|
||||
}
|
||||
|
||||
total += size;
|
||||
}
|
||||
|
||||
if (limit == 0) {
|
||||
std.debug.panic("memory map has no entry", .{});
|
||||
}
|
||||
|
||||
const tail = map.?[max_size_index];
|
||||
const size: usize = total / @intFromEnum(root.arch.paging.frame_size);
|
||||
const address: usize = tail.base + (tail.length - size);
|
||||
|
||||
// allocate space for storing usage data of physical frames
|
||||
frames = PhysicalAddress.init(address).cast([*]u8);
|
||||
length = size;
|
||||
|
||||
for (map.?) |entry| {
|
||||
// mark all memory that is not usable as busy
|
||||
if (entry.type != .usable) {
|
||||
const start: usize = entry.base;
|
||||
const end: usize = start + entry.length;
|
||||
|
||||
for (indexOf(.init(start))..indexOf(.init(end))) |i| {
|
||||
frames.?[i] = .busy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (indexOf(.init(address))..indexOf(.init(address + size))) |i| {
|
||||
// mark where frame data live as allocated, too
|
||||
frames.?[i] = .busy;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn locationOf(address: PhysicalAddress) struct {usize, u6} {
|
||||
const location = address.base / @intFromEnum(root.arch.paging.frame_size);
|
||||
const row: usize = location / 8;
|
||||
const column: u6 = location % 8;
|
||||
|
||||
return .{ row, column };
|
||||
}
|
||||
|
||||
pub fn addressOf(index: usize) usize {
|
||||
return index * @intFromEnum(root.arch.paging.frame_size);
|
||||
}
|
||||
|
||||
pub fn setFrame(address: PhysicalAddress, size: usize, value: bool) void {
|
||||
const location = locationOf(address);
|
||||
const row =
|
||||
}
|
||||
|
||||
pub fn getFrame(address: PhysicalAddress) bool {
|
||||
const location = locationOf(address);
|
||||
}
|
||||
0
src/machine/pci.zig
Normal file
0
src/machine/pci.zig
Normal file
146
src/main.zig
Executable file
146
src/main.zig
Executable file
@ -0,0 +1,146 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const arch = @import("arch/arch.zig");
|
||||
pub const debug = @import("debug.zig");
|
||||
pub const machine = @import("machine/machine.zig");
|
||||
|
||||
pub fn main() void {
|
||||
for (0..128) |i| {
|
||||
debug.print("{}: hello world\n", .{i});
|
||||
}
|
||||
for (0..256) |i| {
|
||||
debug.print("{s}", .{&[1]u8{@intCast(i)}});
|
||||
}
|
||||
|
||||
debug.print("\n", .{});
|
||||
// provoke a breakpoint exception
|
||||
arch.int3();
|
||||
|
||||
const addresses = [_]usize{
|
||||
// the identity-mapped vga buffer page
|
||||
0xb8000,
|
||||
// some code page
|
||||
0x201008,
|
||||
// some stack page
|
||||
0x0100_0020_1a10,
|
||||
// virtual address mapped to physical address 0
|
||||
0,
|
||||
};
|
||||
|
||||
for (addresses) |i| {
|
||||
const phys = machine.memory.PhysicalAddress.init(i);
|
||||
const virt = phys.toVirtual();
|
||||
|
||||
debug.print("phys: {!}\n", .{arch.paging.translate(virt)});
|
||||
}
|
||||
|
||||
for (machine.memory.map.?) |region| {
|
||||
debug.print("\n", .{});
|
||||
debug.print("base: {}\n", .{region.base});
|
||||
debug.print("length: {}\n", .{region.length});
|
||||
debug.print("type: {}\n", .{region.type});
|
||||
}
|
||||
|
||||
const virt: machine.memory.VirtualAddress = .init(0xffffffff8003ffff);
|
||||
const phys = arch.paging.translate(virt);
|
||||
debug.print("\n", .{});
|
||||
debug.print("{!}\n", .{phys});
|
||||
|
||||
const vendor = arch.cpuid.Vendor.get();
|
||||
debug.print("vendor: {s}\n", .{vendor.id});
|
||||
|
||||
//const features = arch.cpuid.Features.get();
|
||||
//debug.print("features: {}\n", .{features});
|
||||
|
||||
const bus_frequency = arch.cpuid.BusFrequency.get();
|
||||
debug.print("bus frequency: {}\n", .{bus_frequency});
|
||||
|
||||
const core_frequency = arch.cpuid.CoreFrequency.get();
|
||||
debug.print("core frequency: {}\n", .{core_frequency});
|
||||
|
||||
const apic_base = arch.ModelSpecificRegister.apic_base.read();
|
||||
debug.print("apic_base: 0x{x}\n", .{apic_base});
|
||||
|
||||
const apic_base_phys = machine.memory.PhysicalAddress.init(apic_base);
|
||||
const apic_base_virt = apic_base_phys.toVirtual();
|
||||
debug.print("apic_base virt: 0x{x}\n", .{apic_base_virt.base});
|
||||
|
||||
const apic_base_translated = arch.paging.translate(apic_base_virt);
|
||||
debug.print("apic_base translated: {!}\n", .{apic_base_translated});
|
||||
|
||||
debug.print("fin.\n", .{});
|
||||
|
||||
arch.interrupt.hardware.disable();
|
||||
//const time = arch.interrupt.hardware.Rtc.time();
|
||||
//debug.print("time: {}\n", .{time});
|
||||
arch.interrupt.hardware.enable();
|
||||
|
||||
var totalmem: usize = 0;
|
||||
for (machine.memory.map.?) |entry| {
|
||||
totalmem += entry.length;
|
||||
}
|
||||
debug.print("total memory: {}\n", .{totalmem});
|
||||
|
||||
var freemem: usize = 0;
|
||||
for (machine.memory.map.?) |entry| {
|
||||
if (entry.type == .usable) {
|
||||
freemem += entry.length;
|
||||
}
|
||||
}
|
||||
debug.print("free memory: {}\n", .{freemem});
|
||||
|
||||
debug.print("sleeping 2000 milliseconds...\n", .{});
|
||||
arch.machine.time.wait(2000);
|
||||
debug.print("done sleeping.\n", .{});
|
||||
|
||||
// disabled
|
||||
while (false) {
|
||||
//const allocator = machine.memory.Allocator.allocator();
|
||||
//var list = std.ArrayList(u8).init(allocator);
|
||||
|
||||
//while (true) {
|
||||
// list.append(42) catch break;
|
||||
//}
|
||||
|
||||
//if (list.items.len == 0) break;
|
||||
//debug.print("list items len: {}\n", .{list.items.len});
|
||||
//list.deinit();
|
||||
}
|
||||
|
||||
const boot_time = machine.acpi.boot_time.?;
|
||||
debug.print("boot time: {}\n", .{boot_time});
|
||||
|
||||
const timestamp = arch.machine.time.getUnixTimestampMs();
|
||||
debug.print("time: {}\n", .{timestamp});
|
||||
|
||||
// disabled
|
||||
while (false) {
|
||||
const rtc = arch.machine.time.Rtc.getUnixTimestamp();
|
||||
debug.print("RTC: {}\n", .{rtc});
|
||||
|
||||
const system_time = arch.machine.time.getUnixTimestampMs();
|
||||
debug.print("System time: {}\n\n", .{system_time});
|
||||
}
|
||||
|
||||
// trigger interrupt 32
|
||||
//asm volatile ("int $0x20");
|
||||
|
||||
//const elapsed = asm volatile (
|
||||
// \\call uacpi_kernel_get_nanoseconds_since_boot
|
||||
// : [ret] "=r" (-> u64)
|
||||
//);
|
||||
//debug.print("elapsed: {}\n", .{elapsed});
|
||||
|
||||
// provoke a page fault exception
|
||||
//const pointer: *align(1) u64 = @ptrFromInt(0xdeadbeaf);
|
||||
//pointer.* = 42;
|
||||
}
|
||||
|
||||
pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
|
||||
debug.print("Kernel panic: {s}\n", .{message});
|
||||
debug.abort();
|
||||
}
|
||||
|
||||
comptime {
|
||||
@export(&machine.init, .{ .name = "_start", .linkage = .strong });
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user