virtio-gpu: a modern virtio 1.0 display driver, bring-up to a flushed frame (v2 V3)
The first native scanout backend's driver half. The device manager matches the
virtio-gpu PCI function by its Display/Other class triple and spawns the driver,
which claims the device, confirms vendor 0x1AF4/device 0x1050 from config space,
enables memory-space + bus mastering, and walks the virtio vendor capabilities to
find the common-config and notify structures in its BAR.
From there it is the standard modern-virtio bring-up: reset, negotiate VERSION_1,
stand up the control virtqueue in coherent DMA, then drive the GPU end to end —
RESOURCE_CREATE_2D, ATTACH_BACKING (a coherent DMA buffer; V4 swaps in the shm-
shared surface), SET_SCANOUT, paint a known pattern, TRANSFER_TO_HOST_2D,
RESOURCE_FLUSH, and wait for the device's used-ring ack. Reading the backing back
proves it is CPU-visible RAM; the ack proves the device consumed the frame —
together the automated stand-in for "it's on screen", no screenshot.
- system/drivers/virtio-gpu/: the driver, plus virtio-gpu-protocol.zig (control
commands) and virtio-pci.zig (the 1.0 PCI transport + split-virtqueue), both with
host-tested struct sizes.
- device-manager matches the display/other class triple to "virtio-gpu"; the driver
self-confirms the vendor/device id, since the class alone cannot distinguish it.
- ServiceId.scanout (11): the driver registers it so the compositor finds it in V4.
Gate: python3 test/qemu_test.py virtio-gpu (QEMU -device virtio-gpu-pci) — the
device-manager stack discovers the function, the driver brings up a 640x480 scanout
and flushes a test pattern: "virtio-gpu: scanout 640x480 online" + "flush acked,
pixel check ok". host tests, display-service, shm, and device-list still pass.
Note: the pre-existing pci-scan case triple-faults on main (verified at 88ad432,
before this change); it is unrelated and tracked separately.
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
//! The virtio-gpu control protocol — the command/response structs the driver exchanges with
|
||||
//! the device over its control virtqueue (virtio spec, "GPU Device"). `extern` structs, so
|
||||
//! the layout matches the little-endian wire format exactly. Host-tested for size. See
|
||||
//! docs/display-v2.md.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
/// Control command / response types (virtio_gpu_ctrl_type). Commands are 0x01xx, responses
|
||||
/// 0x11xx (ok) / 0x12xx (error).
|
||||
pub const CmdType = enum(u32) {
|
||||
get_display_info = 0x0100,
|
||||
resource_create_2d = 0x0101,
|
||||
resource_unref = 0x0102,
|
||||
set_scanout = 0x0103,
|
||||
resource_flush = 0x0104,
|
||||
transfer_to_host_2d = 0x0105,
|
||||
resource_attach_backing = 0x0106,
|
||||
resource_detach_backing = 0x0107,
|
||||
get_edid = 0x010a,
|
||||
|
||||
resp_ok_nodata = 0x1100,
|
||||
resp_ok_display_info = 0x1101,
|
||||
resp_ok_edid = 0x1104,
|
||||
resp_err_unspec = 0x1200,
|
||||
_,
|
||||
};
|
||||
|
||||
/// Set in a command's `flags` to request a fence; the device echoes `fence_id` in the
|
||||
/// response and does not report completion until the command's effects are visible.
|
||||
pub const flag_fence: u32 = 1 << 0;
|
||||
|
||||
/// virtio_gpu_ctrl_hdr — the header on every command and response.
|
||||
pub const CtrlHdr = extern struct {
|
||||
type: u32,
|
||||
flags: u32 = 0,
|
||||
fence_id: u64 = 0,
|
||||
ctx_id: u32 = 0,
|
||||
ring_idx: u8 = 0,
|
||||
padding: [3]u8 = .{ 0, 0, 0 },
|
||||
};
|
||||
|
||||
pub const Rect = extern struct {
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
};
|
||||
|
||||
/// 2D pixel formats. QEMU's virtio-gpu host default is B8G8R8X8 (matches our bgrx).
|
||||
pub const format_b8g8r8x8_unorm: u32 = 2;
|
||||
pub const format_r8g8b8x8_unorm: u32 = 134;
|
||||
|
||||
pub const ResourceCreate2d = extern struct {
|
||||
hdr: CtrlHdr,
|
||||
resource_id: u32,
|
||||
format: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
};
|
||||
|
||||
/// One scatter-gather entry of a resource's guest backing (a physical span).
|
||||
pub const MemEntry = extern struct {
|
||||
addr: u64,
|
||||
length: u32,
|
||||
padding: u32 = 0,
|
||||
};
|
||||
|
||||
/// Header for RESOURCE_ATTACH_BACKING; `nr_entries` `MemEntry` follow it inline.
|
||||
pub const ResourceAttachBacking = extern struct {
|
||||
hdr: CtrlHdr,
|
||||
resource_id: u32,
|
||||
nr_entries: u32,
|
||||
};
|
||||
|
||||
pub const SetScanout = extern struct {
|
||||
hdr: CtrlHdr,
|
||||
rect: Rect,
|
||||
scanout_id: u32,
|
||||
resource_id: u32,
|
||||
};
|
||||
|
||||
pub const ResourceFlush = extern struct {
|
||||
hdr: CtrlHdr,
|
||||
rect: Rect,
|
||||
resource_id: u32,
|
||||
padding: u32 = 0,
|
||||
};
|
||||
|
||||
/// Copy the guest backing into the host resource for `rect` (2D resources must transfer
|
||||
/// before a flush shows the update).
|
||||
pub const TransferToHost2d = extern struct {
|
||||
hdr: CtrlHdr,
|
||||
rect: Rect,
|
||||
offset: u64,
|
||||
resource_id: u32,
|
||||
padding: u32 = 0,
|
||||
};
|
||||
|
||||
pub const max_scanouts = 16;
|
||||
|
||||
pub const DisplayOne = extern struct {
|
||||
rect: Rect,
|
||||
enabled: u32,
|
||||
flags: u32,
|
||||
};
|
||||
|
||||
pub const RespDisplayInfo = extern struct {
|
||||
hdr: CtrlHdr,
|
||||
pmodes: [max_scanouts]DisplayOne,
|
||||
};
|
||||
|
||||
pub const GetEdid = extern struct {
|
||||
hdr: CtrlHdr,
|
||||
scanout: u32,
|
||||
padding: u32 = 0,
|
||||
};
|
||||
|
||||
pub const RespEdid = extern struct {
|
||||
hdr: CtrlHdr,
|
||||
size: u32,
|
||||
padding: u32 = 0,
|
||||
edid: [1024]u8,
|
||||
};
|
||||
|
||||
test "virtio-gpu struct sizes match the wire layout" {
|
||||
try std.testing.expectEqual(@as(usize, 24), @sizeOf(CtrlHdr));
|
||||
try std.testing.expectEqual(@as(usize, 16), @sizeOf(Rect));
|
||||
try std.testing.expectEqual(@as(usize, 40), @sizeOf(ResourceCreate2d));
|
||||
try std.testing.expectEqual(@as(usize, 16), @sizeOf(MemEntry));
|
||||
try std.testing.expectEqual(@as(usize, 32), @sizeOf(ResourceAttachBacking));
|
||||
try std.testing.expectEqual(@as(usize, 48), @sizeOf(SetScanout));
|
||||
try std.testing.expectEqual(@as(usize, 48), @sizeOf(ResourceFlush));
|
||||
try std.testing.expectEqual(@as(usize, 56), @sizeOf(TransferToHost2d));
|
||||
try std.testing.expectEqual(@as(usize, 24 + 4 + 4 + 1024), @sizeOf(RespEdid));
|
||||
}
|
||||
Reference in New Issue
Block a user