display: hot-attach a virtio-gpu native backend over the GOP floor (v2 V4)
The compositor now boots on the GOP framebuffer and upgrades to the virtio-gpu driver the moment it announces itself — the pluggable-scanout payoff. The shared surface. The scanout resource is an shm region the driver creates (shm_physical, a new syscall, hands it the guest-physical for attach_backing) and passes to the compositor as a capability. The compositor maps it and composites straight into it: on x86 DMA is cache-coherent, so the cacheable shared pages the CPU paints are exactly what the device transfers-and-flushes — no copy, no explicit flush. The handshake. After bring-up the driver looks up .display and sends attach_scanout with the geometry + the surface capability. The compositor maps the surface, looks up the driver's .scanout endpoint itself (the driver registered it — no need to pass it), switches to backend.VirtioGpu, and re-composites the current frame. present() over the native backend is a present request on .scanout -> transfer-to-host + resource flush. The first native present is deferred to a one-shot timer: presenting inline from the announce handler would deadlock, since the driver is still blocked on our reply and not yet serving .scanout. After it lands, the compositor reads a pixel back from the shared surface to confirm the frame reached the device's backing. - shm_physical (syscall 36) + runtime.shm.physical. - scanout-protocol (the compositor->driver present channel), separate from the client-facing display protocol; the display protocol gains attach_scanout. - backend.VirtioGpu joins backend.Gop in the tagged union; select() still boots GOP. - the virtio-gpu driver's scanout backing is now shm (was DMA); it announces + serves .scanout present requests (transfer-to-host + flush of the shared surface). Also fixes a latent framebuffer-geometry corruption the display service hit only when it enumerated the device tree alongside a busy device-manager: Gop.init now captures the geometry into a small value the instant device_enumerate returns (rather than re-reading the 328-byte descriptor across the later claim/mmio_map syscalls) and retries on a zero geometry. The underlying device-table clobber is a separate kernel bug, tracked apart. Gate: python3 test/qemu_test.py display-native (QEMU -device virtio-gpu-pci) — "display: scanout upgraded to virtio-gpu" + "display: native present verified" + "display-demo: ok", passing 3/3. host tests, display-service, display-demo, shm, and virtio-gpu still pass.
This commit is contained in:
@@ -105,6 +105,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void {
|
||||
shmTest(boot_information);
|
||||
} else if (eql(case, "virtio-gpu")) {
|
||||
virtioGpuTest(boot_information);
|
||||
} else if (eql(case, "display-native")) {
|
||||
displayNativeTest(boot_information);
|
||||
} else if (eql(case, "clock")) {
|
||||
clockTest();
|
||||
} else if (eql(case, "smp")) {
|
||||
@@ -2455,6 +2457,53 @@ fn virtioGpuTest(boot_information: *const BootInformation) void {
|
||||
while (true) scheduler.yield();
|
||||
}
|
||||
|
||||
/// V4 — the native backend + hot-attach (docs/display-v2.md). Boot the compositor and the
|
||||
/// hardware-free `display-demo` client (as displayDemoTest does), then the device-manager
|
||||
/// stack so it discovers the virtio-gpu function — present via QEMU's `-device
|
||||
/// virtio-gpu-pci` — and spawns the driver. The driver brings up its scanout, then announces
|
||||
/// the shared surface to the already-running compositor, which maps it, upgrades off the GOP
|
||||
/// floor, and presents the composited frame through the native backend. Its serial heartbeats
|
||||
/// — `display: scanout upgraded to virtio-gpu` and `display: native present verified` — plus
|
||||
/// the demo's own `display-demo: ok` are the harness's markers. Display is spawned first so
|
||||
/// it is registered on `.display` before the driver announces.
|
||||
fn displayNativeTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: display-native\n", .{});
|
||||
if (boot_information.initial_ramdisk_len == 0) {
|
||||
check("bootloader handed over an initial_ramdisk", false);
|
||||
result();
|
||||
return;
|
||||
}
|
||||
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();
|
||||
return;
|
||||
};
|
||||
|
||||
process.setInitialRamdisk(image);
|
||||
var manager: u32 = 0;
|
||||
var i: u32 = 0;
|
||||
while (i < rd.count) : (i += 1) {
|
||||
const item = rd.entry(i) orelse continue;
|
||||
if (!eql(item.name, "device-manager")) continue;
|
||||
manager = process.spawnProcessSupervised(item.blob, 4, &.{"device-manager"}, scheduler.currentId(), null) catch 0;
|
||||
break;
|
||||
}
|
||||
if (manager == 0) {
|
||||
log("display-native: could not spawn device-manager\n", .{});
|
||||
result();
|
||||
return;
|
||||
}
|
||||
if (!spawnNamed(rd, "display")) {
|
||||
log("display-native: could not spawn the display service\n", .{});
|
||||
result();
|
||||
return;
|
||||
}
|
||||
_ = spawnNamed(rd, "display-demo");
|
||||
scheduler.setPriority(1); // below the compositor, the demo, and the driver, so they run
|
||||
while (true) scheduler.yield();
|
||||
}
|
||||
|
||||
/// Process arguments, end to end: spawn args-echo bare (its argv[0] is the
|
||||
/// initial-ramdisk name). Instance 1 sees argc == 1 and respawns itself through
|
||||
/// `system_spawn` with the extra arguments "alpha beta-42" — the syscall argument
|
||||
|
||||
Reference in New Issue
Block a user