Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e1ba1c0
netd takes this machine's address from the network
Japabu Sep 8, 2026
220305b
The cable: the T14's own NIC, and a boot that answers a ping on it
Japabu Sep 8, 2026
938957a
The address the loop pings is the claimed function's, not the machine…
Japabu Sep 8, 2026
24625c6
Merge the MSI claim onto the cable's branch
Japabu Sep 8, 2026
a85520a
The 32-bit window is a free run, and this kernel cannot yet prove one…
Japabu Sep 8, 2026
6738cbe
Merge the low-space survey onto the cable's branch
Japabu Sep 8, 2026
0a5717f
Runs 29 and 30 recorded, and the track file says where the laptop stands
Japabu Sep 8, 2026
ccda08b
A reply is the boot's only if the boot's own records bracket it
Japabu Sep 8, 2026
f817503
The judge moves to bootlog, and a half-written readback is refused by…
Japabu Sep 8, 2026
fe442b1
netd holds every resolver a lease can carry, and asks under its own n…
Japabu Sep 8, 2026
0ca8d42
The prose the review refused, deleted
Japabu Sep 8, 2026
751e40e
`--nic` is a flag that describes a boot, and the test that says so na…
Japabu Sep 8, 2026
36ef73b
Three comments that restate the declaration above them
Japabu Sep 8, 2026
e6efb81
The lancase registration no longer says the router records this machi…
Japabu Sep 8, 2026
f651400
netd asks for its own resolver count, and one writer both takes and d…
Japabu Sep 13, 2026
cd755d5
A reply is placed by a measured clock offset, not by a window wider t…
Japabu Sep 13, 2026
f8d21d9
The harness refuses the boots it cannot run, and lan_hold sleeps a bo…
Japabu Sep 13, 2026
07eb6ec
A reply is this boot's by a distance the clocks support, not by one s…
Japabu Sep 13, 2026
ebf5323
The pcap walk's two happy arms are one, and the issue keeps its readings
Japabu Sep 13, 2026
0a70cc2
Merge origin/msi-claim into bar-placement
Japabu Sep 13, 2026
3a6492f
Merge origin/bar-window into bar-placement
Japabu Sep 13, 2026
872a332
Merge origin/lan-metal into bar-placement
Japabu Sep 13, 2026
5578617
A BAR goes where the machine proves it decodes, and firmware's answer…
Japabu Sep 13, 2026
1977e95
Merge origin/host-bridge-aperture into bar-placement
Japabu Sep 13, 2026
d512765
Merge origin/host-bridge-abi into bar-placement
Japabu Sep 13, 2026
62cc957
`bar_placement_is_proven` reads records off a console, where every li…
Japabu Sep 13, 2026
c1783d5
A BAR firmware assigned no address has no answer to settle a candidat…
Japabu Sep 13, 2026
5fc3053
Firmware says where a BAR may go, because a load at an address no bri…
Japabu Sep 13, 2026
3644a86
A claimed function says when its first message arrives, because a cou…
Japabu Sep 13, 2026
2e7aa4d
Run 36 read the I219's silence, and two tracker files say what it means
Japabu Sep 13, 2026
01c6c2e
A read is issued only at an address this kernel can name, and one add…
Japabu Sep 13, 2026
eaaaea5
An unknown flag boots QEMU, and this agent found out by doing it
Japabu Sep 13, 2026
eab1e5b
A run answers the part of it a window holds, and a BAR nothing can se…
Japabu Sep 13, 2026
4204e09
The prose the second review refused, deleted
Japabu Sep 13, 2026
6276fc8
Run 42 priced the cable's boot, and the ping it answered is the judge…
Japabu Sep 14, 2026
cfec6de
A BAR this machine settles nothing against is counted where it is kep…
Japabu Sep 14, 2026
2898c0f
Merge origin/main into bar-placement
Japabu Sep 14, 2026
41a038c
The metal loop asks its own ICMP question, and `toyos-metal` reads it…
Japabu Sep 14, 2026
89a1daa
The echo request's checksum is pinned to its field, and the reply to …
Japabu Sep 14, 2026
d8477a8
The loader logs the ranges it hands over, not the whole memory space map
Japabu Sep 14, 2026
eecba96
CI priced the three markers, and the one that waits out a bound is Ni…
Japabu Sep 14, 2026
ffd3f9c
The retry's exit condition names which of the two windows has to be a…
Japabu Sep 14, 2026
ca50ef7
The loader names the range it has no room for, and the panel's price …
Japabu Sep 14, 2026
0cd6965
The seed order claim is deleted from the issue
Japabu Sep 14, 2026
338306f
Merge origin/main into bar-placement
Japabu Sep 16, 2026
e6d32cf
Merge remote-tracking branch 'origin/main' into bar-placement
Japabu Sep 21, 2026
d41cbfb
Merge origin/main into bar-placement: MSI delivery under BAR placemen…
Japabu Sep 21, 2026
1909355
Merge origin/main (the governance cut, #475) into bar-placement
Japabu Sep 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ fatfs = "0.3.6"
fontdue = "0.9"
gpt = "3.1.0"
# `statvfs` for `worktree::free_bytes`; `getloadavg` and the libproc pair
# (`proc_listallpids`/`proc_pidpath`) for gate A's host-conditions line.
# (`proc_listallpids`/`proc_pidpath`) for gate A's host-conditions line; and the
# unprivileged ICMP datagram socket `icmp::echo` asks a metal boot over, which
# is the platform call that replaces a `ping` binary.
libc = "0.2"
toml = "0.8"
uuid = { version = "1", features = ["v4"] }
Expand Down
174 changes: 174 additions & 0 deletions bootloader/src/gcd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
//! Which memory-mapped address space this platform declared, and which of it
//! nothing owns.
//!
//! **The firmware's account is read; the address space is never probed**,
//! because a load no bridge forwards does not come back on this hardware.
//!
//! The Global Coherency Domain is DXE's own map of the physical address space
//! (PI 1.8 Vol. 2 §7.2). EDK2's `PciHostBridgeDxe` adds each root bridge's
//! Mem, PMem and MemAbove4G apertures to it as
//! [`MEMORY_MAPPED_IO`] and allocates every BAR it assigns out of them, so an
//! MMIO descriptor with no owner is aperture the bridges decode and nothing
//! has taken. That is the necessary condition
//! `EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL`'s `Configuration()` cannot give: it
//! answers a bridge's *current* settings, which a BAR may lie outside.
//!
//! It has to be asked here, like the root bridge protocol: DXE services die
//! with boot services.

use core::ffi::c_void;

use toyos_abi::boot::RootBridgeWindow;
use uefi::prelude::*;
use uefi::{guid, Guid};

const HEAD: &str = "GCD:";

/// The configuration table entry carrying [`DxeServices`] (PI 1.8 Vol. 2 §7.1).
const DXE_SERVICES_TABLE_GUID: Guid = guid!("05ad34ba-6f02-4214-952e-4da0398e2bb9");

/// `EFI_DXE_SERVICES_TABLE_SIGNATURE`: the eight bytes `DXE_SERV`, read as a
/// little-endian word. Checked because a configuration table entry is a pointer
/// firmware wrote and this loader calls through it.
const SIGNATURE: u64 = u64::from_le_bytes(*b"DXE_SERV");

/// `EfiGcdMemoryTypeMemoryMappedIo` of `EFI_GCD_MEMORY_TYPE` (PI 1.8 Vol. 2
/// §7.2.1).
const MEMORY_MAPPED_IO: u32 = 3;

/// `EFI_GCD_MEMORY_SPACE_DESCRIPTOR` (PI 1.8 Vol. 2 §7.2.1), in the spec's own
/// field order.
#[repr(C)]
#[derive(Clone, Copy)]
struct MemorySpace {
base: u64,
length: u64,
capabilities: u64,
attributes: u64,
/// `EFI_GCD_MEMORY_TYPE`, which the descriptor carries as a C enum and
/// therefore as four bytes.
kind: u32,
/// The image that allocated this range, and the device it was allocated
/// for. **Both null is the whole of what "free" means here** (PI 1.8
/// Vol. 2 §7.2.3: `AllocateMemorySpace` sets them and `FreeMemorySpace`
/// clears them).
image: *mut c_void,
device: *mut c_void,
}

/// The enum is four bytes and the two handles are eight each, so the u32 is
/// followed by four bytes of padding; a struct the compiler laid out otherwise
/// would read every field past `kind` off by that much.
const _: () = assert!(core::mem::size_of::<MemorySpace>() == 56);
const _: () = assert!(core::mem::offset_of!(MemorySpace, image) == 40);

/// `DXE_SERVICES` (PI 1.8 Vol. 2 §7.1), in the spec's own field order. Only
/// `get_memory_space_map` is called; the rest are here because their order is
/// what puts it where it is.
#[repr(C)]
struct DxeServices {
signature: u64,
revision: u32,
header_size: u32,
crc32: u32,
reserved: u32,
add_memory_space: *mut c_void,
allocate_memory_space: *mut c_void,
free_memory_space: *mut c_void,
remove_memory_space: *mut c_void,
get_memory_space_descriptor: *mut c_void,
set_memory_space_attributes: *mut c_void,
get_memory_space_map: unsafe extern "efiapi" fn(
count: *mut usize,
map: *mut *mut MemorySpace,
) -> Status,
add_io_space: *mut c_void,
allocate_io_space: *mut c_void,
free_io_space: *mut c_void,
remove_io_space: *mut c_void,
get_io_space_descriptor: *mut c_void,
get_io_space_map: *mut c_void,
dispatch: *mut c_void,
schedule: *mut c_void,
trust: *mut c_void,
process_firmware_volume: *mut c_void,
set_memory_space_capabilities: *mut c_void,
}

/// A field added or dropped above moves `get_memory_space_map`, and the symptom
/// is firmware being asked to add memory space instead of describing it: the
/// 24-byte table header and eighteen pointers.
const _: () = assert!(core::mem::size_of::<DxeServices>() == 24 + 18 * 8);
const _: () = assert!(core::mem::offset_of!(DxeServices, get_memory_space_map) == 24 + 6 * 8);

/// The smallest run worth handing the kernel: the only page it maps.
const GRANULE: u64 = 2 * 1024 * 1024;

/// Append every memory-mapped range this platform declared and nothing owns to
/// `out`, and answer how many were added.
pub fn free_mmio(system_table: &SystemTable<Boot>, out: &mut [RootBridgeWindow]) -> usize {
let Some(entry) =
system_table.config_table().iter().find(|e| e.guid == DXE_SERVICES_TABLE_GUID)
else {
println!("{HEAD} no configuration table carries DXE services, so none of this machine's declared address space is known");
return 0;
};
// SAFETY: the entry firmware installed under the DXE services GUID points
// at that table for the life of boot services, and the signature below is
// what says the pointer is the table rather than a coincidence of GUIDs.
let services = unsafe { &*(entry.address as *const DxeServices) };
if services.signature != SIGNATURE {
println!(
"{HEAD} the DXE services table signature is {:#018x} and not {SIGNATURE:#018x}, so it is not called",
services.signature
);
return 0;
}

let mut count: usize = 0;
let mut map: *mut MemorySpace = core::ptr::null_mut();
// SAFETY: the call is the spec's — two out parameters, which firmware fills
// with a count and a pool buffer it allocated and this loader frees below.
let status = unsafe { (services.get_memory_space_map)(&mut count, &mut map) };
if !status.is_success() || map.is_null() {
println!("{HEAD} GetMemorySpaceMap() answered {status:?}, so none of this machine's declared address space is known");
return 0;
}

let mut added = 0usize;
let mut refused = 0usize;
for index in 0..count {
// SAFETY: firmware answered `count` descriptors at `map`, and `index`
// is inside that count.
let space = unsafe { *map.add(index) };
let free = space.image.is_null() && space.device.is_null();
if space.kind != MEMORY_MAPPED_IO || !free || space.length < GRANULE {
continue;
}
// Every range this function found, whether it fits or not: one the
// kernel has no room for is aperture this machine has and the
// placement will not use.
let slot = out.get_mut(added);
println!(
"{HEAD} {:#x}+{:#x} mmio cap={:#x} attr={:#x} free{}",
space.base,
space.length,
space.capabilities,
space.attributes,
if slot.is_some() { "" } else { ", past the room the kernel is handed" },
);
match slot {
Some(slot) => {
*slot = RootBridgeWindow { base: space.base, length: space.length };
added += 1;
}
None => refused += 1,
}
}

// SAFETY: `map` is the pool buffer `GetMemorySpaceMap` allocated, nothing
// else holds it, and every descriptor has been copied out of it above.
let _ = unsafe { system_table.boot_services().free_pool(map as *mut u8) };
println!("{HEAD} {count} descriptor(s); {added} free mmio range(s) of {GRANULE:#x} bytes or more handed to the kernel, {refused} past its room");
added
}
9 changes: 7 additions & 2 deletions bootloader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ macro_rules! println {
mod attempt;
mod blackbox;
mod bootnext;
mod gcd;
mod loaderlog;
mod rootbridge;
mod watchdog;
Expand Down Expand Up @@ -576,9 +577,13 @@ fn report_reach(what: &str, at: u64, len: u64) {
fn start_kernel(kernel: LoadedKernel, kernel_elf_bytes: vec::Vec<u8>, cmdline: vec::Vec<u8>, rsdp_addr: u64, gop: Option<GopInfo>, boot_part: Option<BootPartition>, log_partition_guid: [u8; 16], rtc_utc_offset: Option<i32>, system_table: SystemTable<Boot>) -> ! {
// The last of the firmware questions, and asked here for the same reason
// the GOP's was asked before this: the protocol dies with boot services.
//
// Both readers answer memory the root bridges decode, which is what this
// array carries and the only thing the kernel asks of it.
let mut root_bridge_windows = [RootBridgeWindow::default(); MAX_ROOT_BRIDGE_WINDOWS];
let root_bridge_window_count =
rootbridge::windows(&system_table, &mut root_bridge_windows) as u64;
let named = rootbridge::windows(&system_table, &mut root_bridge_windows);
let free = gcd::free_mmio(&system_table, &mut root_bridge_windows[named..]);
let root_bridge_window_count = (named + free) as u64;

// Pre-allocated before exiting boot services, and flat: `alloc_page` splits
// it into 512-entry pages.
Expand Down
18 changes: 0 additions & 18 deletions issues/build/toyos-metal-parses-its-own-argv.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
status: open
kind: finding
opened: 2026-09-08
---

# `getsockname` answers an address nobody asked netd for

`userland/libc/src/socket.rs`'s `getsockname()` fills the caller's `sockaddr`
with `[10, 0, 2, 15]` and the socket's local port. The address is a literal in
that function; netd is not asked, and the SDK has nothing to ask it with —
`toyos::net` carries `tcp_connect`, `tcp_bind`, `tcp_accept`, the UDP calls and
`dns_lookup`, and no call that answers "what is this machine's address".

It was true by coincidence until now: netd carried the same literal, so the
shim and the stack agreed. netd takes its address from DHCP as of the change
that filed this, so the two agree only on a machine whose server happens to
lease `10.0.2.15` — QEMU's user-mode backend does, and the bench's router does
not. Every C program that asks what address it is bound to is told the wrong
one there.

What it costs to fix is a message type on netd's protocol and one plumbed
answer; what it costs to leave is that the one caller of `getsockname` in a
POSIX program is the one that then advertises an address nothing can reach.
Nothing in the tree reads it today, which is why this is a finding and not a
defect.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
status: open
kind: defect
opened: 2026-09-14
---

# The panel seeds the loader's own file, so the GCD map is logged nowhere

`/system/bin/console` seeds the screen from every `*.log` file in `/log`, newest
two, oldest first (`userland/console/src/main.rs:277-286`). `/log` is the root of
the log partition and the bootloader writes `loader.log` there
(`userland/logd/src/store.rs:8-9`, `bootloader/src/loaderlog.rs:51`), so
`loader.log` passes the extension filter. A line the loader writes to its file costs a row of the
kernel's log on the panel exactly as a line it prints to the firmware console
does — the file is not a second channel.

Measured on `bar-placement` at `ffd3f9c2` with the loader's whole GCD map moved
off the console and into `loader.log` alone, by `loaderlog::line` and no
`println!`:

cargo test screen_console_shell EXIT=1
FAIL screen_console_shell: no `i8042:` line above the prompt, and the
console seeded 22093 bytes of kernel log

and all 34 of the q35 map's lines are in the decoded panel, above a prompt with
no kernel line left over it. The same head with those lines written to neither
channel is `cargo test screen_console_shell` **EXIT=0**, `PASS (2s)`, `30 kernel
log rows above a prompt`.

What that costs today: `bootloader/src/gcd.rs` prints one line per free MMIO
range it hands the kernel and no line for any other descriptor, so the map's
held MMIO descriptors — firmware's *current* allocations, the reading that lets
that file's module header be checked against a real machine — reach no console
and no file. Eighteen of the ThinkPad T14's 75 descriptors are MMIO and five
are logged; eleven of q35's 34 are MMIO and two are logged.

The fix is `/system/bin/console`'s, at `userland/console/src/main.rs:277-286`,
and nobody is holding it. What pays for it is `toyos-metal`'s readback of
`loader.log` (`src/metal.rs:1849`): a machine whose aperture is in neither
channel cannot have that premise checked against it afterwards.

**Exit condition.** `/system/bin/console`'s seed takes only the files
`/system/bin/logd` wrote — `src/bootlog.rs:175-187` already splits a `/log`
listing that way for the host-side reader, so the rule exists and the guest does
not apply it. With that in place `loader.log` costs no panel row, the loader
writes one line per descriptor to its file alone, and `cargo test
screen_console_shell` stays green with the whole map in the readback.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
status: open
kind: tooling
opened: 2026-09-13
---

# The cable judge spends two premises nothing has measured

`tests/common/lan.rs`'s `on_metal` decides whether a ping the metal loop saw was
this boot's, and two of its steps rest on readings nobody has taken.

**The T14's RTC is unchanged across the reset.** `Driver::wire` reads
`date -u +%s` under Ubuntu before the flash and
`bootlog::host_second_inside_this_boot` spends that offset on records ToyOS wrote
after it; a machine whose firmware or whose kernel moved the counter would be
judged against a clock that no longer exists. Closed by one boot: a ToyOS
record's wall clock read back against `date -u +%s` on the machine after it, with
the loop's own skew applied — or by the judge ceasing to compare the two
operating systems' clocks at all.

**The router repeats the lease across the two operating systems.** The same
judge refuses a boot whose leased address is not the one the loop pinged, and
the one the loop pinged is what Ubuntu held on that MAC. A server that hands the
MAC a different address under ToyOS reds the arm for a fact about the router
rather than about the boot — a red naming the wrong thing, not a false green.
Closed by a lancase run whose lease record and whose `ping_addr` are compared,
which is the first thing that run prints.
Loading
Loading