Initial commit

This commit is contained in:
ktkk 2025-09-19 18:46:01 +00:00
commit 5f2e19904d
9 changed files with 503 additions and 0 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.zig-cache/
zig-out/
.envrc
.direnv/

44
build.zig Normal file
View file

@ -0,0 +1,44 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.resolveTargetQuery(.{
.cpu_arch = std.Target.Cpu.Arch.x86,
.ofmt = std.Target.ObjectFormat.elf,
.abi = std.Target.Abi.none,
.os_tag = std.Target.Os.Tag.freestanding,
.cpu_features_sub = std.Target.x86.featureSet(&.{
.mmx,
.sse,
.sse2,
.avx,
.avx2,
}),
.cpu_features_add = std.Target.x86.featureSet(&.{
.soft_float,
}),
});
const optimize = b.standardOptimizeOption(.{});
const kernel = b.addExecutable(.{
.name = "kernel",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("src/main.zig"),
}),
});
kernel.setLinkerScript(b.path("kernel.ld"));
b.installArtifact(kernel);
const qemu = b.addSystemCommand(&[_][]const u8{
"qemu-system-x86_64",
});
qemu.addArg("-kernel");
qemu.addFileArg(kernel.getEmittedBin());
const run = b.step("run", "Run QEMU");
run.dependOn(&qemu.step);
}

61
flake.lock generated Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1757745802,
"narHash": "sha256-hLEO2TPj55KcUFUU1vgtHE9UEIOjRcH/4QbmfHNF820=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c23193b943c6c689d70ee98ce3128239ed9e32d1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

43
flake.nix Normal file
View file

@ -0,0 +1,43 @@
{
description = "Zig Template";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {
nixpkgs,
flake-utils,
...
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = nixpkgs.legacyPackages.${system};
nativeBuildInputs = with pkgs; [
zig
zls
];
buildInputs = with pkgs; [
qemu
grub2
];
in {
devShells.default = pkgs.mkShell {inherit nativeBuildInputs buildInputs;};
packages.default = pkgs.stdenv.mkDerivation {
pname = "template";
version = "0.0.0";
src = ./.;
nativeBuildInputs =
nativeBuildInputs
++ [
pkgs.zig.hook
];
inherit buildInputs;
};
}
);
}

27
kernel.ld Normal file
View file

@ -0,0 +1,27 @@
ENTRY(_start)
SECTIONS {
. = 1M;
.multiboot : ALIGN(4K) {
KEEP(*(.multiboot))
}
.text : ALIGN(4K) {
*(.text)
}
.rodata : ALIGN(4K) {
*(.rodata)
}
.data : ALIGN(4K) {
*(.data);
}
.bss : ALIGN(4K) {
*(COMMON)
*(.bss)
}
}

153
src/Console.zig Normal file
View file

@ -0,0 +1,153 @@
const std = @import("std");
const Self = @This();
pub fn init() Self {
var self = Self{};
self.clear();
return self;
}
const vga_width = 80;
const vga_height = 25;
const vga_size = vga_width * vga_height;
pub const ConsoleColor = enum(u8) {
black = 0,
blue = 1,
green = 2,
cyan = 3,
red = 4,
magenta = 5,
brown = 6,
light_gray = 7,
dark_gray = 8,
light_blue = 9,
light_green = 10,
light_cyan = 11,
light_red = 12,
light_magenta = 13,
light_brown = 14,
white = 15,
};
const default_color = vgaEntryColor(ConsoleColor.light_gray, ConsoleColor.black);
column: usize = 0,
color: u8 = default_color,
buffer: [*]volatile u16 = @ptrFromInt(0xb8000),
fn vgaEntryColor(fg: ConsoleColor, bg: ConsoleColor) u8 {
return @intFromEnum(fg) | (@intFromEnum(bg) << 4);
}
fn vgaEntry(uc: u8, new_color: u8) u16 {
const c: u16 = new_color;
return uc | (c << 8);
}
fn index(x: usize, y: usize) usize {
return y * vga_width + x;
}
pub fn setColor(self: *Self, fg: ConsoleColor, bg: ConsoleColor) void {
self.color = vgaEntryColor(fg, bg);
}
pub fn resetColor(self: *Self) void {
self.color = default_color;
}
pub fn clear(self: *Self) void {
@memset(self.buffer[0..vga_size], vgaEntry(' ', self.color));
}
fn clearRow(self: *Self, y: usize) void {
for (0..vga_width) |x| {
const i = index(x, y);
self.buffer[i] = vgaEntry(' ', self.color);
}
}
fn newLine(self: *Self) void {
for (1..vga_height) |y| {
for (0..vga_width) |x| {
const entry = self.getEntryAt(x, y);
self.putEntryAt(entry, x, y - 1);
}
}
self.clearRow(vga_height - 1);
self.column = 0;
}
pub fn putCharAt(self: *Self, c: u8, new_color: u8, x: usize, y: usize) void {
self.putEntryAt(vgaEntry(c, new_color), x, y);
}
fn putEntryAt(self: *Self, entry: u16, x: usize, y: usize) void {
const i = index(x, y);
self.buffer[i] = entry;
}
fn getEntryAt(self: *Self, x: usize, y: usize) u16 {
const i = index(x, y);
return self.buffer[i];
}
pub fn putChar(self: *Self, c: u8) void {
switch (c) {
'\n' => self.newLine(),
else => {
if (self.column >= vga_width) {
self.newLine();
}
const y = vga_height - 1;
const x = self.column;
self.putCharAt(c, self.color, x, y);
self.column += 1;
},
}
}
pub fn puts(self: *Self, data: []const u8) void {
for (data) |c| {
self.putChar(c);
}
}
const Writer = struct {
console: *Self,
interface: std.Io.Writer,
fn drain(w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
_ = splat;
const self: *@This() = @alignCast(@fieldParentPtr("interface", w));
const buffered = w.buffered();
if (buffered.len != 0) {
self.console.puts(buffered);
return w.consume(buffered.len);
}
self.console.puts(data[0]);
return data[0].len;
}
};
fn writer(self: *Self, buffer: []u8) Writer {
return .{
.console = self,
.interface = .{
.buffer = buffer,
.vtable = &.{
.drain = Writer.drain,
},
},
};
}
pub fn printf(self: *Self, comptime format: []const u8, args: anytype) void {
var w = self.writer(&.{});
w.interface.print(format, args) catch unreachable;
}

93
src/main.zig Normal file
View file

@ -0,0 +1,93 @@
const lazy = @import("utils/Lazy.zig").lazy;
const Console = @import("Console.zig");
const ALIGN = 1 << 0;
const MEMINFO = 1 << 1;
const MAGIC = 0x1BADB002;
const FLAGS = ALIGN | MEMINFO;
const MultibootHeader = packed struct {
magic: i32 = MAGIC,
flags: i32,
checksum: i32,
padding: u32 = 0,
};
export var mutliboot: MultibootHeader align(4) linksection(".multiboot") = .{
.flags = FLAGS,
.checksum = -(MAGIC + FLAGS),
};
var stack_bytes: [16 * 1024]u8 align(16) linksection(".bss") = undefined;
export fn _start() callconv(.naked) noreturn {
asm volatile (
\\ movl %[stack_top], %%esp
\\ movl %%esp, %%ebp
\\ call %[kmain:P]
:
: [stack_top] "i" (@as([*]align(16) u8, @ptrCast(&stack_bytes)) + @sizeOf(@TypeOf(stack_bytes))),
[kmain] "X" (&kmain),
);
}
var console = lazy(Console, .init);
fn kmain() callconv(.c) void {
std.log.info("This is an info message", .{});
std.log.debug("This is a debug message!", .{});
std.log.warn("This is a warning message!", .{});
std.log.err("This is an error message!", .{});
@panic("Uh oh");
}
const std = @import("std");
pub const panic = std.debug.FullPanic(kpanic);
fn kpanic(msg: []const u8, first_trace_addr: ?usize) noreturn {
_ = first_trace_addr;
std.log.err("PANIC: {s}", .{msg});
while (true) {}
}
pub const std_options = std.Options{
.log_level = .debug,
.logFn = klog,
};
fn klog(
comptime level: std.log.Level,
comptime scope: @Type(.enum_literal),
comptime format: []const u8,
args: anytype,
) void {
const c = console.get();
const color = switch (level) {
.err => Console.ConsoleColor.light_red,
.warn => Console.ConsoleColor.light_brown,
.info => Console.ConsoleColor.light_blue,
.debug => Console.ConsoleColor.light_green,
};
c.putChar('[');
{
c.setColor(color, Console.ConsoleColor.black);
defer c.resetColor();
c.puts(level.asText());
}
c.putChar(']');
c.putChar(' ');
c.puts("(" ++ @tagName(scope) ++ ")");
c.putChar(' ');
c.printf(format ++ "\n", args);
}

56
src/multiboot.zig Normal file
View file

@ -0,0 +1,56 @@
pub const Info = extern struct {
flags: c_uint,
mem_lower: c_uint,
mem_upper: c_uint,
boot_device: c_uint,
cmdline: c_uint,
mods_count: c_uint,
mods_addr: c_uint,
u: extern union {
aout_sym: extern struct {
tabsize: c_uint,
strsize: c_uint,
addr: c_uint,
reserved: c_uint,
},
elf_sec: extern struct {
num: c_uint,
size: c_uint,
addr: c_uint,
shndx: c_uint,
},
},
mmap_length: c_uint,
mmap_addr: c_uint,
drives_length: c_uint,
drives_addr: c_uint,
config_table: c_uint,
boot_loader_name: c_uint,
apm_table: c_uint,
vbe_control_info: c_uint,
vbe_move_info: c_uint,
vbe_mode: c_ushort,
vbe_interface_seg: c_ushort,
vbe_interface_off: c_ushort,
vbe_interface_len: c_ushort,
framebuffer_addr: c_ulonglong,
framebuffer_pitch: c_uint,
framebuffer_width: c_uint,
framebuffer_height: c_uint,
framebuffer_bpp: u8,
framebuffer_type: u8,
unnamed_0: extern union {
unnamed_0: extern struct {
framebuffer_palette_addr: c_uint,
framebuffer_palette_num_colors: c_ushort,
},
unnamed_1: extern struct {
framebuffer_red_field_position: u8,
framebuffer_red_mask_size: u8,
framebuffer_green_field_position: u8,
framebuffer_green_mask_size: u8,
framebuffer_blue_field_position: u8,
framebuffer_blue_mask_size: u8,
},
},
};

20
src/utils/Lazy.zig Normal file
View file

@ -0,0 +1,20 @@
const std = @import("std");
pub fn Lazy(comptime T: type, comptime init_fn: std.meta.DeclEnum(T)) type {
return struct {
const Self = @This();
value: ?T = null,
pub fn get(self: *Self) *T {
return if (self.value) |*v| v else blk: {
self.value = @field(T, @tagName(init_fn))();
break :blk &self.value.?;
};
}
};
}
pub fn lazy(comptime T: type, comptime init_fn: std.meta.DeclEnum(T)) Lazy(T, init_fn) {
return .{};
}