#pragma once // virtio 1.0 over PCI — the transport, and virtio-gpu's 2D protocol. // // It lived next to the driver while exactly one driver read it, with a note // saying it should move when a second reader appeared. One has: cmd/vgpud is // the same device driven from userspace (docs/plan/hybrid.md's first // eviction), and it needs the same register offsets and the same commands. // // Still one file rather than transport-and-protocol split in two. Both readers // want both halves, so a seam here would be drawn for the sake of having one. // virtio-net or virtio-blk arriving is when that changes. // // Written from the OASIS specification (virtio 1.1, chapters 2, 4.1 and 5.7), // which is a public document with no licence on what you write from it. Same // reasoning as kernel/drivers/xhci.c. // // Why virtio-gpu at all, when there is already a framebuffer: the VBE // framebuffer is a flat block of memory the loader happened to be told about. // It has no mode setting, no notion of a frame being complete, and no way to // ask the host to present anything -- so a compositor drawing into it is // racing the scanout with no way to synchronise. virtio-gpu has resources, // explicit transfer and an explicit flush, which is the model every real GPU // driver has. It is the smallest device that teaches the right shape. // --- PCI transport --------------------------------------------------------- #define VIRTIO_VENDOR 0x1AF4 // Modern (1.0) device IDs are 0x1040 + the virtio device type. GPU is type 16. #define VIRTIO_DEV_GPU 0x1050 // Vendor-specific PCI capability, which is how a virtio device says where in // its BARs each configuration structure lives. #define PCI_CAP_ID_VNDR 0x09 #define VIRTIO_PCI_CAP_COMMON_CFG 1 #define VIRTIO_PCI_CAP_NOTIFY_CFG 2 #define VIRTIO_PCI_CAP_ISR_CFG 3 #define VIRTIO_PCI_CAP_DEVICE_CFG 4 // Offsets within the common configuration structure. Written out rather than // declared as a struct: the fields are little-endian by specification and of // mixed width, and a struct invites the compiler to pad or a caller to forget // that these are MMIO and must be accessed at exactly the stated width. #define VCOMMON_DFEAT_SEL 0x00 // le32 #define VCOMMON_DFEAT 0x04 // le32 #define VCOMMON_GFEAT_SEL 0x08 // le32 #define VCOMMON_GFEAT 0x0C // le32 #define VCOMMON_MSIX_CFG 0x10 // le16 #define VCOMMON_NUM_QUEUES 0x12 // le16 #define VCOMMON_STATUS 0x14 // u8 #define VCOMMON_CFG_GEN 0x15 // u8 #define VCOMMON_Q_SELECT 0x16 // le16 #define VCOMMON_Q_SIZE 0x18 // le16 #define VCOMMON_Q_MSIX 0x1A // le16 #define VCOMMON_Q_ENABLE 0x1C // le16 #define VCOMMON_Q_NOTIFY_OFF 0x1E // le16 #define VCOMMON_Q_DESC 0x20 // le64 #define VCOMMON_Q_DRIVER 0x28 // le64 #define VCOMMON_Q_DEVICE 0x30 // le64 // device_status bits, set in this order during bring-up. #define VSTATUS_ACK 1 #define VSTATUS_DRIVER 2 #define VSTATUS_DRIVER_OK 4 #define VSTATUS_FEATURES_OK 8 #define VSTATUS_FAILED 0x80 #define VIRTIO_F_VERSION_1 32 // feature bit: this is a 1.0 device // --- split virtqueue ------------------------------------------------------- #define VRING_DESC_F_NEXT 1 // Set in vring_avail.flags to tell the device not to interrupt on completion. // // A driver that polls the used ring wants this, and wants it before the queue // is enabled. Without it the device raises its interrupt line on every // completed command -- and if nothing is registered to acknowledge it, a // level-triggered PCI line stays asserted and re-fires for ever. That is the // failure in docs/history/kernel.md section 13, and cmd/vgpud reproduced it // exactly on its first working run. #define VRING_AVAIL_F_NO_INTERRUPT 1 #define VRING_DESC_F_WRITE 2 // device writes it; i.e. this is our buffer struct vring_desc { uint64 addr; uint32 len; uint16 flags; uint16 next; } __attribute__((packed)); struct vring_avail { uint16 flags; uint16 idx; uint16 ring[]; } __attribute__((packed)); struct vring_used_elem { uint32 id; uint32 len; } __attribute__((packed)); struct vring_used { uint16 flags; uint16 idx; struct vring_used_elem ring[]; } __attribute__((packed)); // --- virtio-gpu, 2D -------------------------------------------------------- #define VIRTIO_GPU_CMD_GET_DISPLAY_INFO 0x0100 #define VIRTIO_GPU_CMD_RESOURCE_CREATE_2D 0x0101 #define VIRTIO_GPU_CMD_RESOURCE_UNREF 0x0102 #define VIRTIO_GPU_CMD_SET_SCANOUT 0x0103 #define VIRTIO_GPU_CMD_RESOURCE_FLUSH 0x0104 #define VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D 0x0105 #define VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING 0x0106 #define VIRTIO_GPU_RESP_OK_NODATA 0x1100 #define VIRTIO_GPU_RESP_OK_DISPLAY_INFO 0x1101 #define VIRTIO_GPU_MAX_SCANOUTS 16 // B8G8R8X8, which is what a 32-bit little-endian 0x00RRGGBB word looks like in // memory -- the same layout vga.c already draws into, so a framebuffer can be // handed over without a conversion pass. #define VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM 2 struct virtio_gpu_ctrl_hdr { uint32 type; uint32 flags; uint64 fence_id; uint32 ctx_id; uint32 padding; } __attribute__((packed)); struct virtio_gpu_rect { uint32 x, y, width, height; } __attribute__((packed)); struct virtio_gpu_display_one { struct virtio_gpu_rect r; uint32 enabled; uint32 flags; } __attribute__((packed)); struct virtio_gpu_resp_display_info { struct virtio_gpu_ctrl_hdr hdr; struct virtio_gpu_display_one pmodes[VIRTIO_GPU_MAX_SCANOUTS]; } __attribute__((packed)); struct virtio_gpu_resource_create_2d { struct virtio_gpu_ctrl_hdr hdr; uint32 resource_id; uint32 format; uint32 width; uint32 height; } __attribute__((packed)); struct virtio_gpu_mem_entry { uint64 addr; uint32 length; uint32 padding; } __attribute__((packed)); struct virtio_gpu_resource_attach_backing { struct virtio_gpu_ctrl_hdr hdr; uint32 resource_id; uint32 nr_entries; } __attribute__((packed)); struct virtio_gpu_set_scanout { struct virtio_gpu_ctrl_hdr hdr; struct virtio_gpu_rect r; uint32 scanout_id; uint32 resource_id; } __attribute__((packed)); struct virtio_gpu_transfer_to_host_2d { struct virtio_gpu_ctrl_hdr hdr; struct virtio_gpu_rect r; uint64 offset; uint32 resource_id; uint32 padding; } __attribute__((packed)); struct virtio_gpu_resource_flush { struct virtio_gpu_ctrl_hdr hdr; struct virtio_gpu_rect r; uint32 resource_id; uint32 padding; } __attribute__((packed));