gpudev

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2026 License: BSD-3-Clause Imports: 7 Imported by: 0

README

gpudev

github.com/go-virtio/gpudev is the host side of virtio-gpu: a pure-Go device model that guest drivers talk to (go-virtio/gpu, the Linux virtio_gpu driver, or any other). It executes the control queue's 2D commands, answers the capset queries, hosts webgpu-batch contexts (capset 30: go-gfx/webgpu command streams from a guest), and presents through a pluggable backend.

It is the counterpart of go-virtio/gpu, which is guest-side only. Design: go-virtio/gpu docs/WEBGPU.md.

Packages

package what
gpudev the device model: resources, backing, TRANSFER_TO_HOST_2D, scanouts, flushes, the capset queries, and a Backend interface
gpudev/virtq the device side of a split virtqueue, which checks everything in the rings
gpudev/pcisim an emulated virtio-pci modern transport, in-process, that implements go-virtio/common.Transport
gpudev/memback an in-memory backend (byte images, composed scanouts) for tests and fuzzing
gpudev/webgpuback the browser backend on go-gfx/webgpu (GOOS=js): resource textures, canvas scanouts, and webgpu-batch replay through Device.Replay
ram := &gpudev.RAM{Base: 1 << 30, Bytes: make([]byte, 64<<20)}
dev := gpudev.New(gpudev.Config{Scanouts: []gpudev.Rect{{Width: 1024, Height: 768}}}, ram, memback.New())
tr := pcisim.New(dev, ram)

g, _ := gpu.OpenVirtioGPU(tr) // the go-virtio/gpu guest driver, unchanged
fb, _ := g.SetupFramebuffer(0, 1024, 768)
fb.Flush()

Security

The guest is untrusted. Every id, size, offset, count and rectangle is bounded in 64-bit arithmetic before it reaches the backend or guest memory, on 32-bit hosts too:

  • resource sizes are bounded by MaxHostMem (w*h is checked before *4), resource count by MaxResources, and backing entries by MaxBackingEntries;
  • every backing entry and ring must lie in guest memory;
  • a transfer's whole span must lie in the backing. QEMU truncates the 64-bit offset instead (virtio-gpu.c:531);
  • chains may not loop, exceed the queue size, or put readable buffers after writable ones; indirect descriptors are refused;
  • requests are copied out of guest memory before they are decoded.

Each decoder of guest input has a fuzz target, run in CI: FuzzHandleControl and FuzzPop.

Status

  • 2D: validated in-process against the go-virtio/gpu guest driver. The driver brings the device up over pcisim and draws frames through real virtqueues; the scanout holds exactly what it drew (e2e). It has not yet run under a hypervisor.

  • webgpu-batch contexts: validated in-process against the go-virtio/gpu guest driver. The driver queries the capset, creates a context and makes fenced, ringed SUBMIT_3D calls, and the device runs them against the in-memory backend (e2e). Each frame is decoded with wgpubatch.Decode. Guest object ids are private to the context and are translated to the backend's ids, in streams (wire.Remap) and in creation descriptors (wire.RemapDescriptor), before the backend sees them. The backend then checks each stream with go-gfx/webgpu's own reader. A frame that fails loses its context. BIND_RESOURCE gives a bounded window of a resource attached to that same context. A guest program written against the go-gfx/webgpu API also runs through this path: a remote device (webgpu.NewRemoteDevice) sends its frames through the go-virtio/gpu driver (e2e, TestGuestRemoteDevice).

  • WebGPU backend (webgpuback, GOOS=js): validated in a browser on a real GPU. The go-virtio/gpu guest driver drives gpudev over pcisim inside a Chromium worker.

    • 2D: the guest's framebuffer goes through writeTexture into the resource texture, then copyTextureToTexture into the canvas texture, which is read back and equals the guest's pixels.
    • webgpu-batch: the guest creates a buffer, a WGSL shader, the layouts and a compute pipeline by descriptor, then submits one stream (write, dispatch, copy). The host reads back the doubled values that the GPU computed.

    CI runs both on macOS (a GPU adapter is required) and on Linux (SwiftShader). They were also run on Apple Metal locally. BIND_RESOURCE windows (readback into guest memory) are not implemented by this backend yet, and the A8/X8-first 2D formats need a swizzle pass this backend does not have yet.

100% statement coverage in every package, with tests on linux, macOS, windows, 386 and s390x.

License

BSD-3-Clause.

Documentation

Overview

Package gpudev is a virtio-gpu device model: the host side of the control queue that go-virtio/gpu, the Linux virtio_gpu driver and any other guest driver talk to. It executes the 2D commands (resources, backing, transfers, scanouts, flushes), answers the capset queries, and hosts webgpu-batch contexts (go-virtio/gpu/wgpubatch) whose go-gfx/webgpu command streams it validates and hands to a Backend.

The guest is untrusted. Every id, size, offset, count and rectangle a request carries is bounded in 64-bit arithmetic before it reaches the Backend or guest memory; quotas bound what a guest can make the host hold. References: the virtio spec, device-types/gpu/description.tex; Linux include/uapi/linux/virtio_gpu.h; QEMU hw/display/virtio-gpu.c (the reference 2D device, whose checks are cited where they are followed).

Index

Constants

View Source
const (
	CmdGetDisplayInfo        uint32 = 0x0100
	CmdResourceCreate2D      uint32 = 0x0101
	CmdResourceUnref         uint32 = 0x0102
	CmdSetScanout            uint32 = 0x0103
	CmdResourceFlush         uint32 = 0x0104
	CmdTransferToHost2D      uint32 = 0x0105
	CmdResourceAttachBacking uint32 = 0x0106
	CmdResourceDetachBacking uint32 = 0x0107
	CmdGetCapsetInfo         uint32 = 0x0108
	CmdGetCapset             uint32 = 0x0109

	CmdCtxCreate         uint32 = 0x0200
	CmdCtxDestroy        uint32 = 0x0201
	CmdCtxAttachResource uint32 = 0x0202
	CmdCtxDetachResource uint32 = 0x0203
	CmdSubmit3D          uint32 = 0x0207

	CmdUpdateCursor uint32 = 0x0300
	CmdMoveCursor   uint32 = 0x0301
)

Command types.

View Source
const (
	RespOKNoData      uint32 = 0x1100
	RespOKDisplayInfo uint32 = 0x1101
	RespOKCapsetInfo  uint32 = 0x1102
	RespOKCapset      uint32 = 0x1103

	RespErrUnspec            uint32 = 0x1200
	RespErrOutOfMemory       uint32 = 0x1201
	RespErrInvalidScanoutID  uint32 = 0x1202
	RespErrInvalidResourceID uint32 = 0x1203
	RespErrInvalidContextID  uint32 = 0x1204
	RespErrInvalidParameter  uint32 = 0x1205
)

Response types.

View Source
const (
	FlagFence       uint32 = 1 << 0
	FlagInfoRingIdx uint32 = 1 << 1
)

ctrl_hdr flags (description.tex:255-256).

View Source
const (
	FeatureVirgl        uint64 = 1 << 0
	FeatureEDID         uint64 = 1 << 1
	FeatureResourceBlob uint64 = 1 << 3
	FeatureContextInit  uint64 = 1 << 4
)

Feature bits (virtio_gpu.h VIRTIO_GPU_F_*).

View Source
const BytesPerPixel = 4

BytesPerPixel of every 2D format.

View Source
const HdrSize = 24

HdrSize is sizeof(struct virtio_gpu_ctrl_hdr).

View Source
const MaxScanouts = 16

MaxScanouts is VIRTIO_GPU_MAX_SCANOUTS (virtio_gpu.h:244).

Variables

View Source
var ErrOutOfRange = errors.New("gpudev: guest address range outside memory")

ErrOutOfRange: an address range outside guest memory.

View Source
var ErrWindow = errors.New("gpudev: access outside the bound window")

ErrWindow: an access outside a window, or after its backing went away.

Functions

This section is empty.

Types

type Backend

type Backend interface {
	// CreateResource makes a w×h 2D resource (w, h > 0, f valid).
	CreateResource(id uint32, f Format, w, h uint32) error
	// DestroyResource releases it; the Device has already detached it
	// from every scanout.
	DestroyResource(id uint32)
	// Upload writes r of the resource from src, where row y of r starts
	// at src[y*stride] and holds r.Width*BytesPerPixel bytes. src may
	// alias guest memory: it is only valid during the call.
	Upload(id uint32, r Rect, src []byte, stride uint64) error
	// SetScanout shows rect r of resource id on scanout (id 0: disable).
	SetScanout(scanout, id uint32, r Rect) error
	// Flush presents r (resource coordinates, inside the scanout's rect)
	// of resource id on scanout.
	Flush(scanout, id uint32, r Rect) error
	// NewContext makes the backend half of a webgpu-batch context.
	NewContext(ctx uint32) (Context, error)
}

Backend presents what the device model has validated. The Device checks every guest-supplied id, size, offset and rectangle before it calls a Backend, so an implementation may trust its arguments: ids name live resources, rectangles lie inside them, and src holds the rows Upload describes.

type Config

type Config struct {
	// Scanouts are the enabled displays' sizes, at most MaxScanouts.
	Scanouts []Rect
	// MaxHostMem bounds the bytes of all 2D resources (default 256 MiB,
	// QEMU's max_hostmem default).
	MaxHostMem uint64
	// MaxResources bounds the number of live resources (default 4096).
	MaxResources int
	// MaxBackingEntries bounds ATTACH_BACKING's nr_entries (default
	// 16384, QEMU virtio_gpu_create_mapping_iov).
	MaxBackingEntries uint32
	// WebGPUBatch, when set, offers the webgpu-batch capset with these
	// limits and accepts its contexts.
	WebGPUBatch *wgpubatch.Caps
	// MaxContexts bounds live contexts (default 16).
	MaxContexts int
}

Config describes the device a guest sees and the bounds it enforces. Zero values take the defaults documented on each field.

type Context

type Context interface {
	// Create makes an object of a go-gfx/webgpu wire kind from its
	// descriptor (object references already translated) and returns the
	// id the backend's replayer knows it by (nonzero).
	Create(kind uint32, desc []byte) (id uint32, err error)
	// Destroy releases an object Create returned.
	Destroy(id uint32)
	// Replay executes one go-gfx/webgpu Recorder submit whose ids are
	// all ids Create returned, and returns once it has executed: the
	// Device answers a fenced SUBMIT_3D after Replay returns.
	Replay(words []uint32, data []byte) error
	// Bind gives an object a window of guest memory (a resource's
	// backing) for upload and readback.
	Bind(id uint32, w Window) error
	// Close releases everything the context holds.
	Close()
}

Context is the backend half of a webgpu-batch context. The Device has decoded and bounded each message and translated every object id from the guest's namespace to the ids Create returned.

type Device

type Device struct {
	// contains filtered or unexported fields
}

Device is one virtio-gpu device. Its methods are safe for concurrent use; commands execute one at a time.

func New

func New(cfg Config, mem Memory, be Backend) *Device

New returns a device over guest memory mem presenting through be.

func (*Device) Features

func (d *Device) Features() uint64

Features are the device feature bits (VERSION_1 is the transport's). With webgpu-batch: VIRGL, the "3D available" gate guests look for, and CONTEXT_INIT, which selects the capset.

func (*Device) HandleControl

func (d *Device) HandleControl(req, resp []byte) int

HandleControl executes one control-queue request and writes the response into resp, returning its length (0 when resp cannot hold it: the guest gave a response buffer too short for the answer).

req must be host memory, not a view of guest memory: the device checks a value and then uses it, and a guest rewriting shared memory between the two would defeat the check (virglrenderer copies its commands out for the same reason, vkr_ring.c:312). The pcisim transport gathers every request into a fresh buffer.

func (*Device) NumCapsets

func (d *Device) NumCapsets() uint32

NumCapsets is virtio_gpu_config.num_capsets.

func (*Device) NumScanouts

func (d *Device) NumScanouts() uint32

NumScanouts and NumCapsets are the virtio_gpu_config fields.

type Format

type Format uint32

Format is a VIRTIO_GPU_FORMAT_* value (virtio_gpu.h:381-390). Every format is 4 bytes per pixel; the name gives the byte order in memory.

const (
	FormatB8G8R8A8 Format = 1
	FormatB8G8R8X8 Format = 2
	FormatA8R8G8B8 Format = 3
	FormatX8R8G8B8 Format = 4
	FormatR8G8B8A8 Format = 67
	FormatX8B8G8R8 Format = 68
	FormatA8B8G8R8 Format = 121
	FormatR8G8B8X8 Format = 134
)

The 2D formats.

func (Format) Valid

func (f Format) Valid() bool

Valid reports whether f is one of the 2D formats.

type Memory

type Memory interface {
	// Slice returns a view of n bytes at guest-physical addr, or an
	// error if any of them lies outside guest memory.
	Slice(addr, n uint64) ([]byte, error)
}

Memory is the guest's physical memory as the device sees it. Every guest-supplied address goes through Slice, which bounds it.

type RAM

type RAM struct {
	Base  uint64
	Bytes []byte
}

RAM is guest memory as one byte slice mapped at guest-physical Base.

func (*RAM) Slice

func (m *RAM) Slice(addr, n uint64) ([]byte, error)

Slice implements Memory with 64-bit bounds checks: no addr or n can wrap around or reach past the slice, on 32-bit hosts too.

type Rect

type Rect struct{ X, Y, Width, Height uint32 }

Rect is struct virtio_gpu_rect.

type Window

type Window interface {
	Len() uint64
	ReadAt(p []byte, off uint64) error
	WriteAt(p []byte, off uint64) error
}

Window is a bounded range of a resource's backing, for BIND_RESOURCE.

Directories

Path Synopsis
Package memback is an in-memory gpudev.Backend: resources are byte images, scanouts are framebuffers a flush composes into, and webgpu-batch contexts keep their objects in a table and check every replayed stream with go-gfx/webgpu's own reader (wire.Decode).
Package memback is an in-memory gpudev.Backend: resources are byte images, scanouts are framebuffers a flush composes into, and webgpu-batch contexts keep their objects in a table and check every replayed stream with go-gfx/webgpu's own reader (wire.Decode).
Package pcisim puts a gpudev.Device behind an emulated virtio-pci modern transport, in process: it implements go-virtio/common.Transport, so the go-virtio/gpu guest driver (or any driver written against that interface) can bring the device up and drive it with no VM.
Package pcisim puts a gpudev.Device behind an emulated virtio-pci modern transport, in process: it implements go-virtio/common.Transport, so the go-virtio/gpu guest driver (or any driver written against that interface) can bring the device up and drive it with no VM.
Package virtq is the device side of a virtio split virtqueue: it takes descriptor chains the driver made available and returns them used.
Package virtq is the device side of a virtio split virtqueue: it takes descriptor chains the driver made available and returns them used.
Package webgpuback is the browser backend of gpudev, on go-gfx/webgpu.
Package webgpuback is the browser backend of gpudev, on go-gfx/webgpu.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL