feat(loader, kernel): impl part of loader and initialize kernel structure

This commit is contained in:
2026-04-11 09:42:09 +08:00
parent 1233ae9e9b
commit 34ccf69569
53 changed files with 1743 additions and 777 deletions

3
arch/x86_64/arch.zig Normal file
View File

@@ -0,0 +1,3 @@
pub const io = @import("io.zig");
pub const processor = @import("processor.zig");
pub const mem = @import("mem.zig");

View File

@@ -0,0 +1,7 @@
const boot_consts = @cImport({
@cInclude("boot.h");
});
pub const BOOT_HEAP_SIZE: usize = boot_consts.BOOT_HEAP_SIZE;
pub const BOOT_STACK_SIZE: usize = boot_consts.BOOT_STACK_SIZE;
pub const LOAD_PHYSICAL_ADDR: usize = boot_consts.LOAD_PHYSICAL_ADDR;

View File

@@ -0,0 +1,159 @@
const std = @import("std");
const Step = std.Build.Step;
const OptimizeMode = std.builtin.OptimizeMode;
fn getTarget(b: *std.Build) std.Build.ResolvedTarget {
var query: std.Target.Query = .{
.cpu_arch = .x86_64,
.os_tag = .freestanding,
.abi = .none,
};
const Features = std.Target.x86.Feature;
query.cpu_features_sub.addFeature(@intFromEnum(Features.mmx));
query.cpu_features_sub.addFeature(@intFromEnum(Features.sse));
query.cpu_features_sub.addFeature(@intFromEnum(Features.sse2));
query.cpu_features_sub.addFeature(@intFromEnum(Features.sse3));
query.cpu_features_sub.addFeature(@intFromEnum(Features.ssse3));
query.cpu_features_sub.addFeature(@intFromEnum(Features.sse4_1));
query.cpu_features_sub.addFeature(@intFromEnum(Features.sse4_2));
query.cpu_features_sub.addFeature(@intFromEnum(Features.avx));
query.cpu_features_sub.addFeature(@intFromEnum(Features.avx2));
query.cpu_features_add.addFeature(@intFromEnum(Features.soft_float));
const target = b.resolveTargetQuery(query);
return target;
}
var tool_extract_symbol: *Step.InstallArtifact = undefined;
var tool_generate_image: *Step.InstallArtifact = undefined;
var tool_compress_kernel: *Step.InstallArtifact = undefined;
fn buildTool(b: *std.Build) void {
const mvzr = b.dependency("mvzr", .{}).module("mvzr");
const elfy = b.dependency("elfy", .{}).module("elfy");
const compile_extract_symbol = b.addExecutable(.{
.name = "symbol_extract",
.root_module = b.addModule("symbol_extract", .{
.root_source_file = b.path("arch/x86_64/boot/build_tool/symbol_extract.zig"),
.target = b.resolveTargetQuery(.{
.cpu_arch = @import("builtin").target.cpu.arch,
}),
.optimize = OptimizeMode.ReleaseSafe,
}),
});
compile_extract_symbol.root_module.addImport("mvzr", mvzr);
compile_extract_symbol.root_module.addImport("elfy", elfy);
tool_extract_symbol = b.addInstallArtifact(compile_extract_symbol, .{
.dest_dir = .{ .override = .{ .custom = "build_tool" } }
});
const compile_generate_image = b.addExecutable(.{
.name = "generate_image",
.root_module = b.addModule("generate_image", .{
.root_source_file = b.path("arch/x86_64/boot/build_tool/generate_image.zig"),
.target = b.resolveTargetQuery(.{
.cpu_arch = @import("builtin").target.cpu.arch,
}),
.optimize = OptimizeMode.ReleaseSafe,
}),
});
tool_generate_image = b.addInstallArtifact(compile_generate_image, .{
.dest_dir = .{ .override = .{ .custom = "build_tool" } }
});
const compile_compress_kernel = b.addExecutable(.{
.name = "compress_kernel",
.root_module = b.addModule("compress_kernel", .{
.root_source_file = b.path("arch/x86_64/boot/build_tool/compress_kernel.zig"),
.target = b.resolveTargetQuery(.{
.cpu_arch = @import("builtin").target.cpu.arch,
}),
.optimize = OptimizeMode.ReleaseSafe,
}),
});
tool_compress_kernel = b.addInstallArtifact(compile_compress_kernel, .{
.dest_dir = .{ .override = .{ .custom = "build_tool" } }
});
}
fn buildHeader(b: *std.Build, optimize: OptimizeMode, kcapsule: *Step.Compile) *Step.ObjCopy {
const target = getTarget(b);
const copy_header = b.addWriteFiles();
const include_dir = copy_header.addCopyDirectory(b.path("arch/x86_64/boot/include"), "include", .{});
const symbol_extract = b.addRunArtifact(tool_extract_symbol.artifact);
symbol_extract.step.dependOn(&tool_extract_symbol.step);
symbol_extract.addFileArg(kcapsule.getEmittedBin());
symbol_extract.addArgs(&[_][] const u8{
"^(_end|_edata|startup_32|startup_64)$",
"CAP_"
});
symbol_extract.addFileArg(include_dir.join(b.allocator, "kcapsule.h") catch @panic("OOM"));
symbol_extract.step.dependOn(&copy_header.step);
symbol_extract.step.dependOn(&kcapsule.step);
const header_elf = b.addExecutable(.{
.name = "header.bin",
.root_module = b.addModule("header", .{
.root_source_file = null,
.target = target,
.optimize = optimize,
}),
});
header_elf.root_module.addAssemblyFile(b.path("arch/x86_64/boot/header.S"));
header_elf.root_module.addIncludePath(include_dir);
header_elf.setLinkerScript(b.path("arch/x86_64/boot/header.ld"));
header_elf.step.dependOn(&symbol_extract.step);
const header = b.addObjCopy(header_elf.getEmittedBin(), .{
.format = .bin,
});
return header;
}
fn buildKcapsule(b: *std.Build,
optimize: OptimizeMode,
kernel: *Step.Compile) *Step.Compile {
const target = getTarget(b);
const compress = b.addRunArtifact(tool_compress_kernel.artifact);
compress.step.dependOn(&kernel.step);
compress.addFileArg(kernel.getEmittedBin());
const kernelz_path = kernel.getEmittedBinDirectory().join(b.allocator, "kernelz") catch @panic("OOM");
const payload_asm_path = kernel.getEmittedBinDirectory().join(b.allocator, "payload.S") catch @panic("OOM");
compress.addFileArg(kernelz_path);
compress.addFileArg(payload_asm_path);
const arch_module = b.addModule("arch", .{
.root_source_file = b.path("arch/x86_64/arch.zig"),
.target = target,
.optimize = optimize,
.pic = true,
});
const loader_module = b.addModule("loader", .{
.root_source_file = b.path("arch/x86_64/boot/extractor.zig"),
.target = target,
.optimize = optimize,
.pic = true,
});
loader_module.addImport("arch", arch_module);
loader_module.addIncludePath(b.path("arch/x86_64/boot/include"));
loader_module.addAssemblyFile(payload_asm_path);
const kcapsule = b.addExecutable(.{
.name = "kcapsule",
.use_lld = true,
.use_llvm = true,
.root_module = loader_module,
});
kcapsule.pie = true;
kcapsule.setLinkerScript(b.path("arch/x86_64/boot/kcapsule.ld"));
kcapsule.root_module.addAssemblyFile(b.path("arch/x86_64/boot/head.S"));
kcapsule.step.dependOn(&compress.step);
return kcapsule;
}
pub fn buildBootImage(b: *std.Build, kernel: *Step.Compile) void {
buildTool(b);
const optimize = OptimizeMode.Debug;
const kcapsule = buildKcapsule(b, optimize, kernel);
const header = buildHeader(b, optimize, kcapsule);
const kcapsule_install = b.addInstallArtifact(kcapsule, .{});
const kcapsule_bin = b.addObjCopy(kcapsule.getEmittedBin(), .{
.format = .bin,
});
const image = b.addRunArtifact(tool_generate_image.artifact);
image.addFileArg(header.getOutput());
image.addFileArg(kcapsule_bin.getOutput());
image.addArg(std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "yukiImage" }) catch @panic("OOM"));
b.getInstallStep().dependOn(&kcapsule_install.step);
b.getInstallStep().dependOn(&image.step);
}

View File

@@ -0,0 +1,57 @@
const std = @import("std");
const Compress = std.compress.flate.Compress;
const Dir = std.Io.Dir;
const SIZE_ALIGN: usize = 0x200;
pub fn main(init: std.process.Init) !void {
const args = try init.minimal.args.toSlice(init.arena.allocator());
if (args.len != 4) {
std.debug.print("Usage: {s} <kernel_bin> <output_file> <assembly_file>\n", .{args[0]});
std.process.exit(1);
}
const kernel_bin_path = args[1];
const output_path = args[2];
const assembly_path = args[3];
std.debug.print("Compressing kernel: {s} -> {s}\n", .{kernel_bin_path, output_path});
const kernel_file = try Dir.cwd().openFile(init.io, kernel_bin_path, .{});
defer kernel_file.close(init.io);
const compressed_file = try Dir.cwd().createFile(init.io, output_path, .{ .truncate = true });
defer compressed_file.close(init.io);
var read_buffer: [4096]u8 = undefined;
var write_buffer: [4096]u8 = undefined;
var work_buffer: [std.compress.flate.max_window_len]u8 = undefined;
var writer = compressed_file.writer(init.io, &write_buffer);
var reader = kernel_file.reader(init.io, &[0]u8{});
var compressor = try Compress.init(&writer.interface, &work_buffer, .gzip, .best);
var compressor_writer = &compressor.writer;
var total_read: usize = 0;
while (true) {
const read_bytes = try reader.interface.readSliceShort(&read_buffer);
if (read_bytes == 0) break;
total_read += read_bytes;
try compressor_writer.writeAll(read_buffer[0..read_bytes]);
}
try compressor_writer.flush();
const aligned_size = (total_read + SIZE_ALIGN - 1) & ~(SIZE_ALIGN - 1);
var assembly_file = try Dir.cwd().createFile(init.io, assembly_path, .{ .truncate = true });
defer assembly_file.close(init.io);
var assembly_writer = assembly_file.writer(init.io, &[0]u8{});
try assembly_writer.interface.print(
\\/* Auto-generated compressed kernel data */
\\ .section .rodata.compressed,"a"
\\ .global compressed_kernel_data
\\compressed_kernel_data:
\\ .incbin "{s}"
\\compressed_end:
\\ .set compressed_kernel_size, compressed_end - compressed_kernel_data
\\ .set uncompressed_kernel_size, {d}
\\ .global compressed_kernel_size
\\ .global uncompressed_kernel_size
, .{output_path, aligned_size});
try assembly_writer.interface.flush();
std.debug.print("Uncompressed aligned size: 0x{x} bytes\n", .{aligned_size});
}

View File

@@ -0,0 +1,50 @@
const std = @import("std");
const Dir = std.Io.Dir;
const ALIGN_SIZE: usize = 4096;
pub fn main(init: std.process.Init) !void {
const args = try init.minimal.args.toSlice(init.arena.allocator());
if (args.len != 4) {
std.debug.print("Usage: {s} <header_file> <kcapsule_file> <output_file>\n", .{args[0]});
std.process.exit(1);
}
const header_path = args[1];
const kcapsule_path = args[2];
const output_path = args[3];
var image = try Dir.cwd().createFile(init.io, output_path, .{ .truncate = true });
defer image.close(init.io);
var image_buffer: [4096]u8 = undefined;
var w = image.writer(init.io, &image_buffer);
var read_buffer: [4096]u8 = undefined;
const header_file = try Dir.cwd().openFile(init.io, header_path, .{});
var r = header_file.reader(init.io, &[0]u8{});
var written: usize = 0;
while (true) {
const read_bytes = try r.interface.readSliceShort(&read_buffer);
if (read_bytes == 0) break;
try w.interface.writeAll(read_buffer[0..read_bytes]);
written += read_bytes;
}
header_file.close(init.io);
if (written % ALIGN_SIZE != 0) {
var padding_size = ALIGN_SIZE - (written % ALIGN_SIZE);
@memset(read_buffer[0..@min(padding_size, read_buffer.len)], 0);
while (padding_size > 0) {
const to_write = @min(padding_size, read_buffer.len);
try w.interface.writeAll(read_buffer[0..to_write]);
padding_size -= to_write;
}
}
const kcapsule_file = try Dir.cwd().openFile(init.io, kcapsule_path, .{});
r = kcapsule_file.reader(init.io, &[0]u8{});
while (true) {
const read_bytes = try r.interface.readSliceShort(&read_buffer);
if (read_bytes == 0) break;
try w.interface.writeAll(read_buffer[0..read_bytes]);
written += read_bytes;
}
kcapsule_file.close(init.io);
try w.interface.flush();
}

View File

@@ -0,0 +1,41 @@
const std = @import("std");
const Dir = std.Io.Dir;
const elfy = @import("elfy");
const mvzr = @import("mvzr");
pub fn main(init: std.process.Init) !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
const allocator = gpa.allocator();
defer _ = gpa.deinit();
const args = try init.minimal.args.toSlice(init.arena.allocator());
if (args.len != 5) {
std.debug.print("Usage: {s} <source_file> <pattern> <prefix> <output_file>\n", .{args[0]});
std.process.exit(1);
}
const input_path = args[1];
const pattern = args[2];
const prefix = args[3];
const output_path = args[4];
const re = mvzr.compile(pattern) orelse @panic("Failed to compile regex pattern");
var file = try Dir.cwd().createFile(init.io, output_path, .{ .truncate = true });
defer file.close(init.io);
var w = file.writer(init.io, &[0]u8{});
_ = try w.interface.write("/* Auto-generated symbol extract header */\n#pragma once\n");
var binary = try elfy.Elf.init(init.io, input_path, .ReadOnly, allocator);
defer binary.deinit();
var symbols = try binary.getIterator(elfy.ElfSymbol);
while (try symbols.next()) |symbol| {
const name = try binary.getSymbolName(symbol);
if (re.isMatch(name)) {
try w.interface.print("#define {s}{s} 0x{x}\n", .{prefix, name, symbol.getValue()});
}
}
try w.interface.flush();
}

View File

@@ -0,0 +1,82 @@
fn isWhitespace(c: u8) bool {
return c == ' ' or c == '\t' or c == '\n' or c == '\r';
}
pub const Cmdline = struct {
ptr: []const u8,
pub fn init(ptr: []const u8) Cmdline {
return Cmdline{
.ptr = ptr,
};
}
fn _parseArg(self: *const Cmdline, key: []const u8, buf: []u8) ?usize {
var state: union(enum) {
search_key,
compare_key: usize,
copy_value: usize,
skip_arg
} = .search_key;
var i: usize = 0;
while (i < self.ptr.len) {
const c = self.ptr[i];
switch (state) {
.search_key => {
if (isWhitespace(c)) {
i = i + 1;
continue;
}
state = .{ .compare_key = 0};
},
.compare_key => |key_index| {
if (key_index < key.len) {
if (c == key[key_index]) {
state = .{ .compare_key = key_index + 1 };
} else {
state = .skip_arg;
}
} else {
if (c == '=') {
state = .{ .copy_value = 0 };
} else if (isWhitespace(c)) {
return 0;
} else {
state = .skip_arg;
}
}
i = i + 1;
},
.skip_arg => {
if (isWhitespace(c)) {
state = .search_key;
}
i = i + 1;
},
.copy_value => |buf_index| {
if (isWhitespace(c) or buf_index >= buf.len) {
return buf_index;
}
buf[buf_index] = c;
state = .{ .copy_value = buf_index + 1 };
i = i + 1;
},
}
}
switch (state) {
.copy_value => |buf_index| return buf_index,
else => return null
}
}
pub fn parseArg(self: *const Cmdline, key: []const u8, buf: []u8) ?usize {
return self._parseArg(key, buf);
}
pub fn parseArgBool(self: *const Cmdline, key: []const u8) bool {
const result = self._parseArg(key, &[_]u8{});
if (result) {
return true;
} else {
return false;
}
}
};

View File

@@ -0,0 +1,118 @@
const std = @import("std");
const Cmdline = @import("cmdline.zig").Cmdline;
const io = @import("arch").io;
const PREDEFINED_PORTS: [2]u16 = .{
0x3F8, // ttyS0
0x2F8, // ttyS1
};
var serial_writer: ?SerialWriter = null;
// Parse earlyprintk argument from cmdline
// Format: earlyprintk=serial,port,baudrate, port can either be ioport address (e.g., 0x3F8) or
// a predefined name (e.g., ttyS0/ttyS1).
fn parseEarlyprintk(cmdline: *const Cmdline) ?struct { port: u16, baudrate: u32 } {
var buf = [_]u8{0} ** 32;
const len = cmdline.parseArg("earlyprintk", &buf) orelse 0;
if (len == 0) return null;
const arg = buf[0..len];
var it = std.mem.splitAny(u8, arg, ",");
if(std.mem.eql(u8, it.next() orelse "", "serial") == false) {
return null;
}
const port_str = it.next() orelse return null;
var port: u16 = undefined;
if (port_str.len == 5 and std.mem.startsWith(u8, port_str, "ttyS")) {
const port_idx = std.fmt.parseInt(u8, port_str[4..5], 10) catch return null;
if (port_idx >= PREDEFINED_PORTS.len) {
return null;
}
port = PREDEFINED_PORTS[port_idx];
} else if (std.fmt.parseInt(u16, port_str, 16) catch null) |port_val| {
port = port_val;
} else {
return null;
}
const baudrate_str = it.next() orelse return null;
const baudrate = std.fmt.parseInt(u32, baudrate_str, 10) catch return null;
return .{ .port = port, .baudrate = baudrate };
}
fn serialInit(port: u16, baudrate: u32) void {
const divisor: u16 = @intCast(115200 / baudrate);
// Disable interrupts
io.outb(port + 1, 0x00);
// Disable FIFO
io.outb(port + 2, 0x00);
// Set 8 data bits, 1 stop bit, no parity
io.outb(port + 3, 0x80);
const c = io.inb(port + 3);
// Enable DLAB
io.outb(port + 3, c | 0x80);
// Set divisor low byte
io.outb(port + 0, @intCast(divisor & 0x00FF));
// Set divisor high byte
io.outb(port + 1, @intCast((divisor >> 8) & 0x00FF));
io.outb(port + 3, c & ~@as(u8, 0x80));
}
pub fn earlyConsoleInit(cmdline: *const Cmdline) void {
const result = parseEarlyprintk(cmdline) orelse return;
serialInit(result.port, result.baudrate);
serial_writer = SerialWriter.init(result.port);
}
const SerialWriter = struct {
port: u16,
interface: std.Io.Writer,
var WRITER_VTABLE: std.Io.Writer.VTable = undefined;
pub fn init(port: u16) SerialWriter {
WRITER_VTABLE = .{
.drain = SerialWriter.drain,
};
var sw = SerialWriter{
.port = port,
.interface = undefined,
};
sw.interface = .{
.buffer = &[0]u8{},
.vtable = @call(.never_inline, getWriterVTable, .{}),
};
return sw;
}
pub fn writer(self: *SerialWriter) *std.Io.Writer {
return &self.interface;
}
fn getWriterVTable() *const std.Io.Writer.VTable {
return &WRITER_VTABLE;
}
// fn getWriterDrain() *const @TypeOf(SerialWriter.drain) {
// return SerialWriter.drain;
// }
fn drain(self: *std.Io.Writer, buf: []const []const u8, splat: usize) error{}!usize {
const sw: *SerialWriter = @fieldParentPtr("interface", self);
var written: usize = 0;
for (buf, 0..buf.len) |part, i| {
const repeat = if (i == buf.len - 1) splat else 1;
for (0..repeat) |_| {
for (part) |c| {
SerialWriter.putchar(sw, c);
written += 1;
}
}
}
return written;
}
fn putchar(self: *const SerialWriter, c: u8) void {
var timeout: usize = 0xffff;
while(io.inb(self.port + 5) & 0x20 == 0 and timeout > 0) {
timeout -= 1;
asm volatile ("pause"
::: .{ .memory = true }
);
}
io.outb(self.port, c);
}
};
pub fn dprint(comptime fmt: []const u8, args: anytype) void {
var sw = serial_writer orelse return;
_ = sw.writer().print(fmt, args) catch {};
}

View File

@@ -0,0 +1,46 @@
const std = @import("std");
const BootParams = @import("params.zig").BootParams;
const early_serial_console = @import("early_serial_console.zig");
const Cmdline = @import("cmdline.zig").Cmdline;
const payload = @import("payload.zig");
const multiboot = @import("multiboot.zig");
const Decompress = std.compress.flate.Decompress;
const relocate = @import("relocate.zig");
const ident_map = @import("ident_map.zig");
comptime {
@export(&relocate.relocateSelf, .{
.name = "relocateSelf",
});
}
fn decompress(target_addr: [*]u8) !void {
var data_reader = std.Io.Reader.fixed(payload.getCompressedKernelData());
var work_buffer: [std.compress.flate.max_window_len]u8 = undefined;
var decompressor = Decompress.init(&data_reader, .gzip, &work_buffer);
var decompressor_reader = &decompressor.reader;
var decompress_buf = target_addr;
while (true) {
const read_bytes = try decompressor_reader.readSliceShort(decompress_buf[0..4096]);
if (read_bytes == 0) break;
decompress_buf = decompress_buf[read_bytes..];
if ((@intFromPtr(decompress_buf) - @intFromPtr(target_addr) ) % 10240 == 0) {
early_serial_console.dprint("Decompressed {d} bytes...\n", .{@intFromPtr(decompress_buf) - @intFromPtr(target_addr)});
}
}
}
export fn extractKernel(multiboot_info: *u8,
target_addr: usize) callconv(.c) noreturn {
const params = multiboot.parseMultibootInfo(multiboot_info);
const cmdline = Cmdline.init(params.cmdline);
early_serial_console.earlyConsoleInit(&cmdline);
early_serial_console.dprint("Starting kernel extraction...\n", .{});
early_serial_console.dprint("Kernel compressed size {d} bytes\n", .{payload.getCompressedKernelData().len});
early_serial_console.dprint("Kernel uncompressed size {d} bytes\n", .{payload.getUncompressedKernelSize()});
decompress(@ptrFromInt(target_addr)) catch |err| {
early_serial_console.dprint("Kernel decompression failed: {s}\n", .{@errorName(err)});
while(true){}
};
early_serial_console.dprint("Kernel extraction completed.\n", .{});
while(true){}
}

181
arch/x86_64/boot/head.S Normal file
View File

@@ -0,0 +1,181 @@
#include "boot.h"
#include "processor_flags.h"
#define rva(x) ((x)-startup_32)
#define __BOOT_DS (3*8)
#define __KERNEL32_CS (1*8)
#define __KERNEL_CS (2*8)
.section .head.text, "ax"
.global _start
_start: // just make linker happy
.code32
.global startup_32
.type startup_32, %function
startup_32:
movl $0x50000, %edx
movl $0xdeadbeef, (%edx)
cld
cli
// Actually multiboot doesn't give us a scartch area to use as stack,
// so we temporaily save the 2nd 32bit of boot information and use it as the stack.
movl 4(%ebx), %ecx
leal 8(%ebx), %esp
call 1f
1: popl %ebp
movl %ecx, 4(%ebx)
subl $rva(1b), %ebp
leal rva(gdt)(%ebp), %eax
mov %eax, 2(%eax)
lgdt (%eax)
mov $__BOOT_DS, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
leal rva(boot_stack_end)(%ebp), %esp
pushl $__KERNEL32_CS
leal rva(1f)(%ebp), %eax
pushl %eax
lret
1:
movl %cr4, %eax
orl $X86_CR4_PAE, %eax
movl %eax, %cr4
leal rva(pgtable)(%ebp), %edi
xorl %eax, %eax
movl $(BOOT_INIT_PGTABLE_SIZE/4), %ecx
rep stosl
leal rva(pgtable)(%ebp), %edi
leal 0x1007(%edi), %eax
movl %eax, 0(%edi)
leal rva(pgtable + 0x1000)(%ebp), %edi
leal 0x1007(%edi), %eax
movl $4, %ecx
1: movl %eax, 0(%edi)
addl $0x1000, %eax
addl $8, %edi
decl %ecx
jnz 1b
leal rva(pgtable + 0x2000)(%ebp), %edi
movl $0x183, %eax
movl $2048, %ecx
1: movl %eax, 0(%edi)
addl $0x200000, %eax
addl $8, %edi
decl %ecx
jnz 1b
leal rva(pgtable)(%ebp), %eax
movl %eax, %cr3
movl $MSR_EFER, %ecx
rdmsr
btsl $_EFER_LME, %eax
wrmsr
leal rva(startup_64)(%ebp), %eax
pushl $__KERNEL_CS
pushl %eax
movl $CR0_STATE, %eax
movl %eax, %cr0
lret
#define rva64(x) ((x)-startup_64)
#define STARTUP64_OFFSET 0x200
.code64
.global startup_64
.type startup_64, %function
.org STARTUP64_OFFSET
startup_64:
cld
cli
xorl %eax, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss
movl %eax, %fs
movl %eax, %gs
movq %rbx, %rsi
movq $LOAD_PHYSICAL_ADDR, %rbp
// movl has the side effect of zero-extending the upper 32 bits of rbx
movl $uncompressed_kernel_size, %ebx
// there's no need to copy the code before startup_64
addq %rbp, %rbx
leaq rva64(boot_stack_end)(%rbx), %rsp
movq %rsi, %r15
leaq (_bss - 8)(%rip), %rsi
leaq rva64(_bss - 8)(%rbx), %rdi
movl $rva64(_bss), %ecx
shrl $3, %ecx
std
rep movsq
cld
leaq rva64(gdt64)(%rbx), %rax
leaq rva64(gdt)(%rbx), %rdx
movq %rdx, 2(%rax)
lgdt (%rax)
leaq rva64(relocated)(%rbx), %rax
jmp *%rax
.text
relocated:
// clear bss
xorl %eax, %eax
leaq _bss(%rip), %rdi
leaq _ebss(%rip), %rcx
subq %rdi, %rcx
shrq $3, %rcx
rep stosq
mov %rbx, %rdi
subq $STARTUP64_OFFSET, %rdi
call relocateSelf
movq %r15, %rdi // boot params pointer
movq %rbp, %rsi // load physical address
call extractKernel
hlt
.data
gdt64:
.word gdt_end - gdt - 1
.quad gdt - gdt64
gdt64_end:
.balign 8
gdt:
.word gdt_end - gdt - 1
.long 0
.word 0
.quad 0x00cf9a000000ffff // __KERNEL32_CS
.quad 0x00af9a000000ffff // __KERNEL_CS
.quad 0x00cf92000000ffff // __KERNEL_DS
gdt_end:
.bss
.balign 4
boot_heap:
.space BOOT_HEAP_SIZE
boot_stack:
.space BOOT_STACK_SIZE
boot_stack_end:
.section ".pgtable","aw",@nobits
.balign 4096
pgtable:
.space BOOT_PGTABLE_SIZE
pgtable_end:

69
arch/x86_64/boot/header.S Normal file
View File

@@ -0,0 +1,69 @@
#include "multiboot2.h"
#include "kcapsule.h" // Auto-generated file
#include "boot.h"
.set falign, 0x200
.code16
.section ".header", "a"
.global _start
_start: // Just make linker happy
yuki_header:
.ascii "YUKIOS"
.quad 0x131084C
.org 0x50
.balign 8
multiboot2_header:
.long MULTIBOOT2_HEADER_MAGIC
.long MULTIBOOT_ARCHITECTURE_I386
.long multiboot2_header_end - multiboot2_header
.long -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + (multiboot2_header_end - multiboot2_header))
.balign 8
address_tag:
.short MULTIBOOT_HEADER_TAG_ADDRESS
.short 0
.long address_tag_end - address_tag
.long LOAD_PHYSICAL_ADDR + multiboot2_header
.long -1
.long 0
.long 0
address_tag_end:
.balign 8
entry_tag:
.short MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS
.short MULTIBOOT_HEADER_TAG_OPTIONAL
.long entry_tag_end - entry_tag
.long LOAD_PHYSICAL_ADDR + header_size + CAP_startup_32
entry_tag_end:
// entry_efi_i386_tag:
// .short MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32
// .short MULTIBOOT_HEADER_TAG_OPTIONAL
// .long entry_efi_i386_tag_end - entry_efi_i386_tag
// .long header_size + CAP_startup_32
// entry_efi_i386_tag_end:
// entry_efi_amd64_tag:
// .short MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64
// .short MULTIBOOT_HEADER_TAG_OPTIONAL
// .long entry_efi_amd64_tag_end - entry_efi_amd64_tag
// .long header_size + CAP_startup_64
// entry_efi_amd64_tag_end:
.balign 8
framebuffer_tag:
.short MULTIBOOT_HEADER_TAG_FRAMEBUFFER
.short MULTIBOOT_HEADER_TAG_OPTIONAL
.long framebuffer_tag_end - framebuffer_tag
.long 0
.long 0
.long 0
framebuffer_tag_end:
// .balign 8
// relocatable_tag:
// .short MULTIBOOT_HEADER_TAG_RELOCATABLE
// .short MULTIBOOT_HEADER_TAG_OPTIONAL
// .long relocatable_tag_end - relocatable_tag
// .long 0x0 /* min_addr */
// .long 0xf0000000 /* max_addr */
// .long falign /* align */
// .long 0x0
// relocatable_tag_end:
multiboot2_header_end:
.balign falign

View File

@@ -0,0 +1,9 @@
OUTPUT_ARCH(x86_64)
SECTIONS {
. = 0;
.header : {
KEEP(*(.header))
}
header_size = ALIGN(ABSOLUTE(.), 4096);
}

View File

@@ -0,0 +1,62 @@
const std = @import("std");
const processor = @import("arch").processor;
const mem = @import("arch").mem;
const PFType = mem.PFType;
const early_serial_console = @import("early_serial_console.zig");
const assert = std.debug.assert;
var pgtable_buffer: struct {
buffer: [*][mem.PAGE_SIZE]u8,
size: usize,
used_pages: usize,
} = undefined;
fn allocPgtablePage() error.OutOfMemory![4096]u8 {
// if (used + mem.PAGE_SIZE > size) {
// early_serial_console.dprint("Out of page table memory!\n", .{});
// return error.OutOfMemory;
// }
// const page = buffer + used;
// return page;
}
// fn pgdIdentInit() {
// }
// fn pudIdentInit() {
// }
fn kernelAddIdentityMap(pgdt_page: [*]mem.PGDEntry, start: u64, end: u64) void {
assert(start % mem.PMD_SIZE == 0);
assert(end % mem.PMD_SIZE == 0);
var address = start;
while (address < end) {
// var pgd = &pgdt_page[mem.PGDEntry.getIndex(address)];
// if ()
address += mem.PMD_SIZE;
}
}
export fn initializeIdentMap() void {
// var pgd_table = processor.readCr3();
// if (pgdtable == _pgtable) {
// }
}
fn printPF(msg: []const u8, errorCode: u64, faultAddress: u64, ip: u64) void {
early_serial_console.dprint(
\\{s}:
\\error code: 0x{x}
\\fault address: 0x{x}
\\instruction pointer: 0x{x}
\\
, .{msg, errorCode, faultAddress, ip});
}
export fn doBootPageFault(regs: processor.PtRegs, errorCode: u64) void {
const faultAddress = processor.readCr2();
if (errorCode & (PFType.PROT | PFType.USER | PFType.RSVD) == 0) {
printPF("Unexpected page fault", errorCode, faultAddress, regs.ip);
}
const address = faultAddress & mem.PMD_MASK;
kernelAddIdentityMap(address, address + mem.PMD_SIZE);
}

23
arch/x86_64/boot/idt.zig Normal file
View File

@@ -0,0 +1,23 @@
const desc = @import("arch").desc;
const mem = @import("arch").mem;
const TrapType = @import("arch").trap.TrapType;
extern const __KERNEL_CS: u8;
var idt_table: [256]desc.IdtEntry align(mem.PAGE_SIZE) = .{.{}} ** 256;
extern const boot_page_fault: u8;
extern const boot_nmi: u8;
fn setIdtEntry(vec: usize, handler: anytype) void {
var entry = desc.IdtEntry{};
entry.setOffset(@intFromPtr(handler));
entry.selector = @intFromPtr(&__KERNEL_CS);
entry.gate_type = .trap;
entry.present = 1;
idt_table[vec] = entry;
}
export fn loadLoaderIdt() void {
setIdtEntry(TrapType.PF, &boot_page_fault);
setIdtEntry(TrapType.NMI, &boot_nmi);
desc.loadIdt(&idt_table);
}

View File

@@ -0,0 +1,56 @@
.macro EXCEPTION_HANDLER name function error_code=0
.global \name
.type \name, @function
\name:
if \error_code == 0
pushq $0
end
pushq %rdi
pushq %rsi
pushq %rdx
pushq %rcx
pushq %rax
pushq %r8
pushq %r9
pushq %r10
pushq %r11
pushq %rbx
pushq %rbp
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq %rsp, %rdi
/* Error code is second parameter */
movq 120(%rsp), %rsi
call \function
/* Restore regs */
popq %r15
popq %r14
popq %r13
popq %r12
popq %rbp
popq %rbx
popq %r11
popq %r10
popq %r9
popq %r8
popq %rax
popq %rcx
popq %rdx
popq %rsi
popq %rdi
addq $8, %rsp
iretq
.endm
.text
.code64
EXCEPTION_HANDLER boot_page_fault doBootPageFault error_code=1
EXCEPTION_HANDLER boot_nmi doBootNMI

View File

@@ -0,0 +1,6 @@
#define BOOT_HEAP_SIZE (512 * 1024)
#define BOOT_STACK_SIZE (64 * 1024)
#define LOAD_PHYSICAL_ADDR (0x100000)
#define BOOT_PGTABLE_SIZE (32 * 4096)
#define BOOT_INIT_PGTABLE_SIZE (6 * 4096)

View File

@@ -0,0 +1,39 @@
#pragma once
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 32768
#define MULTIBOOT_HEADER_ALIGN 8
/* The magic field should contain this. */
#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
/* Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/* Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN 0x00000008
#define MULTIBOOT_HEADER_TAG_END 0
#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
#define MULTIBOOT_HEADER_TAG_ADDRESS 2
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
#define MULTIBOOT_HEADER_TAG_EFI_BS 7
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
#define MULTIBOOT_ARCHITECTURE_I386 0
#define MULTIBOOT_ARCHITECTURE_MIPS32 4
#define MULTIBOOT_HEADER_TAG_OPTIONAL 1
#define MULTIBOOT_LOAD_PREFERENCE_NONE 0
#define MULTIBOOT_LOAD_PREFERENCE_LOW 1
#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2

View File

@@ -0,0 +1,15 @@
#define X86_CR0_PE 0x00000001
#define X86_CR0_MP 0x00000002
#define X86_CR0_ET 0x00000004
#define X86_CR0_NE 0x00000020
#define X86_CR0_WP 0x00010000
#define X86_CR0_AM 0x00040000
#define X86_CR0_PG 0x80000000
#define CR0_STATE (X86_CR0_PE | X86_CR0_MP | X86_CR0_ET | \
X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | \
X86_CR0_PG)
#define X86_CR4_PAE 0x00000020
#define X86_CR4_OSFXSR 0x00000200
#define X86_CR4_OSXMMEXCPT 0x00000400
#define MSR_EFER 0xc0000080
#define _EFER_LME 8

View File

@@ -0,0 +1,59 @@
OUTPUT_ARCH(i386:x86_64)
ENTRY(startup_32)
SECTIONS {
. = 0;
.head.text : {
KEEP(*(.head.text))
}
.rodata.compressed : {
*(.rodata.compressed)
}
.text : {
_text = .;
*(.text)
*(.text.*)
_etext = .;
}
.rodata : {
_rodata = .;
*(.rodata)
*(.rodata.*)
_erodata = .;
}
.data : ALIGN(0x1000) {
_data = .;
*(.data)
*(.data.*)
. = ALIGN(. + 4, 0x200);
_edata = .;
}
.rela.dyn : {
__rela_dyn_start = .;
*(.rela.dyn)
__rela_dyn_end = .;
}
. = ALIGN(8);
.bss : {
_bss = .;
*(.bss)
*(.bss.*)
*(COMMON)
. = ALIGN(8);
_ebss = .;
}
. = ALIGN(4096);
.pgtable : {
_pgtable = .;
*(.pgtable)
_epgtable = .;
}
_end = .;
/DISCARD/ : {
*(.dynamic) *(.dynsym) *(.dynstr) *(.dynbss)
*(.hash) *(.gnu.hash)
*(.note.*)
}
}

View File

@@ -0,0 +1,250 @@
const std = @import("std");
const BootParams = @import("params.zig").BootParams;
pub const MULTIBOOT_TAG_ALIGN: usize = 8;
pub const MultibootTagType = enum(u32) {
end,
cmdline,
boot_loader_name,
module,
basic_meminfo,
bootdev,
mmap,
vbe,
framebuffer,
elf_sections,
apm,
efi32,
efi64,
smbios,
acpi_old,
acpi_new,
network,
efi_mmap,
efi_bs,
efi32_ih,
efi64_ih,
load_base_addr,
};
pub const MultibootTag = extern struct {
type: u32 align(1),
size: u32 align(1),
};
pub const MultibootTagCmdline = extern struct {
type: u32 align(1),
size: u32 align(1),
string: [0]u8 align(1),
};
pub const MultibootTagModule = extern struct {
type: u32 align(1),
size: u32 align(1),
mod_start: u32 align(1),
mod_end: u32 align(1),
cmdline: [0]u8 align(1),
};
pub const MultibootTagBasicMeminfo = extern struct {
type: u32 align(1),
size: u32 align(1),
mem_lower: u32 align(1),
mem_upper: u32 align(1),
};
pub const MultibootTagBootdev = extern struct {
type: u32 align(1),
size: u32 align(1),
biosdev: u32 align(1),
slice: u32 align(1),
part: u32 align(1),
};
pub const MultibootMmapEntry = extern struct {
addr: u64 align(1),
len: u64 align(1),
type: u32 align(1),
zero: u32 align(1),
};
pub const MultibootTagMmap = extern struct {
type: u32 align(1),
size: u32 align(1),
entry_size: u32 align(1),
entry_version: u32 align(1),
entries: [0]MultibootMmapEntry align(1),
};
pub const MultibootVbeInfoBlock = extern struct {
external_specification: [512]u8 align(1),
};
pub const MultibootVbeModeInfoBlock = extern struct {
external_specification: [256]u8 align(1),
};
pub const MultibootTagVbe = extern struct {
type: u32 align(1),
size: u32 align(1),
vbe_mode: u16 align(1),
vbe_interface_seg: u16 align(1),
vbe_interface_off: u16 align(1),
vbe_interface_len: u16 align(1),
vbe_control_info: MultibootVbeInfoBlock align(1),
vbe_mode_info: MultibootVbeModeInfoBlock align(1),
};
pub const MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED = 0;
pub const MULTIBOOT_FRAMEBUFFER_TYPE_RGB = 1;
pub const MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT = 2;
pub const MultibootColor = extern struct {
red: u8 align(1),
green: u8 align(1),
blue: u8 align(1),
};
pub const MultibootTagFramebufferCommon = extern struct {
type: u32 align(1),
size: u32 align(1),
framebuffer_addr: u64 align(1),
framebuffer_pitch: u32 align(1),
framebuffer_width: u32 align(1),
framebuffer_height: u32 align(1),
framebuffer_bpp: u8 align(1),
framebuffer_type: u8 align(1),
reserved: u16 align(1),
};
pub const MultibootTagFramebuffer = extern struct {
common: MultibootTagFramebufferCommon align(1),
details: extern union {
indexed: extern struct {
framebuffer_palette_num_colors: u16 align(1),
framebuffer_palette: [*]MultibootColor align(1),
} align(1),
rgb: extern struct {
framebuffer_red_field_position: u8 align(1),
framebuffer_red_mask_size: u8 align(1),
framebuffer_green_field_position: u8 align(1),
framebuffer_green_mask_size: u8 align(1),
framebuffer_blue_field_position: u8 align(1),
framebuffer_blue_mask_size: u8 align(1),
} align(1),
} align(1),
};
pub const MultibootTagElfSections = extern struct {
type: u32 align(1),
size: u32 align(1),
num: u32 align(1),
entsize: u32 align(1),
shndx: u32 align(1),
sections: [*]u8 align(1),
};
pub const MultibootTagApm = extern struct {
type: u32 align(1),
size: u32 align(1),
version: u16 align(1),
cseg: u16 align(1),
offset: u32 align(1),
cseg_16: u16 align(1),
dseg: u16 align(1),
flags: u16 align(1),
cseg_len: u16 align(1),
cseg_16_len: u16 align(1),
dseg_len: u16 align(1),
};
pub const MultibootTagEfi32 = extern struct {
type: u32 align(1),
size: u32 align(1),
pointer: u32 align(1),
};
pub const MultibootTagEfi64 = extern struct {
type: u32 align(1),
size: u32 align(1),
pointer: u64 align(1),
};
pub const MultibootTagSmbios = extern struct {
type: u32 align(1),
size: u32 align(1),
major: u8 align(1),
minor: u8 align(1),
reserved: [6]u8 align(1),
tables: [*]u8 align(1),
};
pub const MultibootTagOldAcpi = extern struct {
type: u32 align(1),
size: u32 align(1),
rsdp: [*]u8 align(1),
};
pub const MultibootTagNewAcpi = extern struct {
type: u32 align(1),
size: u32 align(1),
rsdp: [*]u8 align(1),
};
pub const MultibootTagNetwork = extern struct {
type: u32 align(1),
size: u32 align(1),
dhcpack: [*]u8 align(1),
};
pub const MultibootTagEfiMmap = extern struct {
type: u32 align(1),
size: u32 align(1),
descr_size: u32 align(1),
descr_vers: u32 align(1),
efi_mmap: [*]u8 align(1),
};
pub const MultibootTagEfi32Ih = extern struct {
type: u32 align(1),
size: u32 align(1),
pointer: u32 align(1),
};
pub const MultibootTagEfi64Ih = extern struct {
type: u32 align(1),
size: u32 align(1),
pointer: u64 align(1),
};
pub const MultibootTagLoadBaseAddr = extern struct {
type: u32 align(1),
size: u32 align(1),
load_base_addr: u32 align(1),
};
pub fn parseMultibootInfo(multiboot_info: *u8) BootParams {
var params = BootParams{
.cmdline = &[_]u8{},
};
var offset: usize = @sizeOf(MultibootTag);
while (true) {
const tag: *MultibootTag = @ptrFromInt(@intFromPtr(multiboot_info) + offset);
if (tag.type == @intFromEnum(MultibootTagType.end)) {
break;
}
switch (@as(MultibootTagType, @enumFromInt(tag.type))) {
MultibootTagType.cmdline => {
const cmdline_tag: *MultibootTagCmdline = @ptrCast(tag);
const sentinel_cmdline = @as([*:0]u8, @ptrCast(&cmdline_tag.string));
params.cmdline = sentinel_cmdline[0..std.mem.len(sentinel_cmdline)];
},
else => {},
}
offset += (tag.size + MULTIBOOT_TAG_ALIGN - 1) & ~(MULTIBOOT_TAG_ALIGN - 1);
}
return params;
}

View File

@@ -0,0 +1,52 @@
const std = @import("std");
// // const e820 = @import("e820.zig");
// const video = @import("video.zig");
// pub const SetupHeader = extern struct {
// _pad1: [15]u8 align(1),
// jump: u16 align(1),
// header: u32 align(1),
// version: u16 align(1),
// _pad2: [8]u8 align(1),
// type_of_loader: u8 align(1),
// loadflags: u8 align(1),
// _pad3: [22]u8 align(1),
// cmd_line_ptr: u32 align(1),
// _pad4: [4]u8 align(1),
// kernel_alignment: u32 align(1),
// relocatable_kernel: u8 align(1),
// min_alignment: u8 align(1),
// xloadflags: u16 align(1),
// cmdline_size: u32 align(1),
// _pad5: [36]u8 align(1),
// init_size: u32 align(1),
// _pad6: [8]u8 align(1),
// };
// const E820_MAX_ENTRIES_BP = 128;
// pub const BootParams = extern struct {
// screen_info: video.ScreenInfo align(1), // 0x000
// _pad1: [0x30]u8 align(1), // 0x040
// acpi_rsdp_addr: u64 align(1), // 0x070
// _pad2: [0x50]u8 align(1), // 0x078
// ext_cmd_line_ptr: u32 align(1), // 0x0c8
// _pad3: [0x74]u8 align(1), // 0x0cc
// edid_info: [0x80]u8 align(1), // 0x140 reserve for future use
// _pad4: [0x28]u8 align(1), // 0x1c0
// e820_entries: u8 align(1), // 0x1e8
// _pad5: [0x8]u8 align(1), // 0x1e9
// hdr: SetupHeader align(1), // 0x1f1
// _pad6: [0x2d0 - 0x1f1 - @sizeOf(SetupHeader)]u8 align(1),
// // e820_table: [E820_MAX_ENTRIES_BP]e820.E820Entry align(1), // 0x2d0
// _pad7: [0x330]u8 align(1), // 0xcd0
// };
// comptime {
// const assert = std.debug.assert;
// // assert(@sizeOf(BootParams) == 0x1000);
// assert(@sizeOf(SetupHeader) == 0x07b);
// }
pub const BootParams = struct {
cmdline: []u8
};

View File

@@ -0,0 +1,13 @@
const std = @import("std");
extern const compressed_kernel_data: u8;
extern const compressed_kernel_size: u8;
extern const uncompressed_kernel_size: u8;
pub fn getCompressedKernelData() []const u8 {
return @as([*]const u8, @ptrCast(&compressed_kernel_data))[0..@as(usize, @intFromPtr(&compressed_kernel_size))];
}
pub fn getUncompressedKernelSize() usize {
return @as(usize, @intFromPtr(&uncompressed_kernel_size));
}

View File

@@ -0,0 +1,20 @@
const std = @import("std");
const elf = std.elf;
extern var __rela_dyn_start: u8;
extern var __rela_dyn_end: u8;
pub fn relocateSelf(base_addr: usize) callconv(.c) void {
const rela_size = (@intFromPtr(&__rela_dyn_end) - @intFromPtr(&__rela_dyn_start)) / @sizeOf(elf.Elf64_Rela);
const relas = @as([*]elf.Elf64_Rela, @ptrCast(@alignCast(&__rela_dyn_start)))[0..rela_size];
for (relas) |rela| {
const offset = rela.r_offset;
const addend = rela.r_addend;
// const sym_index = rela.r_info >> 32;
const type_ = rela.r_info & 0xffffffff;
if (type_ == @intFromEnum(elf.R_X86_64.RELATIVE)) {
const reloc_addr: *u64 = @ptrFromInt(offset + base_addr);
reloc_addr.* = base_addr + @as(usize, @intCast(addend));
}
}
}

View File

@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
// Ported from Linux kernel include/uapi/linux/screen_info.h
const std = @import("std");
pub const ScreenInfo = extern struct{
orig_x: u8 align(1), // 0x00
orig_y: u8 align(1), // 0x01
ext_mem_k: u16 align(1), // 0x02
orig_video_page: u16 align(1), // 0x04
orig_video_mode: u8 align(1), // 0x06
orig_video_cols: u8 align(1), // 0x07
flags: u8 align(1), // 0x08
unused2: u8 align(1), // 0x09
orig_video_ega_bx: u16 align(1), // 0x0a
unused3: u16 align(1), // 0x0c
orig_video_lines: u8 align(1), // 0x0e
orig_video_isVGA: u8 align(1), // 0x0f
orig_video_points: u16 align(1), // 0x10
// VESA graphic mode -- linear frame buffer
lfb_width: u16 align(1), // 0x12
lfb_height: u16 align(1), // 0x14
lfb_depth: u16 align(1), // 0x16
lfb_base: u32 align(1), // 0x18
lfb_size: u32 align(1), // 0x1c
cl_magic: u16 align(1), // 0x20
cl_offset: u16 align(1), // 0x22
lfb_linelength: u16 align(1), // 0x24
red_size: u8 align(1), // 0x26
red_pos: u8 align(1), // 0x27
green_size: u8 align(1), // 0x28
green_pos: u8 align(1), // 0x29
blue_size: u8 align(1), // 0x2a
blue_pos: u8 align(1), // 0x2b
rsvd_size: u8 align(1), // 0x2c
rsvd_pos: u8 align(1), // 0x2d
vesapm_seg: u16 align(1), // 0x2e
vesapm_off: u16 align(1), // 0x30
pages: u16 align(1), // 0x32
vesa_attributes: u16 align(1), // 0x34
capabilities: u32 align(1), // 0x36
ext_lfb_base: u32 align(1), // 0x3a
_reserved: [2]u8 align(1), // 0x3e
};
// Common video types
pub const VideoType = enum(u8) {
MDA = 0x10, // Monochrome Text Display
CGA = 0x11, // CGA Display
EGAM = 0x20, // EGA/VGA in Monochrome Mode
EGAC = 0x21, // EGA in Color Mode
VGAC = 0x22, // VGA+ in Color Mode
VLFB = 0x23, // VESA VGA in graphic mode
EFI = 0x70, // EFI graphic mode
};
// Video flags
pub const VIDEO_FLAGS_NOCURSOR: u8 = 1 << 0; // The video mode has no cursor set
// Video capabilities
pub const VIDEO_CAPABILITY_SKIP_QUIRKS: u32 = 1 << 0;
pub const VIDEO_CAPABILITY_64BIT_BASE: u32 = 1 << 1; // Frame buffer base is 64-bit
comptime {
std.debug.assert(@sizeOf(ScreenInfo) == 0x40);
}

View File

@@ -0,0 +1,26 @@
const std = @import("std");
const Step = std.Build.Step;
const boot_build = @import("boot/build_boot.zig");
fn buildKernel(b: *std.Build, optimize: std.builtin.OptimizeMode) *Step.Compile {
const target = b.resolveTargetQuery(.{
.cpu_arch = .x86_64,
.os_tag = .freestanding,
.abi = .none,
});
const kernel = b.addExecutable(.{
.name = "kernel",
.root_module = b.addModule("kernel", .{
.root_source_file = b.path("kernel/main.zig"),
.target = target,
.optimize = optimize,
}),
});
kernel.entry = .{ .symbol_name = "start_kernel" };
return kernel;
}
pub fn buildImage(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});
const kernel = buildKernel(b, optimize);
boot_build.buildBootImage(b, kernel);
}

43
arch/x86_64/desc.zig Normal file
View File

@@ -0,0 +1,43 @@
const processor = @import("processor.zig");
pub const GateDesc = packed struct {
offset_1: u16 = 0,
selector: u16 = 0,
ist: u3 = 0,
_reserved_1: u5 = 0,
gate_type: GateType = .interrupt,
_padding: u1 = 0,
dpl: processor.PrivilegeLevel = .Kernel,
present: u1 = 0,
offset_2: u16 = 0,
offset_3: u32 = 0,
_reserved_2: u32 = 0,
pub const GateType = enum(u4) {
interrupt = 0xe,
trap = 0xf,
};
pub fn getOffset(self: *const GateDesc) u64 {
return (@as(u64, self.offset_3) << 32) | (@as(u64, self.offset_2) << 16) | self.offset_1;
}
pub fn setOffset(self: *GateDesc, offset: u64) void {
self.offset_1 = @as(u16, offset & 0xFFFF);
self.offset_2 = @as(u16, (offset >> 16) & 0xFFFF);
self.offset_3 = @as(u32, (offset >> 32) & 0xFFFFFFFF);
}
};
pub fn loadIdt(idt_table: []const GateDesc) void {
var idt_ptr: packed struct {
limit: u16,
base: u64,
} = .{
.limit = @as(u16, @sizeOf(GateDesc) * idt_table.len - 1),
.base = @intFromPtr(&idt_table[0]),
};
asm volatile (
\\ lidt %rax
:
: [idt_ptr] "{rax}" (&idt_ptr)
: .{}
);
}

19
arch/x86_64/io.zig Normal file
View File

@@ -0,0 +1,19 @@
pub fn outb(port: u16, value: u8) void {
asm volatile (
\\outb %al, %dx
:
: [port] "{dx}" (port), [value] "{al}" (value)
: .{}
);
}
pub fn inb(port: u16) u8 {
var value: u8 = 0;
asm volatile (
\\inb %dx, %al
: [value] "={al}" (value)
: [port] "{dx}" (port)
: .{}
);
return value;
}

43
arch/x86_64/mem.zig Normal file
View File

@@ -0,0 +1,43 @@
pub const PAGE_SHIFT: usize = 12;
pub const PAGE_SIZE: usize = 1 << PAGE_SHIFT;
pub const PTE_SHIFT: u32 = 12;
pub const PMD_SHIFT: u32 = 21;
pub const PUD_SHIFT: u32 = 30;
pub const PGD_SHIFT: u32 = 39;
pub const PTE_SIZE: usize = 1 << PTE_SHIFT;
pub const PMD_SIZE: usize = 1 << PMD_SHIFT;
pub const PUD_SIZE: usize = 1 << PUD_SHIFT;
pub const PGD_SIZE: usize = 1 << PGD_SHIFT;
pub const PTE_MASK: usize = 1 << PTE_SHIFT - 1;
pub const PMD_MASK: usize = 1 << PMD_SHIFT - 1;
pub const PUD_MASK: usize = 1 << PUD_SHIFT - 1;
pub const PGD_MASK: usize = 1 << PGD_SHIFT - 1;
pub const ENTRIES_PER_TABLE: usize = PAGE_SIZE / 64;
pub const PFType = struct{
pub const PROT: usize = 0;
pub const WRITE: usize = 1<<1;
pub const USER: usize = 1<<2;
pub const RSVD: usize = 1<<3;
pub const INSTR: usize = 1<<4;
pub const PK: usize = 1<<5;
pub const SHSTK: usize = 1<<6;
};
pub const PGDEntry = packed struct {
present: u1,
write: u1,
supervisor: u1,
write_through: u1,
cache_disable: u1,
accessed: u1,
_padding_1: u6,
addr: u40,
pub fn getIndex(self: usize) usize {
return (self >> PGD_SHIFT) & 0x1FF;
}
};

44
arch/x86_64/processor.zig Normal file
View File

@@ -0,0 +1,44 @@
pub fn readCr2() u64 {
return asm volatile (
\\ mov %cr2, %rax
: [ret] "={rax}" (-> u64),
:
: .{});
}
pub fn readCr3() u64 {
return asm volatile (
\\ mov %cr3, %rax
: [ret] "={rax}" (-> u64),
:
: .{});
}
pub const PrivilegeLevel = enum(u4) {
Kernel = 0,
User = 3,
};
pub const PtRegs = struct {
r15: usize,
r14: usize,
r13: usize,
r12: usize,
bp: usize,
bx: usize,
r11: usize,
r10: usize,
r9: usize,
r8: usize,
ax: usize,
cx: usize,
dx: usize,
si: usize,
di: usize,
code: usize,
ip: usize,
cs: usize,
flags: usize,
sp: usize,
ss: usize,
};

26
arch/x86_64/trap.zig Normal file
View File

@@ -0,0 +1,26 @@
pub const TrapType = enum(u8) {
DE = 0, // Divide-by-zero
DB = 1, // Debug
NMI = 2, // Non-maskable Interrupt
BP = 3, // Breakpoint
OF = 4, // Overflow
BR = 5, // Bound Range Exceeded
UD = 6, // Invalid Opcode
NM = 7, // Device Not Available
DF = 8, // Double Fault
OLD_MF = 9, // Coprocessor Segment Overrun
TS = 10, // Invalid TSS
NP = 11, // Segment Not Present
SS = 12, // Stack Segment Fault
GP = 13, // General Protection Fault
PF = 14, // Page Fault
SPURIOUS = 15, // Spurious Interrupt
MF = 16, // x87 Floating-Point Exception
AC = 17, // Alignment Check
MC = 18, // Machine Check
XF = 19, // SIMD Floating-Point Exception
VE = 20, // Virtualization Exception
CP = 21, // Control Protection Exception
VC = 29, // VMM Communication Exception
IRET = 32, // IRET Exception
};