summary refs log tree commit diff
path: root/devices/src/virtio/gpu/protocol.rs
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2017-09-13 19:15:43 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-07-20 05:30:54 -0700
commit3a8100adc75d805300f23f7cff25e3e1d6b40b33 (patch)
tree08efcf2c527b429836d42d431ec84e7d096a94b0 /devices/src/virtio/gpu/protocol.rs
parentf40bb190ece97c908f8dba2efc7c1aceb4fc0e0b (diff)
downloadcrosvm-3a8100adc75d805300f23f7cff25e3e1d6b40b33.tar
crosvm-3a8100adc75d805300f23f7cff25e3e1d6b40b33.tar.gz
crosvm-3a8100adc75d805300f23f7cff25e3e1d6b40b33.tar.bz2
crosvm-3a8100adc75d805300f23f7cff25e3e1d6b40b33.tar.lz
crosvm-3a8100adc75d805300f23f7cff25e3e1d6b40b33.tar.xz
crosvm-3a8100adc75d805300f23f7cff25e3e1d6b40b33.tar.zst
crosvm-3a8100adc75d805300f23f7cff25e3e1d6b40b33.zip
gpu: implement virtio-gpu
Basic 2D and 3D support is there. The drm_cursor_test and
null_platform_test in drm-tests should run to completion.

The extra device is hidden behind both a build time feature called 'gpu'
and the device is only added to a VM if the  '--gpu' flag is given.

TEST=build with --features=gpu;
     drm_cursor_test && null_platform_test
BUG=chromium:837073

Change-Id: Ic91acaaebbee395599d7e1ba41c24c9ed2d84169
Reviewed-on: https://chromium-review.googlesource.com/1036862
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'devices/src/virtio/gpu/protocol.rs')
-rw-r--r--devices/src/virtio/gpu/protocol.rs692
1 files changed, 692 insertions, 0 deletions
diff --git a/devices/src/virtio/gpu/protocol.rs b/devices/src/virtio/gpu/protocol.rs
new file mode 100644
index 0000000..c0fbfc3
--- /dev/null
+++ b/devices/src/virtio/gpu/protocol.rs
@@ -0,0 +1,692 @@
+#![allow(dead_code)]
+#![allow(non_camel_case_types)]
+
+use std::cmp::min;
+use std::fmt;
+use std::marker::PhantomData;
+use std::mem::{size_of, size_of_val};
+use std::str::from_utf8;
+
+use data_model::{DataInit, Le32, Le64, VolatileMemory, VolatileSlice, VolatileMemoryError};
+
+pub const VIRTIO_GPU_F_VIRGL: u32 = 0;
+
+pub const VIRTIO_GPU_UNDEFINED: u32 = 0x0;
+
+/* 2d commands */
+pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x100;
+pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x101;
+pub const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x102;
+pub const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x103;
+pub const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x104;
+pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x105;
+pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x106;
+pub const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x107;
+pub const VIRTIO_GPU_CMD_GET_CAPSET_INFO: u32 = 0x108;
+pub const VIRTIO_GPU_CMD_GET_CAPSET: u32 = 0x109;
+
+/* 3d commands */
+pub const VIRTIO_GPU_CMD_CTX_CREATE: u32 = 0x200;
+pub const VIRTIO_GPU_CMD_CTX_DESTROY: u32 = 0x201;
+pub const VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: u32 = 0x202;
+pub const VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: u32 = 0x203;
+pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: u32 = 0x204;
+pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: u32 = 0x205;
+pub const VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: u32 = 0x206;
+pub const VIRTIO_GPU_CMD_SUBMIT_3D: u32 = 0x207;
+
+/* cursor commands */
+pub const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x300;
+pub const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x301;
+
+/* success responses */
+pub const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
+pub const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
+pub const VIRTIO_GPU_RESP_OK_CAPSET_INFO: u32 = 0x1102;
+pub const VIRTIO_GPU_RESP_OK_CAPSET: u32 = 0x1103;
+
+/* error responses */
+pub const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200;
+pub const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201;
+pub const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202;
+pub const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203;
+pub const VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID: u32 = 0x1204;
+pub const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205;
+
+pub fn virtio_gpu_cmd_str(cmd: u32) -> &'static str {
+    match cmd {
+        VIRTIO_GPU_CMD_GET_DISPLAY_INFO => "VIRTIO_GPU_CMD_GET_DISPLAY_INFO",
+        VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => "VIRTIO_GPU_CMD_RESOURCE_CREATE_2D",
+        VIRTIO_GPU_CMD_RESOURCE_UNREF => "VIRTIO_GPU_CMD_RESOURCE_UNREF",
+        VIRTIO_GPU_CMD_SET_SCANOUT => "VIRTIO_GPU_CMD_SET_SCANOUT",
+        VIRTIO_GPU_CMD_RESOURCE_FLUSH => "VIRTIO_GPU_CMD_RESOURCE_FLUSH",
+        VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => "VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D",
+        VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => "VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING",
+        VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => "VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING",
+        VIRTIO_GPU_CMD_GET_CAPSET_INFO => "VIRTIO_GPU_CMD_GET_CAPSET_INFO",
+        VIRTIO_GPU_CMD_GET_CAPSET => "VIRTIO_GPU_CMD_GET_CAPSET",
+        VIRTIO_GPU_CMD_CTX_CREATE => "VIRTIO_GPU_CMD_CTX_CREATE",
+        VIRTIO_GPU_CMD_CTX_DESTROY => "VIRTIO_GPU_CMD_CTX_DESTROY",
+        VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE => "VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE",
+        VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE => "VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE",
+        VIRTIO_GPU_CMD_RESOURCE_CREATE_3D => "VIRTIO_GPU_CMD_RESOURCE_CREATE_3D",
+        VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D => "VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D",
+        VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => "VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D",
+        VIRTIO_GPU_CMD_SUBMIT_3D => "VIRTIO_GPU_CMD_SUBMIT_3D",
+        VIRTIO_GPU_CMD_UPDATE_CURSOR => "VIRTIO_GPU_CMD_UPDATE_CURSOR",
+        VIRTIO_GPU_CMD_MOVE_CURSOR => "VIRTIO_GPU_CMD_MOVE_CURSOR",
+        VIRTIO_GPU_RESP_OK_NODATA => "VIRTIO_GPU_RESP_OK_NODATA",
+        VIRTIO_GPU_RESP_OK_DISPLAY_INFO => "VIRTIO_GPU_RESP_OK_DISPLAY_INFO",
+        VIRTIO_GPU_RESP_OK_CAPSET_INFO => "VIRTIO_GPU_RESP_OK_CAPSET_INFO",
+        VIRTIO_GPU_RESP_OK_CAPSET => "VIRTIO_GPU_RESP_OK_CAPSET",
+        VIRTIO_GPU_RESP_ERR_UNSPEC => "VIRTIO_GPU_RESP_ERR_UNSPEC",
+        VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY => "VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY",
+        VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID => "VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID",
+        VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID => "VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID",
+        VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID => "VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID",
+        VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER => "VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER",
+        _ => "UNKNOWN",
+    }
+}
+
+pub const VIRTIO_GPU_FLAG_FENCE: u32 = (1 << 0);
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_ctrl_hdr {
+    pub type_: Le32,
+    pub flags: Le32,
+    pub fence_id: Le64,
+    pub ctx_id: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_ctrl_hdr {}
+
+/* data passed in the cursor vq */
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_cursor_pos {
+    pub scanout_id: Le32,
+    pub x: Le32,
+    pub y: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_cursor_pos {}
+
+/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_update_cursor {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub pos: virtio_gpu_cursor_pos, /* update & move */
+    pub resource_id: Le32, /* update only */
+    pub hot_x: Le32, /* update only */
+    pub hot_y: Le32, /* update only */
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_update_cursor {}
+
+/* data passed in the control vq, 2d related */
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct virtio_gpu_rect {
+    pub x: Le32,
+    pub y: Le32,
+    pub width: Le32,
+    pub height: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_rect {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_unref {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub resource_id: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_unref {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_create_2d {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub resource_id: Le32,
+    pub format: Le32,
+    pub width: Le32,
+    pub height: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_create_2d {}
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_set_scanout {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub r: virtio_gpu_rect,
+    pub scanout_id: Le32,
+    pub resource_id: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_set_scanout {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_flush {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub r: virtio_gpu_rect,
+    pub resource_id: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_flush {}
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_transfer_to_host_2d {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub r: virtio_gpu_rect,
+    pub offset: Le64,
+    pub resource_id: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_transfer_to_host_2d {}
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_mem_entry {
+    pub addr: Le64,
+    pub length: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_mem_entry {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_attach_backing {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub resource_id: Le32,
+    pub nr_entries: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_attach_backing {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_detach_backing {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub resource_id: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_detach_backing {}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct virtio_gpu_display_one {
+    pub r: virtio_gpu_rect,
+    pub enabled: Le32,
+    pub flags: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_display_one {}
+
+/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
+const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resp_display_info {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub pmodes: [virtio_gpu_display_one; VIRTIO_GPU_MAX_SCANOUTS],
+}
+
+unsafe impl DataInit for virtio_gpu_resp_display_info {}
+
+/* data passed in the control vq, 3d related */
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_box {
+    pub x: Le32,
+    pub y: Le32,
+    pub z: Le32,
+    pub w: Le32,
+    pub h: Le32,
+    pub d: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_box {}
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_transfer_host_3d {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub box_: virtio_gpu_box,
+    pub offset: Le64,
+    pub resource_id: Le32,
+    pub level: Le32,
+    pub stride: Le32,
+    pub layer_stride: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_transfer_host_3d {}
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
+pub const VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP: u32 = (1 << 0);
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resource_create_3d {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub resource_id: Le32,
+    pub target: Le32,
+    pub format: Le32,
+    pub bind: Le32,
+    pub width: Le32,
+    pub height: Le32,
+    pub depth: Le32,
+    pub array_size: Le32,
+    pub last_level: Le32,
+    pub nr_samples: Le32,
+    pub flags: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resource_create_3d {}
+
+/* VIRTIO_GPU_CMD_CTX_CREATE */
+#[derive(Copy)]
+#[repr(C)]
+pub struct virtio_gpu_ctx_create {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub nlen: Le32,
+    pub padding: Le32,
+    pub debug_name: [u8; 64],
+}
+
+unsafe impl DataInit for virtio_gpu_ctx_create {}
+
+impl Clone for virtio_gpu_ctx_create {
+    fn clone(&self) -> virtio_gpu_ctx_create {
+        *self
+    }
+}
+
+impl fmt::Debug for virtio_gpu_ctx_create {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let debug_name = from_utf8(&self.debug_name[..min(64, self.nlen.to_native() as usize)])
+            .unwrap_or("<invalid>");
+        f.debug_struct("virtio_gpu_ctx_create")
+            .field("hdr", &self.hdr)
+            .field("debug_name", &debug_name)
+            .finish()
+    }
+}
+
+/* VIRTIO_GPU_CMD_CTX_DESTROY */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_ctx_destroy {
+    pub hdr: virtio_gpu_ctrl_hdr,
+}
+
+unsafe impl DataInit for virtio_gpu_ctx_destroy {}
+
+/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_ctx_resource {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub resource_id: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_ctx_resource {}
+
+/* VIRTIO_GPU_CMD_SUBMIT_3D */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_cmd_submit {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub size: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_cmd_submit {}
+
+const VIRTIO_GPU_CAPSET_VIRGL: u32 = 1;
+
+/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_get_capset_info {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub capset_index: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_get_capset_info {}
+
+/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resp_capset_info {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub capset_id: Le32,
+    pub capset_max_version: Le32,
+    pub capset_max_size: Le32,
+    pub padding: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_resp_capset_info {}
+
+/* VIRTIO_GPU_CMD_GET_CAPSET */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_get_capset {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub capset_id: Le32,
+    pub capset_version: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_get_capset {}
+
+/* VIRTIO_GPU_RESP_OK_CAPSET */
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_resp_capset {
+    pub hdr: virtio_gpu_ctrl_hdr,
+    pub capset_data: PhantomData<[u8]>,
+}
+
+unsafe impl DataInit for virtio_gpu_resp_capset {}
+
+
+pub const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct virtio_gpu_config {
+    pub events_read: Le32,
+    pub events_clear: Le32,
+    pub num_scanouts: Le32,
+    pub num_capsets: Le32,
+}
+
+unsafe impl DataInit for virtio_gpu_config {}
+
+/* simple formats for fbcon/X use */
+pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
+pub const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2;
+pub const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3;
+pub const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4;
+pub const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67;
+pub const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68;
+pub const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121;
+pub const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134;
+
+/// A virtio gpu command and associated metadata specific to each command.
+#[derive(Copy, Clone)]
+pub enum GpuCommand {
+    GetDisplayInfo(virtio_gpu_ctrl_hdr),
+    ResourceCreate2d(virtio_gpu_resource_create_2d),
+    ResourceUnref(virtio_gpu_resource_unref),
+    SetScanout(virtio_gpu_set_scanout),
+    ResourceFlush(virtio_gpu_resource_flush),
+    TransferToHost2d(virtio_gpu_transfer_to_host_2d),
+    ResourceAttachBacking(virtio_gpu_resource_attach_backing),
+    ResourceDetachBacking(virtio_gpu_resource_detach_backing),
+    GetCapsetInfo(virtio_gpu_get_capset_info),
+    GetCapset(virtio_gpu_get_capset),
+    CtxCreate(virtio_gpu_ctx_create),
+    CtxDestroy(virtio_gpu_ctx_destroy),
+    CtxAttachResource(virtio_gpu_ctx_resource),
+    CtxDetachResource(virtio_gpu_ctx_resource),
+    ResourceCreate3d(virtio_gpu_resource_create_3d),
+    TransferToHost3d(virtio_gpu_transfer_host_3d),
+    TransferFromHost3d(virtio_gpu_transfer_host_3d),
+    CmdSubmit3d(virtio_gpu_cmd_submit),
+    UpdateCursor(virtio_gpu_update_cursor),
+    MoveCursor(virtio_gpu_update_cursor),
+}
+
+/// An error indicating something went wrong decoding a `GpuCommand`. These correspond to
+/// `VIRTIO_GPU_CMD_*`.
+#[derive(Debug)]
+pub enum GpuCommandDecodeError {
+    /// The command referenced an inaccessible area of memory.
+    Memory(VolatileMemoryError),
+    /// The type of the command was invalid.
+    InvalidType(u32),
+}
+
+impl From<VolatileMemoryError> for GpuCommandDecodeError {
+    fn from(e: VolatileMemoryError) -> GpuCommandDecodeError {
+        GpuCommandDecodeError::Memory(e)
+    }
+}
+
+impl fmt::Debug for GpuCommand {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::GpuCommand::*;
+        match *self {
+            GetDisplayInfo(ref _info) => f.debug_struct("GetDisplayInfo").finish(),
+            ResourceCreate2d(ref _info) => f.debug_struct("ResourceCreate2d").finish(),
+            ResourceUnref(ref _info) => f.debug_struct("ResourceUnref").finish(),
+            SetScanout(ref _info) => f.debug_struct("SetScanout").finish(),
+            ResourceFlush(ref _info) => f.debug_struct("ResourceFlush").finish(),
+            TransferToHost2d(ref _info) => f.debug_struct("TransferToHost2d").finish(),
+            ResourceAttachBacking(ref _info) => f.debug_struct("ResourceAttachBacking").finish(),
+            ResourceDetachBacking(ref _info) => f.debug_struct("ResourceDetachBacking").finish(),
+            GetCapsetInfo(ref _info) => f.debug_struct("GetCapsetInfo").finish(),
+            GetCapset(ref _info) => f.debug_struct("GetCapset").finish(),
+            CtxCreate(ref _info) => f.debug_struct("CtxCreate").finish(),
+            CtxDestroy(ref _info) => f.debug_struct("CtxDestroy").finish(),
+            CtxAttachResource(ref _info) => f.debug_struct("CtxAttachResource").finish(),
+            CtxDetachResource(ref _info) => f.debug_struct("CtxDetachResource").finish(),
+            ResourceCreate3d(ref _info) => f.debug_struct("ResourceCreate3d").finish(),
+            TransferToHost3d(ref _info) => f.debug_struct("TransferToHost3d").finish(),
+            TransferFromHost3d(ref _info) => f.debug_struct("TransferFromHost3d").finish(),
+            CmdSubmit3d(ref _info) => f.debug_struct("CmdSubmit3d").finish(),
+            UpdateCursor(ref _info) => f.debug_struct("UpdateCursor").finish(),
+            MoveCursor(ref _info) => f.debug_struct("MoveCursor").finish(),
+        }
+    }
+}
+
+impl GpuCommand {
+    /// Decodes a command from the given chunk of memory.
+    pub fn decode(cmd: VolatileSlice) -> Result<GpuCommand, GpuCommandDecodeError> {
+        use self::GpuCommand::*;
+        let hdr: virtio_gpu_ctrl_hdr = cmd.get_ref(0)?.load();
+        Ok(match hdr.type_.into() {
+               VIRTIO_GPU_CMD_GET_DISPLAY_INFO => GetDisplayInfo(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => ResourceCreate2d(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_RESOURCE_UNREF => ResourceUnref(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_SET_SCANOUT => SetScanout(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_RESOURCE_FLUSH => ResourceFlush(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => TransferToHost2d(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => {
+                   ResourceAttachBacking(cmd.get_ref(0)?.load())
+               }
+               VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => {
+                   ResourceDetachBacking(cmd.get_ref(0)?.load())
+               }
+               VIRTIO_GPU_CMD_GET_CAPSET_INFO => GetCapsetInfo(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_GET_CAPSET => GetCapset(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_CTX_CREATE => CtxCreate(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_CTX_DESTROY => CtxDestroy(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE => CtxAttachResource(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE => CtxDetachResource(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_RESOURCE_CREATE_3D => ResourceCreate3d(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D => TransferToHost3d(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => TransferFromHost3d(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_SUBMIT_3D => CmdSubmit3d(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_UPDATE_CURSOR => UpdateCursor(cmd.get_ref(0)?.load()),
+               VIRTIO_GPU_CMD_MOVE_CURSOR => MoveCursor(cmd.get_ref(0)?.load()),
+               _ => return Err(GpuCommandDecodeError::InvalidType(hdr.type_.into())),
+           })
+    }
+
+    /// Gets the generic `virtio_gpu_ctrl_hdr` from this command.
+    pub fn ctrl_hdr(&self) -> &virtio_gpu_ctrl_hdr {
+        use self::GpuCommand::*;
+        match *self {
+            GetDisplayInfo(ref info) => &info,
+            ResourceCreate2d(ref info) => &info.hdr,
+            ResourceUnref(ref info) => &info.hdr,
+            SetScanout(ref info) => &info.hdr,
+            ResourceFlush(ref info) => &info.hdr,
+            TransferToHost2d(ref info) => &info.hdr,
+            ResourceAttachBacking(ref info) => &info.hdr,
+            ResourceDetachBacking(ref info) => &info.hdr,
+            GetCapsetInfo(ref info) => &info.hdr,
+            GetCapset(ref info) => &info.hdr,
+            CtxCreate(ref info) => &info.hdr,
+            CtxDestroy(ref info) => &info.hdr,
+            CtxAttachResource(ref info) => &info.hdr,
+            CtxDetachResource(ref info) => &info.hdr,
+            ResourceCreate3d(ref info) => &info.hdr,
+            TransferToHost3d(ref info) => &info.hdr,
+            TransferFromHost3d(ref info) => &info.hdr,
+            CmdSubmit3d(ref info) => &info.hdr,
+            UpdateCursor(ref info) => &info.hdr,
+            MoveCursor(ref info) => &info.hdr,
+        }
+    }
+}
+
+/// A response to a `GpuCommand`. These correspond to `VIRTIO_GPU_RESP_*`.
+#[derive(Debug, PartialEq)]
+pub enum GpuResponse {
+    OkNoData,
+    OkDisplayInfo(Vec<(u32, u32)>),
+    OkCapsetInfo { id: u32, version: u32, size: u32 },
+    OkCapset(Vec<u8>),
+    ErrUnspec,
+    ErrOutOfMemory,
+    ErrInvalidScanoutId,
+    ErrInvalidResourceId,
+    ErrInvalidContextId,
+    ErrInvalidParameter,
+}
+
+/// An error indicating something went wrong decoding a `GpuCommand`.
+#[derive(Debug)]
+pub enum GpuResponseEncodeError {
+    /// The response was encoded to an inaccessible area of memory.
+    Memory(VolatileMemoryError),
+    /// More displays than are valid were in a `OkDisplayInfo`.
+    TooManyDisplays(usize),
+}
+
+impl From<VolatileMemoryError> for GpuResponseEncodeError {
+    fn from(e: VolatileMemoryError) -> GpuResponseEncodeError {
+        GpuResponseEncodeError::Memory(e)
+    }
+}
+
+impl GpuResponse {
+    /// Encodes a this `GpuResponse` into `resp` and the given set of metadata.
+    pub fn encode(&self,
+                  flags: u32,
+                  fence_id: u64,
+                  ctx_id: u32,
+                  resp: VolatileSlice)
+                  -> Result<u32, GpuResponseEncodeError> {
+        let hdr = virtio_gpu_ctrl_hdr {
+            type_: Le32::from(self.get_type()),
+            flags: Le32::from(flags),
+            fence_id: Le64::from(fence_id),
+            ctx_id: Le32::from(ctx_id),
+            padding: Le32::from(0),
+        };
+        let len = match self {
+            &GpuResponse::OkDisplayInfo(ref info) => {
+                if info.len() > VIRTIO_GPU_MAX_SCANOUTS {
+                    return Err(GpuResponseEncodeError::TooManyDisplays(info.len()));
+                }
+                let mut disp_info = virtio_gpu_resp_display_info {
+                    hdr,
+                    pmodes: Default::default(),
+                };
+                for (disp_mode, &(width, height)) in disp_info.pmodes.iter_mut().zip(info) {
+                    disp_mode.r.width = Le32::from(width);
+                    disp_mode.r.height = Le32::from(height);
+                    disp_mode.enabled = Le32::from(1);
+                }
+                resp.get_ref(0)?.store(disp_info);
+                size_of_val(&disp_info)
+            }
+            &GpuResponse::OkCapsetInfo { id, version, size } => {
+                resp.get_ref(0)?
+                    .store(virtio_gpu_resp_capset_info {
+                               hdr,
+                               capset_id: Le32::from(id),
+                               capset_max_version: Le32::from(version),
+                               capset_max_size: Le32::from(size),
+                               padding: Le32::from(0),
+                           });
+                size_of::<virtio_gpu_resp_capset_info>()
+            }
+            &GpuResponse::OkCapset(ref data) => {
+                resp.get_ref(0)?.store(hdr);
+                let resp_data_slice = resp.get_slice(size_of_val(&hdr) as u64, data.len() as u64)?;
+                resp_data_slice.copy_from(data);
+                size_of_val(&hdr) + data.len()
+            }
+            _ => {
+                resp.get_ref(0)?.store(hdr);
+                size_of_val(&hdr)
+            }
+        };
+        Ok(len as u32)
+    }
+
+    /// Gets the `VIRTIO_GPU_*` enum value that corresponds to this variant.
+    pub fn get_type(&self) -> u32 {
+        match self {
+            &GpuResponse::OkNoData => VIRTIO_GPU_RESP_OK_NODATA,
+            &GpuResponse::OkDisplayInfo(_) => VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+            &GpuResponse::OkCapsetInfo { .. } => VIRTIO_GPU_RESP_OK_CAPSET_INFO,
+            &GpuResponse::OkCapset(_) => VIRTIO_GPU_RESP_OK_CAPSET,
+            &GpuResponse::ErrUnspec => VIRTIO_GPU_RESP_ERR_UNSPEC,
+            &GpuResponse::ErrOutOfMemory => VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+            &GpuResponse::ErrInvalidScanoutId => VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+            &GpuResponse::ErrInvalidResourceId => VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+            &GpuResponse::ErrInvalidContextId => VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+            &GpuResponse::ErrInvalidParameter => VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+        }
+    }
+
+    /// Returns true if this response indicates success.
+    pub fn is_ok(&self) -> bool {
+        match self {
+            &GpuResponse::OkNoData => true,
+            &GpuResponse::OkDisplayInfo(_) => true,
+            &GpuResponse::OkCapsetInfo { .. } => true,
+            &GpuResponse::OkCapset(_) => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if this response indicates an error.
+    pub fn is_err(&self) -> bool {
+        !self.is_ok()
+    }
+}