initial commit

This commit is contained in:
2025-05-09 23:16:25 +08:00
commit 207c775cf5
59 changed files with 4113 additions and 0 deletions

59
lib/kernel/io/IoMap.zig Normal file
View 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
View 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
View 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");

View File

@@ -0,0 +1 @@
pub const serial = @import("serial/serial.zig");

View 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;
}
};
}

View 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;
}
};

View 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
View 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
View File

View 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));
}
};
}

View 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
View 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
View 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
View 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;