Discovery-migration prerequisites (M19.0)
The host bridge now carries MMIO apertures derived from the boot memory map's gaps below 4 GiB (largest three, sort-merged; a single after-the- last-region hole dies on OVMF's flash at the top) plus one aperture above the described space — so a user-space device_register of PCI functions with BAR resources can pass containment. The discovery test asserts every PCI memory resource lies inside a bridge window and names any escapee. device_register is idempotent on exact (parent, class, identity, resources) match — a restarted registering bus cannot duplicate its children; proven directly against the broker in the bus test. ChildAdded gains device_id so a report can carry the registered kernel id a matched driver needs as its assignment.
This commit is contained in:
@@ -283,6 +283,34 @@ fn discoveryTest() void {
|
||||
check("PCI functions were enumerated (MCFG/ECAM)", pci_functions >= 1);
|
||||
check("each PCI function exposes its ECAM config space as resource 0", pci_config_ok);
|
||||
|
||||
// M19.0: every PCI memory resource (config slice and BARs alike) must be
|
||||
// contained in one of its parent bridge's windows — the aperture derivation
|
||||
// from the memory map is what makes a future user-space device_register of
|
||||
// these functions pass containment. This is the assert that catches a
|
||||
// too-coarse hole computation before M19.2 would.
|
||||
var bars_contained = true;
|
||||
for (buffer[0..n]) |d| {
|
||||
if (d.class != @intFromEnum(device_abi.DeviceClass.pci_device)) continue;
|
||||
if (d.parent >= n) {
|
||||
bars_contained = false;
|
||||
continue;
|
||||
}
|
||||
const bridge = buffer[@intCast(d.parent)];
|
||||
for (d.resources[0..@intCast(d.resource_count)]) |r| {
|
||||
if (r.kind != @intFromEnum(device_abi.ResourceKind.memory)) continue;
|
||||
var inside = false;
|
||||
for (bridge.resources[0..@intCast(bridge.resource_count)]) |w| {
|
||||
if (w.kind != @intFromEnum(device_abi.ResourceKind.memory)) continue;
|
||||
if (r.start >= w.start and r.start + r.len <= w.start + w.len) inside = true;
|
||||
}
|
||||
if (!inside) {
|
||||
bars_contained = false;
|
||||
log(" escaping BAR: 0x{x}+0x{x} on device {d}\n", .{ r.start, r.len, d.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
check("every PCI BAR lies inside a bridge aperture (M19.0)", bars_contained);
|
||||
|
||||
result();
|
||||
}
|
||||
|
||||
@@ -2085,6 +2113,35 @@ fn hpetGsi() ?u32 {
|
||||
/// land in the device table with the containment invariant intact.
|
||||
fn busTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: bus\n", .{});
|
||||
|
||||
// M19.0: device_register is idempotent on exact match — a restarted
|
||||
// registering bus must not duplicate its children. Driven directly against
|
||||
// the broker: claim an unclaimed node, register the same (class, hid,
|
||||
// resourceless) child twice, expect one id and one table entry.
|
||||
{
|
||||
const me = scheduler.currentId();
|
||||
var probe: [1]device_abi.DeviceDescriptor = undefined;
|
||||
const total = devices_broker.enumerate(&probe);
|
||||
check("device tree is seeded for the idempotence check", total >= 1);
|
||||
if (devices_broker.ownerOf(0) == null) {
|
||||
check("claimed device 0 for the idempotence check", devices_broker.claim(0, me));
|
||||
var child = std.mem.zeroes(device_abi.DeviceDescriptor);
|
||||
child.class = @intFromEnum(device_abi.DeviceClass.unknown);
|
||||
child.pci_class = device_abi.no_pci_class;
|
||||
child.hid_len = 4;
|
||||
child.hid[0..4].* = "idem".*;
|
||||
const first = devices_broker.register(0, me, &child) catch 0;
|
||||
check("first register succeeded", first != 0);
|
||||
const before = devices_broker.enumerate(&probe);
|
||||
const second = devices_broker.register(0, me, &child) catch 0;
|
||||
check("re-register returned the same id", second == first);
|
||||
check("re-register grew nothing", devices_broker.enumerate(&probe) == before);
|
||||
devices_broker.releaseAllOwnedBy(me);
|
||||
} else {
|
||||
check("device 0 unexpectedly claimed before the idempotence check", false);
|
||||
}
|
||||
}
|
||||
|
||||
if (boot_information.initial_ramdisk_len == 0) {
|
||||
check("bootloader handed over an initial_ramdisk", false);
|
||||
result();
|
||||
|
||||
Reference in New Issue
Block a user