Split the system contract into boot-handoff / abi / device-abi
The `system` module (formerly `danos`) had become a grab-bag: it held the
loader<->kernel handoff *and* the kernel<->user ABI *and* the device wire types, in
one module three different audiences imported. Usage proved the seam — the
bootloader never touched the syscall/device ABI, and user space never touched the
boot handoff — so split it by audience, one module per contract:
system/boot-handoff.zig loader <-> kernel: BootInformation, Framebuffer,
MemoryMap, the VM layout + physicalToVirtual, kernel_abi
system/abi.zig kernel <-> user, core: SystemCall, mmap prot flags,
page_size, notify_badge_bit, ServiceId
system/devices/device-abi.zig kernel <-> user, devices: DeviceDescriptor,
DeviceClass, ResourceDescriptor, ResourceKind, ...
device-abi is the devices sub-project's public interface, exposed as its own module
the way vfs exposes vfs-protocol — importable by user space, unlike the
kernel-internal device model it also feeds. That collapses a real duplication:
DeviceClass and ResourceKind were defined twice (device-model.zig and the contract,
kept "in sync by hand"); device-model now re-exports them from device-abi, so the
enum a driver matches on and the one the kernel classifies with are one type.
Each import now declares which contract it speaks: the bootloader imports only
boot-handoff; a driver only abi + device-abi (via the runtime); the kernel all
three. This also retires the `system` / `runtime.system` name overlap. page_size
lands in abi (it's part of the mmap contract user space aligns to); the bootloader
keeps its own local 4 KiB constant so it depends on nothing but the handoff.
All 21 importers rewired, docs updated to keep /system mapping to source. Build,
host tests, and the QEMU suite (36/36) all green.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
//! The **kernel ↔ user** ABI: the core contract every user program speaks to the
|
||||
//! kernel — the system_call numbers, `mmap` protection flags, the page size those
|
||||
//! calls work in, and the IPC name-registry ids and notification bit. Shared by the
|
||||
//! kernel dispatcher (system/kernel/process.zig) and the user runtime library
|
||||
//! (library/runtime/), so the two can never drift.
|
||||
//!
|
||||
//! This is the *core* ABI; the device half — `DeviceDescriptor` and friends, which
|
||||
//! also cross this boundary — lives with the device sub-project as [[device-abi]]
|
||||
//! (system/devices/device-abi.zig). The loader↔kernel handoff is [[boot-handoff]].
|
||||
|
||||
/// Page size every `mmap`/`munmap` grant and the boot memory map are measured in.
|
||||
/// 4 KiB on every architecture danos targets so far. Part of the ABI because user
|
||||
/// code aligns to it (grants are page-granular) and the kernel guarantees it.
|
||||
pub const page_size = 4096;
|
||||
|
||||
/// The kernel system_call numbers — the single source of truth shared by the kernel
|
||||
/// dispatcher (system/kernel/process.zig) and the user runtime library, so the two
|
||||
/// can never drift. The set is deliberately microkernel-minimal: file/device I/O
|
||||
/// is not here — it lives in user-space servers reached through the IPC calls.
|
||||
/// The table grows one milestone at a time; see docs/syscall.md.
|
||||
pub const SystemCall = enum(u64) {
|
||||
exit = 0, // exit(code): end the calling process
|
||||
yield = 1, // yield(): give up the rest of this quantum
|
||||
debug_write = 2, // debug_write(ptr, len): raw bytes to the kernel log (bring-up only)
|
||||
sleep = 3, // sleep(ms): block the caller for ms milliseconds
|
||||
mmap = 4, // mmap(len, prot) -> base: grant zeroed, page-aligned user pages
|
||||
munmap = 5, // munmap(base, len): release pages from a prior mmap
|
||||
create_endpoint = 6, // create_endpoint() -> handle: a new IPC endpoint
|
||||
ipc_register = 7, // ipc_register(service_id, handle): publish an endpoint by well-known id
|
||||
ipc_lookup = 8, // ipc_lookup(service_id) -> handle: find a published endpoint
|
||||
ipc_call = 9, // ipc_call(h, message, len, reply, cap) -> reply_len: send + block for reply
|
||||
ipc_reply_wait = 10, // ipc_reply_wait(h, reply, len, receive, cap) -> receive_len (+badge in rdx)
|
||||
device_enumerate = 11, // device_enumerate(buffer, maximum) -> count: snapshot the device table
|
||||
device_claim = 12, // device_claim(id) -> ok: take exclusive ownership of a device
|
||||
mmio_map = 13, // mmio_map(id, resource_index) -> vaddr: map a claimed device's MMIO into this AS
|
||||
irq_bind = 14, // irq_bind(id, resource_index, endpoint): deliver a device IRQ as an IPC notification
|
||||
irq_ack = 15, // irq_ack(id, resource_index): re-arm a bound IRQ after servicing it
|
||||
device_register = 16, // device_register(parent_id, descriptor) -> id: publish a child of a device you claimed
|
||||
_,
|
||||
};
|
||||
|
||||
/// Set in the badge returned by `ipc_reply_wait` when what arrived is an
|
||||
/// **asynchronous notification** (today: a device interrupt bound with `irq_bind`)
|
||||
/// rather than a message from a client. There is no payload and no reply owed; the
|
||||
/// low bits carry the source, a GSI. Shared so the kernel's ISR and the driver's
|
||||
/// event loop can't disagree about which bit means "the hardware spoke".
|
||||
pub const notify_badge_bit: u64 = 1 << 63;
|
||||
|
||||
/// Well-known IPC service ids for the bootstrap name registry (create_endpoint +
|
||||
/// ipc_register/ipc_lookup). Small integers, so no string interning is needed
|
||||
/// during bring-up. The VFS server registers under `vfs`; clients look it up.
|
||||
pub const ServiceId = enum(u32) {
|
||||
vfs = 1,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Protection flags for `mmap` (matching the usual C bit values).
|
||||
pub const prot_read: u64 = 1;
|
||||
pub const prot_write: u64 = 2;
|
||||
pub const prot_exec: u64 = 4;
|
||||
@@ -1,8 +1,11 @@
|
||||
//! Shared definitions that form the contract between a bootloader
|
||||
//! (boot/, e.g. efi.zig built as BOOTX64.efi) and the kernel (system/kernel/kernel.zig).
|
||||
//! The **loader ↔ kernel** contract: everything a bootloader (boot/, e.g. efi.zig
|
||||
//! built as BOOTX64.efi) and the kernel (system/kernel/kernel.zig) must agree on to
|
||||
//! hand control over — the handoff structures the loader fills in, plus the kernel's
|
||||
//! virtual-memory layout and the physical↔virtual addressing both sides use.
|
||||
//!
|
||||
//! Both binaries import this as the "danos" module, so the handoff layout is
|
||||
//! defined in exactly one place.
|
||||
//! Both binaries import this as the `boot-handoff` module, so the layout is defined
|
||||
//! in exactly one place. **User space never sees this** — the kernel↔user contract is
|
||||
//! [[abi]] (system/abi.zig); device types are [[device-abi]] (system/devices/device-abi.zig).
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
@@ -41,10 +44,6 @@ pub const Framebuffer = extern struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// Page size the memory map is measured in. 4 KiB on every architecture danos
|
||||
/// targets so far.
|
||||
pub const page_size = 4096;
|
||||
|
||||
/// The kernel's virtual-memory layout (higher-half). The kernel is linked at
|
||||
/// `kernel_virt_base` but loaded at a low physical address; all of RAM (and the
|
||||
/// device MMIO windows) is also mapped at `physmap_base + physical`, so the kernel
|
||||
@@ -58,105 +57,6 @@ pub const page_size = 4096;
|
||||
pub const physmap_base: u64 = 0xFFFF_8800_0000_0000;
|
||||
pub const kernel_virt_base: u64 = 0xFFFF_FFFF_8000_0000;
|
||||
|
||||
/// The kernel system_call numbers — the single source of truth shared by the kernel
|
||||
/// dispatcher (system/kernel/process.zig) and the user runtime library, so the two
|
||||
/// can never drift. The set is deliberately microkernel-minimal: file/device I/O
|
||||
/// is not here — it lives in user-space servers reached through the IPC calls.
|
||||
/// The table grows one milestone at a time; see docs/syscall.md.
|
||||
pub const SystemCall = enum(u64) {
|
||||
exit = 0, // exit(code): end the calling process
|
||||
yield = 1, // yield(): give up the rest of this quantum
|
||||
debug_write = 2, // debug_write(ptr, len): raw bytes to the kernel log (bring-up only)
|
||||
sleep = 3, // sleep(ms): block the caller for ms milliseconds
|
||||
mmap = 4, // mmap(len, prot) -> base: grant zeroed, page-aligned user pages
|
||||
munmap = 5, // munmap(base, len): release pages from a prior mmap
|
||||
create_endpoint = 6, // create_endpoint() -> handle: a new IPC endpoint
|
||||
ipc_register = 7, // ipc_register(service_id, handle): publish an endpoint by well-known id
|
||||
ipc_lookup = 8, // ipc_lookup(service_id) -> handle: find a published endpoint
|
||||
ipc_call = 9, // ipc_call(h, message, len, reply, cap) -> reply_len: send + block for reply
|
||||
ipc_reply_wait = 10, // ipc_reply_wait(h, reply, len, receive, cap) -> receive_len (+badge in rdx)
|
||||
device_enumerate = 11, // device_enumerate(buffer, maximum) -> count: snapshot the device table
|
||||
device_claim = 12, // device_claim(id) -> ok: take exclusive ownership of a device
|
||||
mmio_map = 13, // mmio_map(id, resource_index) -> vaddr: map a claimed device's MMIO into this AS
|
||||
irq_bind = 14, // irq_bind(id, resource_index, endpoint): deliver a device IRQ as an IPC notification
|
||||
irq_ack = 15, // irq_ack(id, resource_index): re-arm a bound IRQ after servicing it
|
||||
device_register = 16, // device_register(parent_id, descriptor) -> id: publish a child of a device you claimed
|
||||
_,
|
||||
};
|
||||
|
||||
/// Set in the badge returned by `ipc_reply_wait` when what arrived is an
|
||||
/// **asynchronous notification** (today: a device interrupt bound with `irq_bind`)
|
||||
/// rather than a message from a client. There is no payload and no reply owed; the
|
||||
/// low bits carry the source, a GSI. Shared so the kernel's ISR and the driver's
|
||||
/// event loop can't disagree about which bit means "the hardware spoke".
|
||||
pub const notify_badge_bit: u64 = 1 << 63;
|
||||
|
||||
/// A device class, mirroring system/devices/device-model.zig's `DeviceClass` **in order**
|
||||
/// (its `@intFromEnum` values cross the system_call boundary in `DeviceDescriptor.class`).
|
||||
/// Keep the two in sync.
|
||||
pub const DeviceClass = enum(u32) {
|
||||
root,
|
||||
processor,
|
||||
interrupt_controller,
|
||||
timer,
|
||||
pci_host_bridge,
|
||||
pci_device,
|
||||
acpi_device,
|
||||
unknown,
|
||||
};
|
||||
|
||||
/// A resource kind, mirroring system/devices/device-model.zig's `ResourceKind` in order.
|
||||
pub const ResourceKind = enum(u32) {
|
||||
memory,
|
||||
io_port,
|
||||
irq,
|
||||
bus_range,
|
||||
};
|
||||
|
||||
/// One device resource, as handed to a user-space driver (flat, extern).
|
||||
pub const ResourceDescriptor = extern struct {
|
||||
kind: u64, // a ResourceKind value
|
||||
start: u64,
|
||||
len: u64,
|
||||
};
|
||||
|
||||
pub const maximum_device_resources = 8;
|
||||
|
||||
/// `DeviceDescriptor.parent` for a device with no parent — a root of the device tree.
|
||||
pub const no_parent: u64 = ~@as(u64, 0);
|
||||
|
||||
/// A device, as snapshotted for user space by `device_enumerate`. A driver scans
|
||||
/// these to find the hardware it owns, claims it, and maps its MMIO.
|
||||
///
|
||||
/// `parent` makes the table a tree rather than a list, which is what a **bus driver**
|
||||
/// needs: it claims the bus, finds the devices below it, and publishes any it
|
||||
/// discovers itself with `device_register`. A registered child's resources must lie
|
||||
/// within its parent's (the kernel enforces this) — that containment is what makes
|
||||
/// delegation safe, since a device descriptor is otherwise a licence to map physical
|
||||
/// memory.
|
||||
pub const DeviceDescriptor = extern struct {
|
||||
id: u64,
|
||||
parent: u64, // a device id, or `no_parent`
|
||||
class: u64, // a DeviceClass value
|
||||
hid_len: u64,
|
||||
resource_count: u64,
|
||||
hid: [8]u8,
|
||||
resources: [maximum_device_resources]ResourceDescriptor,
|
||||
};
|
||||
|
||||
/// Well-known IPC service ids for the bootstrap name registry (create_endpoint +
|
||||
/// ipc_register/ipc_lookup). Small integers, so no string interning is needed
|
||||
/// during bring-up. The VFS server registers under `vfs`; clients look it up.
|
||||
pub const ServiceId = enum(u32) {
|
||||
vfs = 1,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Protection flags for `mmap` (matching the usual C bit values).
|
||||
pub const prot_read: u64 = 1;
|
||||
pub const prot_write: u64 = 2;
|
||||
pub const prot_exec: u64 = 4;
|
||||
|
||||
/// Physical address -> its virtual address in the physmap. The single way the
|
||||
/// kernel dereferences a physical address once paging is up.
|
||||
///
|
||||
@@ -204,7 +104,7 @@ pub const MemoryKind = enum(u32) {
|
||||
/// firmware's variable descriptor-stride to worry about.
|
||||
pub const MemoryRegion = extern struct {
|
||||
base: u64, // physical start address
|
||||
pages: u64, // length in `page_size` units
|
||||
pages: u64, // length in 4 KiB pages (the [[abi]] `page_size` unit)
|
||||
kind: MemoryKind,
|
||||
_pad: u32 = 0,
|
||||
};
|
||||
+13
-12
@@ -15,7 +15,8 @@
|
||||
//! the `Hal.mapMmio` callback the caller supplies (the architecture VMM's map primitive).
|
||||
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const abi = @import("abi");
|
||||
const parameters = @import("parameters");
|
||||
const device_model = @import("device-model.zig");
|
||||
const aml = @import("aml/aml.zig");
|
||||
@@ -147,7 +148,7 @@ var aml_block_count: usize = 0;
|
||||
|
||||
fn addAmlBlock(sdt_physical: u64) void {
|
||||
if (aml_block_count >= aml_block_physical.len or sdt_physical == 0) return;
|
||||
const h: *const SystemDescriptorTableHeader = @ptrFromInt(system.physicalToVirtual(sdt_physical));
|
||||
const h: *const SystemDescriptorTableHeader = @ptrFromInt(boot_handoff.physicalToVirtual(sdt_physical));
|
||||
if (h.length <= @sizeOf(SystemDescriptorTableHeader)) return;
|
||||
aml_block_physical[aml_block_count] = sdt_physical + @sizeOf(SystemDescriptorTableHeader);
|
||||
aml_block_len[aml_block_count] = h.length - @sizeOf(SystemDescriptorTableHeader);
|
||||
@@ -376,14 +377,14 @@ pub fn discover(rsdp_physical: u64, device_tree: *DeviceTree, hal: Hal) !void {
|
||||
dsdt_physical = 0;
|
||||
aml_block_count = 0;
|
||||
|
||||
const rsdp: *const RootSystemDescriptionPointer = @ptrFromInt(system.physicalToVirtual(rsdp_physical));
|
||||
const rsdp: *const RootSystemDescriptionPointer = @ptrFromInt(boot_handoff.physicalToVirtual(rsdp_physical));
|
||||
if (!std.mem.eql(u8, &rsdp.signature, "RSD PTR ")) return error.BadRsdpSignature;
|
||||
// Revision 0 checksums only the first 20 bytes (the v1.0 RSDP).
|
||||
if (!checksumOk(@ptrFromInt(system.physicalToVirtual(rsdp_physical)), 20)) return error.BadRsdpChecksum;
|
||||
if (!checksumOk(@ptrFromInt(boot_handoff.physicalToVirtual(rsdp_physical)), 20)) return error.BadRsdpChecksum;
|
||||
|
||||
if (rsdp.revision >= 2) {
|
||||
const xsdp: *const ExtendedSystemDescriptorPointer = @ptrFromInt(system.physicalToVirtual(rsdp_physical));
|
||||
if (!checksumOk(@ptrFromInt(system.physicalToVirtual(rsdp_physical)), xsdp.length)) return error.BadXsdpChecksum;
|
||||
const xsdp: *const ExtendedSystemDescriptorPointer = @ptrFromInt(boot_handoff.physicalToVirtual(rsdp_physical));
|
||||
if (!checksumOk(@ptrFromInt(boot_handoff.physicalToVirtual(rsdp_physical)), xsdp.length)) return error.BadXsdpChecksum;
|
||||
try walkRoot(u64, xsdp.extended_system_descriptor_table_address, device_tree, hal);
|
||||
} else {
|
||||
try walkRoot(u32, rsdp.root_system_description_table_address, device_tree, hal);
|
||||
@@ -393,7 +394,7 @@ pub fn discover(rsdp_physical: u64, device_tree: *DeviceTree, hal: Hal) !void {
|
||||
// read the sleep types from it.
|
||||
var blocks: [aml_block_physical.len][]const u8 = undefined;
|
||||
for (0..aml_block_count) |i| {
|
||||
blocks[i] = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(aml_block_physical[i])))[0..aml_block_len[i]];
|
||||
blocks[i] = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(aml_block_physical[i])))[0..aml_block_len[i]];
|
||||
}
|
||||
const active = blocks[0..aml_block_count];
|
||||
if (aml.parse(device_tree.allocator, active)) |pr| {
|
||||
@@ -412,11 +413,11 @@ pub fn discover(rsdp_physical: u64, device_tree: *DeviceTree, hal: Hal) !void {
|
||||
/// Walk the RSDT (Entry = u32) or XSDT (Entry = u64): validate it, then dispatch
|
||||
/// each SDT it points at. A bad individual table is skipped, not fatal.
|
||||
fn walkRoot(comptime Entry: type, root_physical: u64, device_tree: *DeviceTree, hal: Hal) !void {
|
||||
const header: *const SystemDescriptorTableHeader = @ptrFromInt(system.physicalToVirtual(root_physical));
|
||||
if (!checksumOk(@ptrFromInt(system.physicalToVirtual(root_physical)), header.length)) return error.BadRootChecksum;
|
||||
const header: *const SystemDescriptorTableHeader = @ptrFromInt(boot_handoff.physicalToVirtual(root_physical));
|
||||
if (!checksumOk(@ptrFromInt(boot_handoff.physicalToVirtual(root_physical)), header.length)) return error.BadRootChecksum;
|
||||
|
||||
const count = (header.length - @sizeOf(SystemDescriptorTableHeader)) / @sizeOf(Entry);
|
||||
const base: [*]const u8 = @ptrFromInt(system.physicalToVirtual(root_physical));
|
||||
const base: [*]const u8 = @ptrFromInt(boot_handoff.physicalToVirtual(root_physical));
|
||||
const entries: [*]align(1) const Entry = @ptrCast(base + @sizeOf(SystemDescriptorTableHeader));
|
||||
|
||||
for (entries[0..count]) |ent| {
|
||||
@@ -427,7 +428,7 @@ fn walkRoot(comptime Entry: type, root_physical: u64, device_tree: *DeviceTree,
|
||||
|
||||
/// Dispatch a single SDT on its signature.
|
||||
fn handleTable(device_tree: *DeviceTree, hal: Hal, sdt_physical: u64) !void {
|
||||
const header: *const SystemDescriptorTableHeader = @ptrFromInt(system.physicalToVirtual(sdt_physical));
|
||||
const header: *const SystemDescriptorTableHeader = @ptrFromInt(boot_handoff.physicalToVirtual(sdt_physical));
|
||||
const sig = header.signature;
|
||||
if (std.mem.eql(u8, &sig, &APIC)) {
|
||||
try parseMadt(device_tree, header);
|
||||
@@ -1120,7 +1121,7 @@ fn pciConfigurationPtr(alloc: McfgAllocation, hal: Hal, bus: u8, device: u8, fun
|
||||
(@as(u64, function) << 12);
|
||||
// Map the configuration page (writable, for BAR sizing) and use the virtual
|
||||
// address the HAL hands back.
|
||||
return @ptrFromInt(hal.mapMmio(physical, system.page_size, true));
|
||||
return @ptrFromInt(hal.mapMmio(physical, abi.page_size, true));
|
||||
}
|
||||
|
||||
/// Read a little-endian integer at `off` from a (possibly unaligned) byte pointer.
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
//! The **device ABI**: the flat, `extern` device types that cross the system_call
|
||||
//! boundary — what `device_enumerate` hands a user-space driver, what
|
||||
//! `device_register` takes back. This is the devices sub-project's *public
|
||||
//! interface*, exposed as its own `device-abi` module the same way the VFS server
|
||||
//! exposes `vfs-protocol` — so both the kernel and user space depend on the contract
|
||||
//! by name, and neither reaches into the other's files.
|
||||
//!
|
||||
//! It is also the **single source of truth** for `DeviceClass` and `ResourceKind`:
|
||||
//! the kernel's rich, pointer-based device tree (system/devices/device-model.zig,
|
||||
//! which user space must never import) re-exports these, so the enum that a driver
|
||||
//! matches on and the enum the kernel classifies with are the *same* type — no
|
||||
//! hand-kept "mirror in order" to drift. The core kernel↔user ABI is [[abi]]; the
|
||||
//! loader↔kernel handoff is [[boot-handoff]].
|
||||
|
||||
/// A coarse classification of a device, independent of the describing firmware.
|
||||
/// Kept small on purpose; refine as real drivers arrive. `enum(u32)` because the
|
||||
/// `@intFromEnum` value crosses the system_call boundary in `DeviceDescriptor.class`.
|
||||
pub const DeviceClass = enum(u32) {
|
||||
/// The synthetic root every discovered device hangs beneath.
|
||||
root,
|
||||
processor,
|
||||
interrupt_controller,
|
||||
timer,
|
||||
/// A PCI(e) host bridge — the root of a PCI segment (owns an ECAM window).
|
||||
pci_host_bridge,
|
||||
/// A single PCI function.
|
||||
pci_device,
|
||||
/// A device named in the ACPI namespace (from the DSDT/SSDT), carrying a
|
||||
/// hardware ID (`_HID`) and, where static, current resource settings (`_CRS`).
|
||||
acpi_device,
|
||||
unknown,
|
||||
};
|
||||
|
||||
/// The kind of hardware resource a device occupies. `enum(u32)` for the same
|
||||
/// boundary-crossing reason as `DeviceClass` (see `ResourceDescriptor.kind`).
|
||||
pub const ResourceKind = enum(u32) {
|
||||
/// A memory-mapped I/O window: `start` is the physical base, `len` its size.
|
||||
memory,
|
||||
/// A legacy I/O-port range: `start` is the first port, `len` the count.
|
||||
io_port,
|
||||
/// An interrupt: `start` is the global system interrupt (GSI), `len` is 1.
|
||||
irq,
|
||||
/// A range of bus numbers owned by a bridge: `start`..`start+len`.
|
||||
bus_range,
|
||||
};
|
||||
|
||||
/// One device resource, as handed to a user-space driver (flat, extern).
|
||||
pub const ResourceDescriptor = extern struct {
|
||||
kind: u64, // a ResourceKind value
|
||||
start: u64,
|
||||
len: u64,
|
||||
};
|
||||
|
||||
pub const maximum_device_resources = 8;
|
||||
|
||||
/// `DeviceDescriptor.parent` for a device with no parent — a root of the device tree.
|
||||
pub const no_parent: u64 = ~@as(u64, 0);
|
||||
|
||||
/// A device, as snapshotted for user space by `device_enumerate`. A driver scans
|
||||
/// these to find the hardware it owns, claims it, and maps its MMIO.
|
||||
///
|
||||
/// `parent` makes the table a tree rather than a list, which is what a **bus driver**
|
||||
/// needs: it claims the bus, finds the devices below it, and publishes any it
|
||||
/// discovers itself with `device_register`. A registered child's resources must lie
|
||||
/// within its parent's (the kernel enforces this) — that containment is what makes
|
||||
/// delegation safe, since a device descriptor is otherwise a licence to map physical
|
||||
/// memory.
|
||||
pub const DeviceDescriptor = extern struct {
|
||||
id: u64,
|
||||
parent: u64, // a device id, or `no_parent`
|
||||
class: u64, // a DeviceClass value
|
||||
hid_len: u64,
|
||||
resource_count: u64,
|
||||
hid: [8]u8,
|
||||
resources: [maximum_device_resources]ResourceDescriptor,
|
||||
};
|
||||
@@ -12,6 +12,7 @@
|
||||
//! on top of this — nothing here presumes them.
|
||||
|
||||
const std = @import("std");
|
||||
const device_abi = @import("device-abi");
|
||||
|
||||
/// The hardware primitives a discovery backend needs but can't express portably.
|
||||
/// The kernel injects an implementation (the architecture VMM + port I/O), so the device
|
||||
@@ -26,17 +27,10 @@ pub const Hal = struct {
|
||||
pioWrite: *const fn (width: u8, port: u16, value: u32) void,
|
||||
};
|
||||
|
||||
/// The kind of hardware resource a device occupies.
|
||||
pub const ResourceKind = enum {
|
||||
/// A memory-mapped I/O window: `start` is the physical base, `len` its size.
|
||||
memory,
|
||||
/// A legacy I/O-port range: `start` is the first port, `len` the count.
|
||||
io_port,
|
||||
/// An interrupt: `start` is the global system interrupt (GSI), `len` is 1.
|
||||
irq,
|
||||
/// A range of bus numbers owned by a bridge: `start`..`start+len`.
|
||||
bus_range,
|
||||
};
|
||||
/// The kind of hardware resource a device occupies. Canonically defined by the
|
||||
/// device ABI (system/devices/device-abi.zig) and re-exported here, so the kernel's
|
||||
/// internal tree and the descriptors it hands user space share one enum.
|
||||
pub const ResourceKind = device_abi.ResourceKind;
|
||||
|
||||
/// One hardware resource claimed by a device.
|
||||
pub const Resource = struct {
|
||||
@@ -46,22 +40,10 @@ pub const Resource = struct {
|
||||
};
|
||||
|
||||
/// A coarse classification of a device, independent of the describing firmware.
|
||||
/// Kept small on purpose; refine as real drivers arrive.
|
||||
pub const DeviceClass = enum {
|
||||
/// The synthetic root every discovered device hangs beneath.
|
||||
root,
|
||||
processor,
|
||||
interrupt_controller,
|
||||
timer,
|
||||
/// A PCI(e) host bridge — the root of a PCI segment (owns an ECAM window).
|
||||
pci_host_bridge,
|
||||
/// A single PCI function.
|
||||
pci_device,
|
||||
/// A device named in the ACPI namespace (from the DSDT/SSDT), carrying a
|
||||
/// hardware ID (`_HID`) and, where static, current resource settings (`_CRS`).
|
||||
acpi_device,
|
||||
unknown,
|
||||
};
|
||||
/// Canonically defined by the device ABI (system/devices/device-abi.zig) and
|
||||
/// re-exported here — the enum a driver matches on and the one the kernel classifies
|
||||
/// with are the same type. Kept small on purpose; refine as real drivers arrive.
|
||||
pub const DeviceClass = device_abi.DeviceClass;
|
||||
|
||||
/// Firmware-independent identity. Each backend fills only the fields it knows;
|
||||
/// the rest stay null. The generic layer never branches on *how* an id was
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//! compile-time choice.
|
||||
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const device_model = @import("device-model.zig");
|
||||
const acpi = @import("acpi.zig");
|
||||
const power = @import("power.zig");
|
||||
@@ -65,7 +65,7 @@ pub fn cpusDropped() usize {
|
||||
/// ACPI registers); pass the architecture implementation. Errors leave nothing to clean up
|
||||
/// beyond the tree's own allocations.
|
||||
pub fn discover(
|
||||
boot_information: *const system.BootInformation,
|
||||
boot_information: *const boot_handoff.BootInformation,
|
||||
allocator: std.mem.Allocator,
|
||||
hal: Hal,
|
||||
) !DeviceTree {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//! map). Every interrupt must be acknowledged with an end-of-interrupt write, or
|
||||
//! the LAPIC won't deliver the next one.
|
||||
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const io = @import("io.zig");
|
||||
const paging = @import("paging.zig");
|
||||
|
||||
@@ -121,7 +121,7 @@ pub fn init() void {
|
||||
|
||||
const msr = io.rdmsr(ia32_apic_base_msr);
|
||||
// Reach the LAPIC through the physmap (paging.init maps its page there).
|
||||
base = @intCast(system.physicalToVirtual(msr & 0xFFFFF000)); // physical base is bits 12+
|
||||
base = @intCast(boot_handoff.physicalToVirtual(msr & 0xFFFFF000)); // physical base is bits 12+
|
||||
io.wrmsr(ia32_apic_base_msr, msr | (1 << 11)); // global enable
|
||||
|
||||
write(register_spurious, 0x100 | spurious_vector); // bit 8 = software enable
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! build.zig — no change to the generic code. Keep everything CPU-specific here
|
||||
//! (halt, the descriptor tables, later paging), and nothing generic.
|
||||
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const parameters = @import("parameters");
|
||||
const gdt = @import("gdt.zig");
|
||||
const tss = @import("tss.zig");
|
||||
@@ -132,7 +132,7 @@ pub fn init() void {
|
||||
/// Build the kernel's own page tables (with real permissions) and switch onto
|
||||
/// them. Needs the frame allocator and the boot info (for the memory map and the
|
||||
/// kernel's segment layout). Call once the frame allocator is up.
|
||||
pub fn enablePaging(allocFrame: *const fn () ?u64, freeFrame: *const fn (u64) void, boot_information: *const system.BootInformation) void {
|
||||
pub fn enablePaging(allocFrame: *const fn () ?u64, freeFrame: *const fn (u64) void, boot_information: *const boot_handoff.BootInformation) void {
|
||||
paging.init(allocFrame, freeFrame, boot_information);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
//! Everything is 4 KiB pages — precise and simple; the extra table memory is
|
||||
//! negligible against available RAM.
|
||||
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const abi = @import("abi");
|
||||
const io = @import("io.zig");
|
||||
|
||||
const page_size = system.page_size;
|
||||
const page_size = abi.page_size;
|
||||
|
||||
// Page-table entry bits.
|
||||
const present: u64 = 1 << 0;
|
||||
@@ -58,7 +59,7 @@ const bootstrap_physmap_limit: u64 = 4 << 30;
|
||||
/// both the loader's bootstrap tables and the kernel's own, which share the
|
||||
/// physmap base.
|
||||
fn tableAt(physical: u64) *[512]u64 {
|
||||
return @ptrFromInt(system.physicalToVirtual(physical));
|
||||
return @ptrFromInt(boot_handoff.physicalToVirtual(physical));
|
||||
}
|
||||
|
||||
fn allocTable() u64 {
|
||||
@@ -102,12 +103,12 @@ fn mapRangePhysmap(pml4: u64, physical_base: u64, len: u64, flags: u64) void {
|
||||
var address = physical_base & ~@as(u64, page_size - 1);
|
||||
const end = physical_base + len;
|
||||
while (address < end) : (address += page_size) {
|
||||
mapPage(pml4, system.physicalToVirtual(address), address, flags);
|
||||
mapPage(pml4, boot_handoff.physicalToVirtual(address), address, flags);
|
||||
}
|
||||
}
|
||||
|
||||
fn regions(mm: system.MemoryMap) []const system.MemoryRegion {
|
||||
return @as([*]const system.MemoryRegion, @ptrFromInt(system.physicalToVirtual(mm.regions)))[0..mm.len];
|
||||
fn regions(mm: boot_handoff.MemoryMap) []const boot_handoff.MemoryRegion {
|
||||
return @as([*]const boot_handoff.MemoryRegion, @ptrFromInt(boot_handoff.physicalToVirtual(mm.regions)))[0..mm.len];
|
||||
}
|
||||
|
||||
/// Enable the NX bit in the page-table format (EFER.NXE). Must happen before we
|
||||
@@ -118,7 +119,7 @@ fn enableNx() void {
|
||||
}
|
||||
|
||||
/// Build the address space and switch onto it.
|
||||
pub fn init(allocFrame: *const fn () ?u64, freeFrame: *const fn (u64) void, boot_information: *const system.BootInformation) void {
|
||||
pub fn init(allocFrame: *const fn () ?u64, freeFrame: *const fn (u64) void, boot_information: *const boot_handoff.BootInformation) void {
|
||||
alloc_frame = allocFrame;
|
||||
free_frame = freeFrame;
|
||||
enableNx();
|
||||
@@ -136,7 +137,7 @@ pub fn init(allocFrame: *const fn () ?u64, freeFrame: *const fn (u64) void, boot
|
||||
// the kernel touches directly), RW + NX.
|
||||
const fb = boot_information.framebuffer;
|
||||
mapRangePhysmap(pml4, fb.base, @as(u64, fb.height) * fb.pitch, present | writable | no_execute);
|
||||
mapPage(pml4, system.physicalToVirtual(0xFEE00000), 0xFEE00000, present | writable | no_execute);
|
||||
mapPage(pml4, boot_handoff.physicalToVirtual(0xFEE00000), 0xFEE00000, present | writable | no_execute);
|
||||
|
||||
// 3. The kernel's own segments at their higher-half link addresses, mapped
|
||||
// to their low physical load addresses with real ELF permissions: code
|
||||
@@ -206,11 +207,11 @@ pub fn mapMmio(physical: u64, len: u64, writable_page: bool) u64 {
|
||||
const last = physical + (if (len == 0) 1 else len) - 1;
|
||||
var address = first;
|
||||
while (address <= (last & ~@as(u64, page_size - 1))) : (address += page_size) {
|
||||
const virtual = system.physicalToVirtual(address);
|
||||
const virtual = boot_handoff.physicalToVirtual(address);
|
||||
mapPage(kernel_pml4, virtual, address, flags);
|
||||
invalidate(virtual);
|
||||
}
|
||||
return system.physicalToVirtual(physical);
|
||||
return boot_handoff.physicalToVirtual(physical);
|
||||
}
|
||||
|
||||
/// Like `descend`, but also sets the U/S bit on the intermediate entry (new or
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//! Once a core has its own descriptor tables, LAPIC, and timer, it calls the generic
|
||||
//! scheduler entry and joins the run loop — mechanism here, policy there.
|
||||
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const io = @import("io.zig");
|
||||
const gdt = @import("gdt.zig");
|
||||
const tss = @import("tss.zig");
|
||||
@@ -85,7 +85,7 @@ fn arm() void {
|
||||
const start = @extern([*]const u8, .{ .name = "ap_trampoline_start" });
|
||||
const end = @extern([*]const u8, .{ .name = "ap_trampoline_end" });
|
||||
const len = @intFromPtr(end) - @intFromPtr(start);
|
||||
const destination: [*]u8 = @ptrFromInt(system.physicalToVirtual(tramp_physical));
|
||||
const destination: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(tramp_physical));
|
||||
@memcpy(destination[0..len], start[0..len]);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ fn arm() void {
|
||||
/// reported in — it's long past the trampoline by then, in the kernel image; a
|
||||
/// core that never answered is dead and can't be mid-climb.
|
||||
fn disarm() void {
|
||||
const destination: [*]u8 = @ptrFromInt(system.physicalToVirtual(tramp_physical));
|
||||
const destination: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(tramp_physical));
|
||||
@memset(destination[0..page_size], 0);
|
||||
paging.unmap(tramp_physical); // drop the transient low identity mapping
|
||||
}
|
||||
@@ -107,7 +107,7 @@ fn disarm() void {
|
||||
fn param(comptime name: []const u8) *align(1) volatile u64 {
|
||||
const start = @intFromPtr(@extern([*]const u8, .{ .name = "ap_trampoline_start" }));
|
||||
const sym = @intFromPtr(@extern([*]const u8, .{ .name = name }));
|
||||
return @ptrFromInt(system.physicalToVirtual(tramp_physical + (sym - start)));
|
||||
return @ptrFromInt(boot_handoff.physicalToVirtual(tramp_physical + (sym - start)));
|
||||
}
|
||||
|
||||
/// Wake the core with Local APIC id `apic_id` as dense CPU `index`, hand it
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
//! never assumes a display exists.
|
||||
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
|
||||
/// The one framebuffer console, valid only when `con_present`.
|
||||
var con: Console = undefined;
|
||||
@@ -22,7 +22,7 @@ var con_present: bool = false;
|
||||
|
||||
/// Set up the console over `fb`, or mark it absent if there's no usable
|
||||
/// framebuffer. Clears the screen when present.
|
||||
pub fn init(fb: system.Framebuffer) void {
|
||||
pub fn init(fb: boot_handoff.Framebuffer) void {
|
||||
if (!fb.present()) {
|
||||
con_present = false;
|
||||
return;
|
||||
@@ -58,7 +58,7 @@ const glyph_bytes = glyph_h; // 8 pixels wide => 1 byte per row
|
||||
const glyph_data = 32; // PSF2 header size
|
||||
|
||||
pub const Console = struct {
|
||||
fb: system.Framebuffer,
|
||||
fb: boot_handoff.Framebuffer,
|
||||
cols: u32,
|
||||
rows: u32,
|
||||
col: u32 = 0,
|
||||
@@ -66,12 +66,12 @@ pub const Console = struct {
|
||||
fg: u32 = 0x00c8_c8c8, // light grey
|
||||
bg: u32 = 0x0000_0000, // black
|
||||
|
||||
pub fn init(fb: system.Framebuffer) Console {
|
||||
pub fn init(fb: boot_handoff.Framebuffer) Console {
|
||||
// Reach the framebuffer through the physmap, so the pointer stays valid
|
||||
// once the low identity map is gone. The base is mapped by both the
|
||||
// loader's bootstrap tables and paging.init.
|
||||
var mapped = fb;
|
||||
if (fb.base != 0) mapped.base = system.physicalToVirtual(fb.base);
|
||||
if (fb.base != 0) mapped.base = boot_handoff.physicalToVirtual(fb.base);
|
||||
return .{
|
||||
.fb = mapped,
|
||||
.cols = fb.width / glyph_w,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
const std = @import("std");
|
||||
const platform = @import("platform");
|
||||
const system = @import("system");
|
||||
const device_abi = @import("device-abi");
|
||||
|
||||
const maximum_devices = 64;
|
||||
|
||||
@@ -32,7 +32,7 @@ const maximum_devices = 64;
|
||||
/// `device_release` to reclaim on exit) is future work — see docs/driver-model.md.
|
||||
const maximum_children_per_parent = 16;
|
||||
|
||||
var devices: [maximum_devices]system.DeviceDescriptor = undefined;
|
||||
var devices: [maximum_devices]device_abi.DeviceDescriptor = undefined;
|
||||
var claimed: [maximum_devices]?u32 = .{null} ** maximum_devices; // owner task id, or null
|
||||
var count: usize = 0;
|
||||
|
||||
@@ -46,13 +46,13 @@ pub fn init(device_tree: *const platform.DeviceTree) void {
|
||||
count = 0;
|
||||
dropped = 0;
|
||||
for (&claimed) |*c| c.* = null;
|
||||
walk(device_tree.root, system.no_parent);
|
||||
walk(device_tree.root, device_abi.no_parent);
|
||||
}
|
||||
|
||||
/// Record `node` (unless it's the synthetic root) and recurse, threading the id we
|
||||
/// assigned it down to its children as their parent.
|
||||
fn walk(node: *platform.Device, parent_id: u64) void {
|
||||
const id = if (node.class == .root) system.no_parent else record(node, parent_id);
|
||||
const id = if (node.class == .root) device_abi.no_parent else record(node, parent_id);
|
||||
var child = node.first_child;
|
||||
while (child) |c| : (child = c.next_sibling) walk(c, id);
|
||||
}
|
||||
@@ -60,16 +60,16 @@ fn walk(node: *platform.Device, parent_id: u64) void {
|
||||
fn record(node: *platform.Device, parent_id: u64) u64 {
|
||||
if (count >= maximum_devices) {
|
||||
dropped += 1;
|
||||
return system.no_parent; // children of a dropped node become roots, not orphans
|
||||
return device_abi.no_parent; // children of a dropped node become roots, not orphans
|
||||
}
|
||||
var d = std.mem.zeroes(system.DeviceDescriptor);
|
||||
var d = std.mem.zeroes(device_abi.DeviceDescriptor);
|
||||
d.id = count;
|
||||
d.parent = parent_id;
|
||||
d.class = @intFromEnum(node.class);
|
||||
const h = node.hid();
|
||||
d.hid_len = @min(h.len, d.hid.len);
|
||||
@memcpy(d.hid[0..d.hid_len], h[0..d.hid_len]);
|
||||
const rc = @min(node.resource_count, system.maximum_device_resources);
|
||||
const rc = @min(node.resource_count, device_abi.maximum_device_resources);
|
||||
d.resource_count = rc;
|
||||
for (0..rc) |i| {
|
||||
const r = node.resources[i];
|
||||
@@ -82,7 +82,7 @@ fn record(node: *platform.Device, parent_id: u64) u64 {
|
||||
|
||||
/// Copy up to `out.len` device descriptors into `out`; returns the total count
|
||||
/// available (which may exceed `out.len`).
|
||||
pub fn enumerate(out: []system.DeviceDescriptor) usize {
|
||||
pub fn enumerate(out: []device_abi.DeviceDescriptor) usize {
|
||||
const n = @min(count, out.len);
|
||||
@memcpy(out[0..n], devices[0..n]);
|
||||
return count;
|
||||
@@ -104,7 +104,7 @@ pub fn ownerOf(id: u64) ?u32 {
|
||||
}
|
||||
|
||||
/// Resource `index` of device `id`, or null if out of range.
|
||||
pub fn resourceOf(id: u64, index: u64) ?system.ResourceDescriptor {
|
||||
pub fn resourceOf(id: u64, index: u64) ?device_abi.ResourceDescriptor {
|
||||
if (id >= count) return null;
|
||||
const d = &devices[@intCast(id)];
|
||||
if (index >= d.resource_count) return null;
|
||||
@@ -115,9 +115,9 @@ pub fn resourceOf(id: u64, index: u64) ?system.ResourceDescriptor {
|
||||
/// interval containment; for an irq it's equality, since an interrupt line is not
|
||||
/// divisible. Zero-length child ranges are refused — an empty window is meaningless
|
||||
/// and would otherwise vacuously "fit" anywhere.
|
||||
fn contains(parent: system.ResourceDescriptor, child: system.ResourceDescriptor) bool {
|
||||
fn contains(parent: device_abi.ResourceDescriptor, child: device_abi.ResourceDescriptor) bool {
|
||||
if (parent.kind != child.kind) return false;
|
||||
if (child.kind == @intFromEnum(system.ResourceKind.irq)) return parent.start == child.start;
|
||||
if (child.kind == @intFromEnum(device_abi.ResourceKind.irq)) return parent.start == child.start;
|
||||
if (child.len == 0 or parent.len == 0) return false;
|
||||
// No overflow: a resource that wraps the address space is not containable.
|
||||
const child_end = std.math.add(u64, child.start, child.len) catch return false;
|
||||
@@ -149,10 +149,10 @@ fn childCount(parent_id: u64) usize {
|
||||
/// `owner` must have claimed `parent_id`, and every resource in `descriptor` must be
|
||||
/// contained in a parent resource of the same kind. A device with no resources is
|
||||
/// fine and common: a USB device is addressed through its controller, not by MMIO.
|
||||
pub fn register(parent_id: u64, owner: u32, descriptor: *const system.DeviceDescriptor) RegisterError!u64 {
|
||||
pub fn register(parent_id: u64, owner: u32, descriptor: *const device_abi.DeviceDescriptor) RegisterError!u64 {
|
||||
const parent_owner = ownerOf(parent_id) orelse return error.BadParent;
|
||||
if (parent_owner != owner) return error.BadParent;
|
||||
if (descriptor.resource_count > system.maximum_device_resources) return error.TooManyResources;
|
||||
if (descriptor.resource_count > device_abi.maximum_device_resources) return error.TooManyResources;
|
||||
if (childCount(parent_id) >= maximum_children_per_parent) return error.TooManyChildren;
|
||||
if (count >= maximum_devices) return error.NoSpace;
|
||||
|
||||
@@ -166,7 +166,7 @@ pub fn register(parent_id: u64, owner: u32, descriptor: *const system.DeviceDesc
|
||||
if (!ok) return error.NotContained;
|
||||
}
|
||||
|
||||
var d = std.mem.zeroes(system.DeviceDescriptor);
|
||||
var d = std.mem.zeroes(device_abi.DeviceDescriptor);
|
||||
d.id = count;
|
||||
d.parent = parent_id;
|
||||
d.class = descriptor.class;
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
//! interrupt handlers (ours don't). A lock comes with threads/SMP.
|
||||
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const abi = @import("abi");
|
||||
const architecture = @import("architecture");
|
||||
const pmm = @import("pmm.zig");
|
||||
|
||||
const page_size = system.page_size;
|
||||
const page_size = abi.page_size;
|
||||
|
||||
/// Virtual base of the heap: the start of the higher half, which is unmapped and
|
||||
/// well clear of the identity-mapped low half. (Canonical on x86_64; an architecture that
|
||||
|
||||
@@ -22,13 +22,14 @@
|
||||
//! copy is a later security-track item, matching the existing debug_write gap.
|
||||
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const abi = @import("abi");
|
||||
const architecture = @import("architecture");
|
||||
const scheduler = @import("scheduler.zig");
|
||||
const sync = @import("sync.zig");
|
||||
const heap = @import("heap.zig");
|
||||
|
||||
const page_size = system.page_size;
|
||||
const page_size = abi.page_size;
|
||||
const Task = scheduler.Task;
|
||||
|
||||
/// Largest message a single call/reply may carry. Bumping it is trivial; kept
|
||||
@@ -50,8 +51,8 @@ pub const ENOMEM: i64 = 6; // out of memory
|
||||
/// message from a client — there is no reply owed. The low bits carry the source
|
||||
/// (a GSI for IRQs). Posted by `notifyFromIsr`, from the ISR in system/kernel/irq.zig;
|
||||
/// the message path uses a plain task-id badge with this bit clear. Defined in the
|
||||
/// shared contract (system/system.zig), because ring 3 has to test the same bit.
|
||||
pub const notify_badge_bit: u64 = system.notify_badge_bit;
|
||||
/// shared kernel↔user ABI (system/abi.zig), because ring 3 has to test the same bit.
|
||||
pub const notify_badge_bit: u64 = abi.notify_badge_bit;
|
||||
|
||||
/// End of the user (low) canonical half — user buffers must lie below it.
|
||||
const user_half_end: u64 = 0x0000_8000_0000_0000;
|
||||
@@ -124,8 +125,8 @@ fn copyAcross(source_as: u64, source_va: u64, destination_as: u64, destination_v
|
||||
const s_left = page_size - ((source_va + off) & (page_size - 1));
|
||||
const d_left = page_size - ((destination_va + off) & (page_size - 1));
|
||||
const n = @min(@min(s_left, d_left), len - off);
|
||||
const source: [*]const u8 = @ptrFromInt(system.physicalToVirtual(s));
|
||||
const destination: [*]u8 = @ptrFromInt(system.physicalToVirtual(d));
|
||||
const source: [*]const u8 = @ptrFromInt(boot_handoff.physicalToVirtual(s));
|
||||
const destination: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(d));
|
||||
@memcpy(destination[0..n], source[0..n]);
|
||||
off += n;
|
||||
}
|
||||
@@ -147,7 +148,7 @@ pub fn copyFromUser(user_as: u64, user_va: u64, destination: []u8) bool {
|
||||
const s = architecture.translate(user_as, user_va + off) orelse return false;
|
||||
const s_left = page_size - ((user_va + off) & (page_size - 1));
|
||||
const n = @min(s_left, destination.len - off);
|
||||
const source: [*]const u8 = @ptrFromInt(system.physicalToVirtual(s));
|
||||
const source: [*]const u8 = @ptrFromInt(boot_handoff.physicalToVirtual(s));
|
||||
@memcpy(destination[off..][0..n], source[0..n]);
|
||||
off += n;
|
||||
}
|
||||
|
||||
+13
-12
@@ -1,5 +1,6 @@
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const abi = @import("abi");
|
||||
const parameters = @import("parameters");
|
||||
const architecture = @import("architecture");
|
||||
const console = @import("console.zig");
|
||||
@@ -14,14 +15,14 @@ const initial_ramdisk = @import("initial-ramdisk");
|
||||
const platform = @import("platform");
|
||||
const tests = @import("tests.zig");
|
||||
const build_options = @import("build_options");
|
||||
const BootInformation = system.BootInformation;
|
||||
const BootInformation = boot_handoff.BootInformation;
|
||||
|
||||
/// The calling convention used to enter the kernel. Pinned to SystemV explicitly:
|
||||
/// the bootloader is built for the UEFI target, whose C convention is Microsoft
|
||||
/// x64 (first argument in RCX), while the kernel is SystemV (first argument in
|
||||
/// RDI). Both sides reference this so the `boot_information` pointer lands in the
|
||||
/// register the other expects. `system.kernel_abi` re-exports it to the loader.
|
||||
pub const kernel_abi = system.kernel_abi;
|
||||
/// register the other expects. `boot_handoff.kernel_abi` re-exports it to the loader.
|
||||
pub const kernel_abi = boot_handoff.kernel_abi;
|
||||
|
||||
// POST/checkpoint codes emitted to I/O port 0x80 at boot milestones — the
|
||||
// last-resort progress signal on a machine with no text output at all.
|
||||
@@ -50,7 +51,7 @@ var ap_trampoline_page: u64 = 0;
|
||||
/// half. `boot_information` (also low) is reached through the physmap — its base is the
|
||||
/// same under the loader's bootstrap tables and the kernel's own.
|
||||
export fn kmainEntry(boot_information: *const BootInformation) callconv(kernel_abi) noreturn {
|
||||
kmain(@ptrFromInt(system.physicalToVirtual(@intFromPtr(boot_information))));
|
||||
kmain(@ptrFromInt(boot_handoff.physicalToVirtual(@intFromPtr(boot_information))));
|
||||
}
|
||||
|
||||
fn kmain(boot_information: *const BootInformation) noreturn {
|
||||
@@ -91,7 +92,7 @@ fn kmain(boot_information: *const BootInformation) noreturn {
|
||||
|
||||
// Summarise the physical memory the loader handed us. The array is danos's
|
||||
// own MemoryRegion, so this is a plain slice — no firmware layout in sight.
|
||||
const regions = @as([*]const system.MemoryRegion, @ptrFromInt(system.physicalToVirtual(boot_information.memory_map.regions)))[0..boot_information.memory_map.len];
|
||||
const regions = @as([*]const boot_handoff.MemoryRegion, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.memory_map.regions)))[0..boot_information.memory_map.len];
|
||||
var usable_pages: u64 = 0;
|
||||
var reserved_pages: u64 = 0; // reserved RAM only — MMIO is device space, not RAM
|
||||
for (regions) |r| {
|
||||
@@ -102,7 +103,7 @@ fn kmain(boot_information: *const BootInformation) noreturn {
|
||||
}
|
||||
}
|
||||
const total_pages = usable_pages + reserved_pages;
|
||||
const total_bytes = total_pages * system.page_size;
|
||||
const total_bytes = total_pages * abi.page_size;
|
||||
const gib = 1 << 30;
|
||||
|
||||
log.write("\ndanos: physical memory\n");
|
||||
@@ -277,7 +278,7 @@ fn kmain(boot_information: *const BootInformation) noreturn {
|
||||
// borrowing. This boot context then becomes the BSP's idle loop.
|
||||
if (boot_information.init_len != 0) {
|
||||
status("starting /sbin/init...\n");
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
process.spawnProcess(image, 4) catch |err| {
|
||||
statusPrint("/sbin/init failed to load: {s}\n", .{@errorName(err)});
|
||||
};
|
||||
@@ -300,9 +301,9 @@ fn kmain(boot_information: *const BootInformation) noreturn {
|
||||
/// Spawn every program bundled in the initial_ramdisk as its own ring-3 process. A bad
|
||||
/// image or a program that fails to load is logged and skipped — the rest of the
|
||||
/// system still runs.
|
||||
fn startInitialRamdiskBinaries(boot_information: *const system.BootInformation) void {
|
||||
fn startInitialRamdiskBinaries(boot_information: *const boot_handoff.BootInformation) void {
|
||||
if (boot_information.initial_ramdisk_len == 0) return;
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
status("initial_ramdisk: bad image, skipping\n");
|
||||
return;
|
||||
@@ -386,11 +387,11 @@ fn statusPrint(comptime fmt: []const u8, args: anytype) void {
|
||||
|
||||
/// Frames (4 KiB pages) to whole MiB.
|
||||
fn mib(pages: u64) u64 {
|
||||
return pages * system.page_size / (1024 * 1024);
|
||||
return pages * abi.page_size / (1024 * 1024);
|
||||
}
|
||||
|
||||
fn kib(frames: u64) u64 {
|
||||
return frames * system.page_size / (1024);
|
||||
return frames * abi.page_size / (1024);
|
||||
}
|
||||
|
||||
/// Report a CPU exception and halt **this core**. There's no fault recovery yet, so
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
//! 4 KiB physical frames — the primitive every later memory feature (page
|
||||
//! tables, the heap) is built on top of.
|
||||
//!
|
||||
//! This is generic kernel code: it works on the neutral `system.MemoryRegion`
|
||||
//! This is generic kernel code: it works on the neutral `boot_handoff.MemoryRegion`
|
||||
//! array the loader hands over (see docs/memory-map.md), so it carries no UEFI
|
||||
//! and nothing architecture-specific beyond the 4 KiB page.
|
||||
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const abi = @import("abi");
|
||||
|
||||
const page_size = system.page_size;
|
||||
const page_size = abi.page_size;
|
||||
|
||||
/// One bit per frame, covering physical RAM from 0 up to the highest usable
|
||||
/// address: 1 = used/unavailable, 0 = free. The bitmap itself lives in a frame
|
||||
@@ -48,8 +49,8 @@ inline fn setFree(frame: usize) void {
|
||||
bitmap[frame >> 3] &= ~(@as(u8, 1) << bit(frame));
|
||||
}
|
||||
|
||||
fn regions(map: system.MemoryMap) []const system.MemoryRegion {
|
||||
return @as([*]const system.MemoryRegion, @ptrFromInt(system.physicalToVirtual(map.regions)))[0..map.len];
|
||||
fn regions(map: boot_handoff.MemoryMap) []const boot_handoff.MemoryRegion {
|
||||
return @as([*]const boot_handoff.MemoryRegion, @ptrFromInt(boot_handoff.physicalToVirtual(map.regions)))[0..map.len];
|
||||
}
|
||||
|
||||
/// Build the allocator from the loader's memory map. Reaches physical memory
|
||||
@@ -59,7 +60,7 @@ fn regions(map: system.MemoryMap) []const system.MemoryRegion {
|
||||
/// region (lowest address), which must sit under the bootstrap physmap's reach
|
||||
/// (4 GiB); it always does, as both this and the page-table allocator scan from
|
||||
/// low addresses up.
|
||||
pub fn init(map: system.MemoryMap) void {
|
||||
pub fn init(map: boot_handoff.MemoryMap) void {
|
||||
const regs = regions(map);
|
||||
|
||||
// 1. Size the bitmap to cover every frame up to the highest RAM address —
|
||||
@@ -91,7 +92,7 @@ pub fn init(map: system.MemoryMap) void {
|
||||
}
|
||||
}
|
||||
const bitmap_base = storage orelse @panic("pmm: no region large enough for the frame bitmap");
|
||||
bitmap = @as([*]u8, @ptrFromInt(system.physicalToVirtual(bitmap_base)))[0..bitmap_bytes];
|
||||
bitmap = @as([*]u8, @ptrFromInt(boot_handoff.physicalToVirtual(bitmap_base)))[0..bitmap_bytes];
|
||||
|
||||
// 3. Start with everything marked used, then free the usable regions. Doing
|
||||
// it this way means every gap, reserved span and MMIO hole is unallocatable
|
||||
|
||||
+14
-12
@@ -21,7 +21,9 @@
|
||||
|
||||
const std = @import("std");
|
||||
const elf = std.elf;
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const abi = @import("abi");
|
||||
const device_abi = @import("device-abi");
|
||||
const architecture = @import("architecture");
|
||||
const pmm = @import("pmm.zig");
|
||||
const scheduler = @import("scheduler.zig");
|
||||
@@ -31,8 +33,8 @@ const devices_broker = @import("devices-broker.zig");
|
||||
const irq = @import("irq.zig");
|
||||
const log = @import("log.zig");
|
||||
|
||||
const page_size = system.page_size;
|
||||
const SystemCall = system.SystemCall;
|
||||
const page_size = abi.page_size;
|
||||
const SystemCall = abi.SystemCall;
|
||||
|
||||
/// User virtual addresses. PML4 index 224 — a user-exclusive region, far from
|
||||
/// the identity map (low indices) and the vmm test address (index 128), so
|
||||
@@ -79,7 +81,7 @@ pub var write_from_user: bool = false;
|
||||
pub var write_count: u64 = 0; // total write syscalls served (for the heartbeat tests)
|
||||
pub var exit_code: u64 = 0;
|
||||
|
||||
/// The system_call surface, dispatched on the saved system_call number (`system.SystemCall`).
|
||||
/// The system_call surface, dispatched on the saved system_call number (`abi.SystemCall`).
|
||||
/// This is the microkernel-minimal set — memory + scheduling only; file/device
|
||||
/// I/O will arrive as IPC to user-space servers (docs/syscall.md). The result is
|
||||
/// written back into the trap frame, since the entry paths restore user registers
|
||||
@@ -203,9 +205,9 @@ fn systemDeviceEnumerate(state: *architecture.CpuState) void {
|
||||
const maximum = architecture.systemCallArg(state, 1);
|
||||
const t = scheduler.current();
|
||||
if (t.aspace == 0 or buffer_ptr >= user_half_end) return fail(state);
|
||||
const sz = @sizeOf(system.DeviceDescriptor);
|
||||
const sz = @sizeOf(device_abi.DeviceDescriptor);
|
||||
const cap = @min(maximum, (user_half_end - buffer_ptr) / sz); // clamp to the user half
|
||||
const out: [*]system.DeviceDescriptor = @ptrFromInt(buffer_ptr);
|
||||
const out: [*]device_abi.DeviceDescriptor = @ptrFromInt(buffer_ptr);
|
||||
architecture.setSystemCallResult(state, devices_broker.enumerate(out[0..@intCast(cap)]));
|
||||
}
|
||||
|
||||
@@ -228,7 +230,7 @@ fn systemMmioMap(state: *architecture.CpuState) void {
|
||||
const owner = devices_broker.ownerOf(device_id) orelse return fail(state);
|
||||
if (owner != t.id) return fail(state); // not claimed by this process
|
||||
const r = devices_broker.resourceOf(device_id, resource_index) orelse return fail(state);
|
||||
if (r.kind != @intFromEnum(system.ResourceKind.memory)) return fail(state);
|
||||
if (r.kind != @intFromEnum(device_abi.ResourceKind.memory)) return fail(state);
|
||||
|
||||
if (t.device_map_next == 0) t.device_map_next = device_arena_base;
|
||||
const first = r.start & ~@as(u64, page_size - 1);
|
||||
@@ -260,7 +262,7 @@ fn systemDeviceRegister(state: *architecture.CpuState) void {
|
||||
const t = scheduler.current();
|
||||
if (t.aspace == 0) return fail(state);
|
||||
|
||||
var descriptor: system.DeviceDescriptor = undefined;
|
||||
var descriptor: device_abi.DeviceDescriptor = undefined;
|
||||
if (!ipc.copyFromUser(t.aspace, descriptor_ptr, std.mem.asBytes(&descriptor))) return fail(state);
|
||||
|
||||
const id = devices_broker.register(parent_id, t.id, &descriptor) catch return fail(state);
|
||||
@@ -284,7 +286,7 @@ fn ownedGsi(t: *scheduler.Task, device_id: u64, resource_index: u64) ?u32 {
|
||||
const owner = devices_broker.ownerOf(device_id) orelse return null;
|
||||
if (owner != t.id) return null;
|
||||
const r = devices_broker.resourceOf(device_id, resource_index) orelse return null;
|
||||
if (r.kind != @intFromEnum(system.ResourceKind.irq)) return null;
|
||||
if (r.kind != @intFromEnum(device_abi.ResourceKind.irq)) return null;
|
||||
if (r.start >= irq.maximum_gsi) return null;
|
||||
return @intCast(r.start);
|
||||
}
|
||||
@@ -373,7 +375,7 @@ fn systemMmap(state: *architecture.CpuState) void {
|
||||
}
|
||||
|
||||
for (frames[0..pages], 0..) |frame, i| {
|
||||
const destination: [*]u8 = @ptrFromInt(system.physicalToVirtual(frame));
|
||||
const destination: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(frame));
|
||||
@memset(destination[0..page_size], 0); // hand out zeroed memory
|
||||
architecture.mapUserPageInto(t.aspace, base + i * page_size, frame, true, false); // RW + NX
|
||||
}
|
||||
@@ -428,7 +430,7 @@ pub fn run(blob: []const u8) RunError!void {
|
||||
// Fill the code frame through the physmap (supervisor RW): the user-facing
|
||||
// mapping is read-only, and this also sidesteps CR0.WP/SMAP. The tail is
|
||||
// padded with int3 so a stray jump traps instead of sliding.
|
||||
const code: [*]u8 = @ptrFromInt(system.physicalToVirtual(code_frame));
|
||||
const code: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(code_frame));
|
||||
@memcpy(code[0..blob.len], blob);
|
||||
@memset(code[blob.len..page_size], 0xCC);
|
||||
|
||||
@@ -542,7 +544,7 @@ fn parseSegments(image: []const u8, segs: *[maximum_segments]Segment) InitError!
|
||||
/// frame mapped into it — so no per-page rollback list is needed here.
|
||||
fn loadPageInto(aspace: u64, image: []const u8, seg: Segment, page_index: u64) InitError!void {
|
||||
const frame = pmm.alloc() orelse return error.OutOfMemory;
|
||||
const destination: [*]u8 = @ptrFromInt(system.physicalToVirtual(frame));
|
||||
const destination: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(frame));
|
||||
@memset(destination[0..page_size], 0);
|
||||
const page_off = page_index * page_size;
|
||||
if (page_off < seg.filesz) {
|
||||
|
||||
+30
-28
@@ -10,7 +10,9 @@
|
||||
//! exception report the handler prints (which also reaches serial).
|
||||
|
||||
const std = @import("std");
|
||||
const system = @import("system");
|
||||
const boot_handoff = @import("boot-handoff");
|
||||
const abi = @import("abi");
|
||||
const device_abi = @import("device-abi");
|
||||
const architecture = @import("architecture");
|
||||
const devices_broker = @import("devices-broker.zig");
|
||||
const platform = @import("platform");
|
||||
@@ -155,7 +157,7 @@ fn powerTest(comptime action: enum { off, reboot }) void {
|
||||
result();
|
||||
}
|
||||
|
||||
const BootInformation = system.BootInformation;
|
||||
const BootInformation = boot_handoff.BootInformation;
|
||||
|
||||
fn eql(a: []const u8, b: []const u8) bool {
|
||||
return std.mem.eql(u8, a, b);
|
||||
@@ -167,7 +169,7 @@ fn smoke(boot_information: *const BootInformation) void {
|
||||
|
||||
// The memory map has some usable RAM.
|
||||
const mm = boot_information.memory_map;
|
||||
const regions = @as([*]const system.MemoryRegion, @ptrFromInt(system.physicalToVirtual(mm.regions)))[0..mm.len];
|
||||
const regions = @as([*]const boot_handoff.MemoryRegion, @ptrFromInt(boot_handoff.physicalToVirtual(mm.regions)))[0..mm.len];
|
||||
var usable: u64 = 0;
|
||||
for (regions) |r| {
|
||||
if (r.kind == .usable) usable += r.pages;
|
||||
@@ -179,7 +181,7 @@ fn smoke(boot_information: *const BootInformation) void {
|
||||
const b = pmm.alloc();
|
||||
check("alloc returns a frame", a != null);
|
||||
check("alloc returns distinct frames", a != null and b != null and a.? != b.?);
|
||||
check("frames are page-aligned", (a orelse 1) % system.page_size == 0);
|
||||
check("frames are page-aligned", (a orelse 1) % abi.page_size == 0);
|
||||
|
||||
// Freeing restores the count.
|
||||
const before = pmm.stats().free_frames;
|
||||
@@ -189,7 +191,7 @@ fn smoke(boot_information: *const BootInformation) void {
|
||||
|
||||
// Paging is active on our own tables (the root is non-zero and page-aligned).
|
||||
const root = architecture.activePageTable();
|
||||
check("paging active (page-table root set)", root != 0 and root % system.page_size == 0);
|
||||
check("paging active (page-table root set)", root != 0 and root % abi.page_size == 0);
|
||||
|
||||
result();
|
||||
}
|
||||
@@ -575,7 +577,7 @@ fn smpTest() void {
|
||||
const tramp = architecture.trampolinePage();
|
||||
check("trampoline frame reserved", tramp != 0);
|
||||
if (tramp != 0) {
|
||||
const bytes: [*]const u8 = @ptrFromInt(system.physicalToVirtual(tramp));
|
||||
const bytes: [*]const u8 = @ptrFromInt(boot_handoff.physicalToVirtual(tramp));
|
||||
var zeroed = true;
|
||||
for (0..4096) |b| {
|
||||
if (bytes[b] != 0) zeroed = false;
|
||||
@@ -759,7 +761,7 @@ fn userMemTest() void {
|
||||
var mapped: usize = 0;
|
||||
while (mapped < npages) : (mapped += 1) {
|
||||
frames[mapped] = pmm.alloc() orelse break;
|
||||
architecture.mapUserPageInto(aspace, arena + mapped * system.page_size, frames[mapped], true, false);
|
||||
architecture.mapUserPageInto(aspace, arena + mapped * abi.page_size, frames[mapped], true, false);
|
||||
}
|
||||
check("granted three user pages", mapped == npages);
|
||||
|
||||
@@ -767,13 +769,13 @@ fn userMemTest() void {
|
||||
var translate_ok = true;
|
||||
var rw_ok = true;
|
||||
for (0..npages) |i| {
|
||||
const va = arena + i * system.page_size;
|
||||
const va = arena + i * abi.page_size;
|
||||
const physical = architecture.translate(aspace, va) orelse {
|
||||
translate_ok = false;
|
||||
continue;
|
||||
};
|
||||
if (physical != frames[i]) translate_ok = false;
|
||||
const p: [*]u8 = @ptrFromInt(system.physicalToVirtual(physical));
|
||||
const p: [*]u8 = @ptrFromInt(boot_handoff.physicalToVirtual(physical));
|
||||
p[0] = 0xA5;
|
||||
if (p[0] != 0xA5) rw_ok = false;
|
||||
}
|
||||
@@ -782,7 +784,7 @@ fn userMemTest() void {
|
||||
|
||||
// Release them the way munmap does, then tear down the address space.
|
||||
for (0..npages) |i| {
|
||||
const va = arena + i * system.page_size;
|
||||
const va = arena + i * abi.page_size;
|
||||
if (architecture.translate(aspace, va)) |physical| {
|
||||
architecture.unmapUserPageInto(aspace, va);
|
||||
pmm.free(physical);
|
||||
@@ -879,7 +881,7 @@ fn processTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
|
||||
process.write_count = 0;
|
||||
process.write_from_user = false;
|
||||
@@ -930,7 +932,7 @@ fn initTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.init_base)))[0..boot_information.init_len];
|
||||
process.write_count = 0;
|
||||
const spawned = if (process.spawnProcess(image, 4)) true else |err| blk: {
|
||||
log("DANOS-INIT-ERR: {s}\n", .{@errorName(err)});
|
||||
@@ -964,7 +966,7 @@ fn initialRamdiskTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1008,7 +1010,7 @@ fn vfsTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1071,7 +1073,7 @@ fn hpetTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1115,14 +1117,14 @@ fn hpetRouteOk() bool {
|
||||
|
||||
/// The GSI discovery recorded for the HPET, from the same device table the driver saw.
|
||||
fn hpetGsi() ?u32 {
|
||||
var buffer: [16]system.DeviceDescriptor = undefined;
|
||||
var buffer: [16]device_abi.DeviceDescriptor = undefined;
|
||||
const n = @min(devices_broker.enumerate(&buffer), buffer.len);
|
||||
for (buffer[0..n]) |d| {
|
||||
if (d.class != @intFromEnum(system.DeviceClass.timer)) continue;
|
||||
if (d.parent != system.no_parent) continue; // the block, not a comparator child
|
||||
if (d.class != @intFromEnum(device_abi.DeviceClass.timer)) continue;
|
||||
if (d.parent != device_abi.no_parent) continue; // the block, not a comparator child
|
||||
for (0..d.resource_count) |j| {
|
||||
const r = d.resources[j];
|
||||
if (r.kind == @intFromEnum(system.ResourceKind.irq)) return @intCast(r.start);
|
||||
if (r.kind == @intFromEnum(device_abi.ResourceKind.irq)) return @intCast(r.start);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -1148,7 +1150,7 @@ fn busTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1187,7 +1189,7 @@ fn deviceManagerTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(system.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
@@ -1221,7 +1223,7 @@ fn deviceManagerTest(boot_information: *const BootInformation) void {
|
||||
/// trusted and doesn't obey containment: a PCI function's BAR is not inside its host
|
||||
/// bridge's `bus_range`, because a bus-number range isn't an address window.
|
||||
fn childrenContained() bool {
|
||||
var buffer: [64]system.DeviceDescriptor = undefined;
|
||||
var buffer: [64]device_abi.DeviceDescriptor = undefined;
|
||||
const n = @min(devices_broker.enumerate(&buffer), buffer.len);
|
||||
|
||||
const bus_id = hpetDeviceId() orelse return false;
|
||||
@@ -1237,7 +1239,7 @@ fn childrenContained() bool {
|
||||
for (0..p.resource_count) |j| {
|
||||
const pr = p.resources[j];
|
||||
if (pr.kind != r.kind) continue;
|
||||
if (r.kind == @intFromEnum(system.ResourceKind.irq)) {
|
||||
if (r.kind == @intFromEnum(device_abi.ResourceKind.irq)) {
|
||||
if (pr.start == r.start) ok = true;
|
||||
} else if (r.len != 0 and r.start >= pr.start and
|
||||
r.start + r.len <= pr.start + pr.len) ok = true;
|
||||
@@ -1250,13 +1252,13 @@ fn childrenContained() bool {
|
||||
|
||||
/// Device id of the HPET (the bus bus claims), from the same table drivers see.
|
||||
fn hpetDeviceId() ?u64 {
|
||||
var buffer: [64]system.DeviceDescriptor = undefined;
|
||||
var buffer: [64]device_abi.DeviceDescriptor = undefined;
|
||||
const n = @min(devices_broker.enumerate(&buffer), buffer.len);
|
||||
for (buffer[0..n]) |d| {
|
||||
if (d.class != @intFromEnum(system.DeviceClass.timer)) continue;
|
||||
if (d.parent != system.no_parent) continue; // a comparator child, not the block
|
||||
if (d.class != @intFromEnum(device_abi.DeviceClass.timer)) continue;
|
||||
if (d.parent != device_abi.no_parent) continue; // a comparator child, not the block
|
||||
for (0..d.resource_count) |j| {
|
||||
if (d.resources[j].kind == @intFromEnum(system.ResourceKind.memory)) return d.id;
|
||||
if (d.resources[j].kind == @intFromEnum(device_abi.ResourceKind.memory)) return d.id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -1350,7 +1352,7 @@ fn ioPassTest() void {
|
||||
return;
|
||||
};
|
||||
// Map it the way mmio_map does (device grant), then tear the space down.
|
||||
architecture.mapUserDeviceInto(aspace, process.device_arena_base, frame, system.page_size);
|
||||
architecture.mapUserDeviceInto(aspace, process.device_arena_base, frame, abi.page_size);
|
||||
architecture.destroyAddressSpace(aspace);
|
||||
|
||||
// The page tables were reclaimed; the device-granted frame must not have been.
|
||||
|
||||
Reference in New Issue
Block a user