diff --git a/build/linker-scripts/x86_64.lds b/build/linker-scripts/x86_64.lds index 55eacd2..a9cad22 100644 --- a/build/linker-scripts/x86_64.lds +++ b/build/linker-scripts/x86_64.lds @@ -77,6 +77,13 @@ SECTIONS __percpu_end = .; } :data + .data.tunable : { + __tunables_start = .; + KEEP(*(.data.tunable .data.tunable.*)) + __tunables_end = .; + } :data + + .data : { *(.data .data.*) } :data diff --git a/src/kern/init.zig b/src/kern/init.zig index 3c012cf..4d0176f 100644 --- a/src/kern/init.zig +++ b/src/kern/init.zig @@ -11,6 +11,7 @@ var stack: [r.kib(16)]u8 align(16) = undefined; pub fn init(boot_info: *pl.BootInfo) linksection(r.init) void { ki.impl.early_init(); + ki.tunable.init(boot_info); std.log.info("Welcome to the machine", .{}); pl.early_init(boot_info); ki.log.init(); diff --git a/src/kern/private.zig b/src/kern/private.zig index 663ed25..530e880 100644 --- a/src/kern/private.zig +++ b/src/kern/private.zig @@ -89,3 +89,4 @@ pub const turnstile = @import("turnstile.zig"); pub const mutex = @import("mutex.zig"); pub const queue = @import("queue.zig"); pub const shootdown = @import("shootdown.zig"); +pub const tunable = @import("tunable.zig"); diff --git a/src/kern/root.zig b/src/kern/root.zig index 1a54990..0c21c7b 100644 --- a/src/kern/root.zig +++ b/src/kern/root.zig @@ -20,6 +20,7 @@ pub const Event = p.event.Event; pub const Queue = p.queue.Queue; pub const Mutex = p.mutex.Mutex; pub const ShootdownState = p.shootdown.ShootdownState; +pub const Tunable = p.tunable.Tunable; // === Exported Interfaces === pub const panic = p.panic.panic; diff --git a/src/kern/tunable.zig b/src/kern/tunable.zig new file mode 100644 index 0000000..398d744 --- /dev/null +++ b/src/kern/tunable.zig @@ -0,0 +1,103 @@ +//! Tunable kernel parameters. +//! These are variables set in the cmdline at boot and never changed. +const std = @import("std"); +const rtl = @import("rtl"); +const r = @import("root"); +const pl = r.pl; + +const Entry = struct { + name: []const u8, + address: *anyopaque, + type: union(enum) { + num: std.builtin.Type.Int, + bool: void, + }, +}; + +pub fn Tunable(comptime T: type, comptime default: T, comptime name: []const u8) type { + return struct { + var storage: T = default; + + const metadata: Entry linksection(".data.tunable") = + .{ + .name = name, + .address = &storage, + .type = if (T == bool) + .bool + else switch (@typeInfo(T)) { + .int => |info| .{ .num = info }, + else => @compileError("unsupported type"), + }, + }; + + pub fn load() T { + _ = metadata; + return storage; + } + }; +} + +extern var __tunables_start: u8; +extern var __tunables_end: u8; + +pub fn init(boot_info: *pl.BootInfo) void { + const cmdline = boot_info.cmdline orelse return; + + const start = @intFromPtr(&__tunables_start); + const end = @intFromPtr(&__tunables_end); + const count = (end - start) / @sizeOf(Entry); + const entries: [*]Entry = @ptrFromInt(start); + + for (0..count) |i| { + switch (entries[i].type) { + .num => |info| { + const a = entries[i].address; + + switch (info.signedness) { + .unsigned => { + const value = rtl.cmdline.get_number(u64, cmdline, entries[i].name) orelse { + std.log.warn("Invalid number value for '{s}', falling back.", .{entries[i].name}); + continue; + }; + + switch (info.bits) { + 8 => (@as(*u8, @ptrCast(@alignCast(a)))).* = std.math.cast(u8, value) orelse continue, + 16 => (@as(*u16, @ptrCast(@alignCast(a)))).* = std.math.cast(u16, value) orelse continue, + 32 => (@as(*u32, @ptrCast(@alignCast(a)))).* = std.math.cast(u32, value) orelse continue, + 64 => (@as(*u64, @ptrCast(@alignCast(a)))).* = value, + else => unreachable, + } + }, + + .signed => { + const value = rtl.cmdline.get_number(i64, cmdline, entries[i].name) orelse { + std.log.warn("Invalid number value for '{s}', falling back.", .{entries[i].name}); + continue; + }; + + switch (info.bits) { + 8 => (@as(*i8, @ptrCast(@alignCast(a)))).* = std.math.cast(i8, value) orelse continue, + 16 => (@as(*i16, @ptrCast(@alignCast(a)))).* = std.math.cast(i16, value) orelse continue, + 32 => (@as(*i32, @ptrCast(@alignCast(a)))).* = std.math.cast(i32, value) orelse continue, + 64 => (@as(*i64, @ptrCast(@alignCast(a)))).* = value, + else => unreachable, + } + }, + } + }, + + .bool => { + const value = rtl.cmdline.get_string(cmdline, entries[i].name) orelse continue; + const ptr: *bool = @ptrCast(@alignCast(entries[i].address)); + + if (std.mem.eql(u8, value, "true")) { + ptr.* = true; + } else if (std.mem.eql(u8, value, "false")) { + ptr.* = false; + } else { + std.log.warn("Invalid cmdline boolean value for tunable '{s}'", .{entries[i].name}); + } + }, + } + } +} diff --git a/src/rtl/cmdline.zig b/src/rtl/cmdline.zig index a66cce1..2c00afd 100644 --- a/src/rtl/cmdline.zig +++ b/src/rtl/cmdline.zig @@ -16,11 +16,11 @@ pub fn get_string(cmdline: []const u8, key: []const u8) ?[]const u8 { return null; } -/// Get the integer value of `key` in `cmdline`. -pub fn get_number(cmdline: []const u8, key: []const u8) ?i64 { +/// Get the number value of `key` in `cmdline`. +pub fn get_number(comptime T: type, cmdline: []const u8, key: []const u8) ?T { const str = get_string(cmdline, key); if (str) |s| { - const res = std.fmt.parseInt(i64, s, 10) catch return null; + const res = std.fmt.parseInt(T, s, 10) catch return null; return res; } return null;