gather kernel tunables into config.zig
max_cpus, max_tasks, kernel/IST stack sizes, and timer_hz move from scattered constants into one config module. root.zig goes back to being just the boot contract. Values unchanged; any can become a -D build option later.
This commit is contained in:
@@ -61,6 +61,13 @@ pub fn build(b: *std.Build) void {
|
|||||||
.root_source_file = b.path("src/root.zig"),
|
.root_source_file = b.path("src/root.zig"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Kernel tunables (max_cpus, stack sizes, tick rate). A dependency-free module of
|
||||||
|
// compile-time constants, imported wherever a knob is read; keeps the trade-offs
|
||||||
|
// in one place instead of scattered across the tree. See src/config.zig.
|
||||||
|
const config_mod = b.addModule("config", .{
|
||||||
|
.root_source_file = b.path("src/config.zig"),
|
||||||
|
});
|
||||||
|
|
||||||
// Architecture-specific kernel code (CPU ops, entry, later GDT/IDT/paging).
|
// Architecture-specific kernel code (CPU ops, entry, later GDT/IDT/paging).
|
||||||
// The generic kernel imports this as "arch" and never names x86_64, so a new
|
// The generic kernel imports this as "arch" and never names x86_64, so a new
|
||||||
// architecture is a matter of pointing this module at a different directory.
|
// architecture is a matter of pointing this module at a different directory.
|
||||||
@@ -68,6 +75,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
.root_source_file = b.path("src/kernel/arch/x86_64/cpu.zig"),
|
.root_source_file = b.path("src/kernel/arch/x86_64/cpu.zig"),
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "danos", .module = mod }, // paging uses the shared BootInfo/memory-map types
|
.{ .name = "danos", .module = mod }, // paging uses the shared BootInfo/memory-map types
|
||||||
|
.{ .name = "config", .module = config_mod }, // max_cpus, ist_stack_size, timer_hz
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// CPU-exception stubs — real assembly, since they need cross-symbol
|
// CPU-exception stubs — real assembly, since they need cross-symbol
|
||||||
@@ -86,6 +94,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
.root_source_file = b.path("src/device/platform.zig"),
|
.root_source_file = b.path("src/device/platform.zig"),
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "danos", .module = mod }, // BootInfo (carries the ACPI RSDP)
|
.{ .name = "danos", .module = mod }, // BootInfo (carries the ACPI RSDP)
|
||||||
|
.{ .name = "config", .module = config_mod }, // max_cpus (the discovery pool)
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,6 +131,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
.{ .name = "danos", .module = mod },
|
.{ .name = "danos", .module = mod },
|
||||||
.{ .name = "arch", .module = arch_mod },
|
.{ .name = "arch", .module = arch_mod },
|
||||||
.{ .name = "platform", .module = platform_mod },
|
.{ .name = "platform", .module = platform_mod },
|
||||||
|
.{ .name = "config", .module = config_mod },
|
||||||
.{ .name = "build_options", .module = build_options_mod },
|
.{ .name = "build_options", .module = build_options_mod },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
//! Kernel tunables — the compile-time knobs, gathered in one place.
|
||||||
|
//!
|
||||||
|
//! These constants would otherwise be scattered across the files that use them,
|
||||||
|
//! hiding the trade-offs. Keeping them here makes them visible at a glance and gives
|
||||||
|
//! one spot to change them. They're plain `comptime` constants (zero runtime cost);
|
||||||
|
//! any one can later be promoted to a `-D` build option if a target needs to vary it
|
||||||
|
//! (see build.zig's `-Dtest-case` for the pattern). This keeps root.zig to what it
|
||||||
|
//! actually is — the bootloader↔kernel handoff *contract* — with tunables living here.
|
||||||
|
|
||||||
|
/// Ceiling on logical CPUs the kernel tracks — the size of the per-CPU bookkeeping
|
||||||
|
/// arrays (discovery pool, scheduler state, per-core GDT/TSS). Generous headroom:
|
||||||
|
/// those structs are small, and the *large* per-core resources (kernel and IST
|
||||||
|
/// stacks) are allocated at bring-up for cores that actually come online, so this
|
||||||
|
/// ceiling is cheap. A machine with more logical CPUs has its surplus reported and
|
||||||
|
/// left parked (see acpi `cpusDropped`).
|
||||||
|
pub const max_cpus = 128;
|
||||||
|
|
||||||
|
/// Maximum tasks (kernel threads) alive at once — the static task-table size. Each
|
||||||
|
/// online core consumes one slot for its idle task, plus task 0 on the BSP.
|
||||||
|
pub const max_tasks = 16;
|
||||||
|
|
||||||
|
/// Each task's kernel stack (also each AP's bring-up stack), in bytes.
|
||||||
|
pub const kernel_stack_size = 16 * 1024;
|
||||||
|
|
||||||
|
/// Each core's IST (double-fault) stack, in bytes. The BSP's is static; an AP's is
|
||||||
|
/// heap-allocated at bring-up.
|
||||||
|
pub const ist_stack_size = 16 * 1024;
|
||||||
|
|
||||||
|
/// Scheduler tick / preemption rate, in Hz (the timer's periodic frequency).
|
||||||
|
pub const timer_hz = 1000;
|
||||||
+2
-1
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const danos = @import("danos");
|
const danos = @import("danos");
|
||||||
|
const config = @import("config");
|
||||||
const device = @import("device.zig");
|
const device = @import("device.zig");
|
||||||
const aml = @import("aml/aml.zig");
|
const aml = @import("aml/aml.zig");
|
||||||
const DeviceTree = device.DeviceTree;
|
const DeviceTree = device.DeviceTree;
|
||||||
@@ -116,7 +117,7 @@ pub const CpuInfo = struct {
|
|||||||
dropped: usize = 0,
|
dropped: usize = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const max_cpus = danos.max_cpus;
|
const max_cpus = config.max_cpus;
|
||||||
|
|
||||||
/// Filled in by `discover` (from the MADT); SMP bring-up reads it to wake the APs.
|
/// Filled in by `discover` (from the MADT); SMP bring-up reads it to wake the APs.
|
||||||
pub var cpu_info: CpuInfo = .{};
|
pub var cpu_info: CpuInfo = .{};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
//! (halt, the descriptor tables, later paging), and nothing generic.
|
//! (halt, the descriptor tables, later paging), and nothing generic.
|
||||||
|
|
||||||
const danos = @import("danos");
|
const danos = @import("danos");
|
||||||
|
const config = @import("config");
|
||||||
const gdt = @import("gdt.zig");
|
const gdt = @import("gdt.zig");
|
||||||
const tss = @import("tss.zig");
|
const tss = @import("tss.zig");
|
||||||
const idt = @import("idt.zig");
|
const idt = @import("idt.zig");
|
||||||
@@ -148,8 +149,8 @@ pub fn pageExecutable(virt: u64) bool {
|
|||||||
return paging.isExecutable(virt);
|
return paging.isExecutable(virt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kernel tick rate: 1000 Hz (1 ms), the scheduler's time quantum.
|
/// Kernel tick rate (the scheduler's time quantum), from config.
|
||||||
pub const timer_hz = 1000;
|
pub const timer_hz = config.timer_hz;
|
||||||
|
|
||||||
/// The ACPI PM timer, as a calibration reference (re-exported for the config).
|
/// The ACPI PM timer, as a calibration reference (re-exported for the config).
|
||||||
pub const PmTimer = apic.PmTimer;
|
pub const PmTimer = apic.PmTimer;
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
//! tss.zig). Two cores can't share one TSS descriptor slot, so each core gets its
|
//! tss.zig). Two cores can't share one TSS descriptor slot, so each core gets its
|
||||||
//! own copy of the table with its own TSS descriptor. Slot 0 is the BSP.
|
//! own copy of the table with its own TSS descriptor. Slot 0 is the BSP.
|
||||||
|
|
||||||
const danos = @import("danos");
|
const config = @import("config");
|
||||||
|
|
||||||
/// Selectors into the table (index * 8). Same on every core's GDT.
|
/// Selectors into the table (index * 8). Same on every core's GDT.
|
||||||
pub const kernel_code = 0x08;
|
pub const kernel_code = 0x08;
|
||||||
pub const kernel_data = 0x10;
|
pub const kernel_data = 0x10;
|
||||||
pub const tss_selector = 0x18;
|
pub const tss_selector = 0x18;
|
||||||
|
|
||||||
const max_cpus = danos.max_cpus;
|
const max_cpus = config.max_cpus;
|
||||||
const entries = 5; // null, code, data, TSS-low, TSS-high
|
const entries = 5; // null, code, data, TSS-low, TSS-high
|
||||||
|
|
||||||
/// The shared descriptors (slots 0-2); slots 3-4 hold this core's TSS descriptor,
|
/// The shared descriptors (slots 0-2); slots 3-4 hold this core's TSS descriptor,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
//! once can't share one fault stack. So the TSS and its IST stack are per-core,
|
//! once can't share one fault stack. So the TSS and its IST stack are per-core,
|
||||||
//! indexed by CPU number; slot 0 is the BSP.
|
//! indexed by CPU number; slot 0 is the BSP.
|
||||||
|
|
||||||
const danos = @import("danos");
|
const config = @import("config");
|
||||||
const gdt = @import("gdt.zig");
|
const gdt = @import("gdt.zig");
|
||||||
|
|
||||||
/// x86_64 TSS. `packed` because several 64-bit fields sit at 4-byte-unaligned
|
/// x86_64 TSS. `packed` because several 64-bit fields sit at 4-byte-unaligned
|
||||||
@@ -35,8 +35,8 @@ const Tss = packed struct {
|
|||||||
/// The IST slot (1-based, as the IDT gate encodes it) used for critical faults.
|
/// The IST slot (1-based, as the IDT gate encodes it) used for critical faults.
|
||||||
pub const double_fault_ist = 1;
|
pub const double_fault_ist = 1;
|
||||||
|
|
||||||
const max_cpus = danos.max_cpus;
|
const max_cpus = config.max_cpus;
|
||||||
pub const ist_stack_size = 16 * 1024;
|
pub const ist_stack_size = config.ist_stack_size;
|
||||||
|
|
||||||
/// One TSS per core (small — kept static). The IST stacks are 16 KiB each, so only
|
/// One TSS per core (small — kept static). The IST stacks are 16 KiB each, so only
|
||||||
/// the **BSP's** is static: it must exist before the frame allocator does, to catch a
|
/// the **BSP's** is static: it must exist before the frame allocator does, to catch a
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const danos = @import("danos");
|
const danos = @import("danos");
|
||||||
|
const config = @import("config");
|
||||||
const arch = @import("arch");
|
const arch = @import("arch");
|
||||||
const console = @import("console.zig");
|
const console = @import("console.zig");
|
||||||
const log = @import("log.zig");
|
const log = @import("log.zig");
|
||||||
@@ -278,7 +279,7 @@ fn bringUpSecondaries() void {
|
|||||||
log.print("\ndanos: bringing up {d} application processor(s)\n", .{cores.len - 1});
|
log.print("\ndanos: bringing up {d} application processor(s)\n", .{cores.len - 1});
|
||||||
const max_wake_attempts = 3; // a core that misses the first INIT-SIPI-SIPI gets retried
|
const max_wake_attempts = 3; // a core that misses the first INIT-SIPI-SIPI gets retried
|
||||||
for (cores[1..], 1..) |core, index| {
|
for (cores[1..], 1..) |core, index| {
|
||||||
const stack = heap.allocator().alloc(u8, 16 * 1024) catch {
|
const stack = heap.allocator().alloc(u8, config.kernel_stack_size) catch {
|
||||||
log.print(" cpu apic_id {d}: no stack; skipped\n", .{core.apic_id});
|
log.print(" cpu apic_id {d}: no stack; skipped\n", .{core.apic_id});
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
//! shared queues.
|
//! shared queues.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const danos = @import("danos");
|
const config = @import("config");
|
||||||
const arch = @import("arch");
|
const arch = @import("arch");
|
||||||
const heap = @import("heap.zig");
|
const heap = @import("heap.zig");
|
||||||
const sync = @import("sync.zig");
|
const sync = @import("sync.zig");
|
||||||
@@ -27,8 +27,8 @@ const sync = @import("sync.zig");
|
|||||||
pub const Priority = u3;
|
pub const Priority = u3;
|
||||||
const num_priorities = 8;
|
const num_priorities = 8;
|
||||||
|
|
||||||
const stack_size = 16 * 1024; // each task's kernel stack is 16 KiB
|
const stack_size = config.kernel_stack_size; // each task's kernel stack
|
||||||
const max_tasks = 16; // the maximum number of tasks alive at once is 16 in a static sized pool
|
const max_tasks = config.max_tasks; // maximum tasks alive at once (static pool)
|
||||||
|
|
||||||
const State = enum { free, ready, running, blocked };
|
const State = enum { free, ready, running, blocked };
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ pub const PerCpu = struct {
|
|||||||
pinned_bitmap: u8 = 0,
|
pinned_bitmap: u8 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const max_cpus = danos.max_cpus;
|
const max_cpus = config.max_cpus;
|
||||||
var cpus = [_]PerCpu{.{}} ** max_cpus;
|
var cpus = [_]PerCpu{.{}} ** max_cpus;
|
||||||
|
|
||||||
/// This core's per-CPU state, via the arch layer's GS-base pointer. Valid only once
|
/// This core's per-CPU state, via the arch layer's GS-base pointer. Valid only once
|
||||||
|
|||||||
@@ -45,14 +45,6 @@ pub const Framebuffer = extern struct {
|
|||||||
/// targets so far.
|
/// targets so far.
|
||||||
pub const page_size = 4096;
|
pub const page_size = 4096;
|
||||||
|
|
||||||
/// Ceiling on logical CPUs the kernel tracks — the size of the per-CPU bookkeeping
|
|
||||||
/// arrays (discovery pool, scheduler state, per-core GDT/TSS). A generous headroom:
|
|
||||||
/// these structs are small (a few hundred bytes each), and the *large* per-core
|
|
||||||
/// resources (IST/kernel stacks) are allocated at bring-up, only for cores that
|
|
||||||
/// actually come online — so this ceiling costs little. A machine with more logical
|
|
||||||
/// CPUs than this has its surplus reported and left parked (see acpi `cpusDropped`).
|
|
||||||
pub const max_cpus = 128;
|
|
||||||
|
|
||||||
/// danos's own classification of a span of physical memory — deliberately not
|
/// danos's own classification of a span of physical memory — deliberately not
|
||||||
/// UEFI's vocabulary. Each boot path (UEFI now, device tree later) translates its
|
/// UEFI's vocabulary. Each boot path (UEFI now, device tree later) translates its
|
||||||
/// native memory description into these kinds, so the kernel never learns what
|
/// native memory description into these kinds, so the kernel never learns what
|
||||||
|
|||||||
Reference in New Issue
Block a user