devices/virtio/gpu/
protocol.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#![allow(dead_code)]
6#![allow(non_camel_case_types)]
7
8use std::cmp::min;
9use std::convert::From;
10use std::fmt;
11use std::fmt::Display;
12use std::io;
13use std::io::Write;
14use std::marker::PhantomData;
15use std::mem::size_of;
16use std::mem::size_of_val;
17use std::str::from_utf8;
18
19use base::Error as BaseError;
20use base::TubeError;
21use data_model::Le32;
22use data_model::Le64;
23use gpu_display::GpuDisplayError;
24use remain::sorted;
25use rutabaga_gfx::RutabagaError;
26use thiserror::Error;
27use vm_memory::udmabuf::UdmabufError;
28use zerocopy::FromBytes;
29use zerocopy::Immutable;
30use zerocopy::IntoBytes;
31use zerocopy::KnownLayout;
32
33pub use super::super::device_constants::gpu::virtio_gpu_config;
34pub use super::super::device_constants::gpu::VIRTIO_GPU_F_CONTEXT_INIT;
35pub use super::super::device_constants::gpu::VIRTIO_GPU_F_CREATE_GUEST_HANDLE;
36pub use super::super::device_constants::gpu::VIRTIO_GPU_F_EDID;
37pub use super::super::device_constants::gpu::VIRTIO_GPU_F_FENCE_PASSING;
38pub use super::super::device_constants::gpu::VIRTIO_GPU_F_RESOURCE_BLOB;
39pub use super::super::device_constants::gpu::VIRTIO_GPU_F_RESOURCE_UUID;
40pub use super::super::device_constants::gpu::VIRTIO_GPU_F_VIRGL;
41use super::edid::EdidBytes;
42use super::Reader;
43use super::Writer;
44
45pub const VIRTIO_GPU_UNDEFINED: u32 = 0x0;
46
47/* 2d commands */
48pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x100;
49pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x101;
50pub const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x102;
51pub const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x103;
52pub const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x104;
53pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x105;
54pub const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x106;
55pub const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x107;
56pub const VIRTIO_GPU_CMD_GET_CAPSET_INFO: u32 = 0x108;
57pub const VIRTIO_GPU_CMD_GET_CAPSET: u32 = 0x109;
58pub const VIRTIO_GPU_CMD_GET_EDID: u32 = 0x10a;
59pub const VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID: u32 = 0x10b;
60pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB: u32 = 0x10c;
61pub const VIRTIO_GPU_CMD_SET_SCANOUT_BLOB: u32 = 0x10d;
62
63/* 3d commands */
64pub const VIRTIO_GPU_CMD_CTX_CREATE: u32 = 0x200;
65pub const VIRTIO_GPU_CMD_CTX_DESTROY: u32 = 0x201;
66pub const VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: u32 = 0x202;
67pub const VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: u32 = 0x203;
68pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: u32 = 0x204;
69pub const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: u32 = 0x205;
70pub const VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: u32 = 0x206;
71pub const VIRTIO_GPU_CMD_SUBMIT_3D: u32 = 0x207;
72pub const VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB: u32 = 0x208;
73pub const VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB: u32 = 0x209;
74
75/* cursor commands */
76pub const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x300;
77pub const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x301;
78
79/* success responses */
80pub const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
81pub const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
82pub const VIRTIO_GPU_RESP_OK_CAPSET_INFO: u32 = 0x1102;
83pub const VIRTIO_GPU_RESP_OK_CAPSET: u32 = 0x1103;
84pub const VIRTIO_GPU_RESP_OK_EDID: u32 = 0x1104;
85pub const VIRTIO_GPU_RESP_OK_RESOURCE_UUID: u32 = 0x1105;
86pub const VIRTIO_GPU_RESP_OK_MAP_INFO: u32 = 0x1106;
87
88/* CHROMIUM(b/277982577): success responses */
89pub const VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO: u32 = 0x11FF;
90
91/* error responses */
92pub const VIRTIO_GPU_RESP_ERR_UNSPEC: u32 = 0x1200;
93pub const VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: u32 = 0x1201;
94pub const VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: u32 = 0x1202;
95pub const VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: u32 = 0x1203;
96pub const VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID: u32 = 0x1204;
97pub const VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: u32 = 0x1205;
98
99pub const VIRTIO_GPU_BLOB_MEM_GUEST: u32 = 0x0001;
100pub const VIRTIO_GPU_BLOB_MEM_HOST3D: u32 = 0x0002;
101pub const VIRTIO_GPU_BLOB_MEM_HOST3D_GUEST: u32 = 0x0003;
102
103pub const VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE: u32 = 0x0001;
104pub const VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE: u32 = 0x0002;
105pub const VIRTIO_GPU_BLOB_FLAG_USE_CROSS_DEVICE: u32 = 0x0004;
106/* Create a OS-specific handle from guest memory (not upstreamed). */
107pub const VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE: u32 = 0x0008;
108
109pub const VIRTIO_GPU_SHM_ID_NONE: u8 = 0x0000;
110pub const VIRTIO_GPU_SHM_ID_HOST_VISIBLE: u8 = 0x0001;
111
112pub fn virtio_gpu_cmd_str(cmd: u32) -> &'static str {
113    match cmd {
114        VIRTIO_GPU_CMD_GET_DISPLAY_INFO => "VIRTIO_GPU_CMD_GET_DISPLAY_INFO",
115        VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => "VIRTIO_GPU_CMD_RESOURCE_CREATE_2D",
116        VIRTIO_GPU_CMD_RESOURCE_UNREF => "VIRTIO_GPU_CMD_RESOURCE_UNREF",
117        VIRTIO_GPU_CMD_SET_SCANOUT => "VIRTIO_GPU_CMD_SET_SCANOUT",
118        VIRTIO_GPU_CMD_SET_SCANOUT_BLOB => "VIRTIO_GPU_CMD_SET_SCANOUT_BLOB",
119        VIRTIO_GPU_CMD_RESOURCE_FLUSH => "VIRTIO_GPU_CMD_RESOURCE_FLUSH",
120        VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => "VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D",
121        VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => "VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING",
122        VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => "VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING",
123        VIRTIO_GPU_CMD_GET_CAPSET_INFO => "VIRTIO_GPU_CMD_GET_CAPSET_INFO",
124        VIRTIO_GPU_CMD_GET_CAPSET => "VIRTIO_GPU_CMD_GET_CAPSET",
125        VIRTIO_GPU_CMD_GET_EDID => "VIRTIO_GPU_CMD_GET_EDID",
126        VIRTIO_GPU_CMD_CTX_CREATE => "VIRTIO_GPU_CMD_CTX_CREATE",
127        VIRTIO_GPU_CMD_CTX_DESTROY => "VIRTIO_GPU_CMD_CTX_DESTROY",
128        VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE => "VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE",
129        VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE => "VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE",
130        VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID => "VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID",
131        VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB => "VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB",
132        VIRTIO_GPU_CMD_RESOURCE_CREATE_3D => "VIRTIO_GPU_CMD_RESOURCE_CREATE_3D",
133        VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D => "VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D",
134        VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => "VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D",
135        VIRTIO_GPU_CMD_SUBMIT_3D => "VIRTIO_GPU_CMD_SUBMIT_3D",
136        VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB => "VIRTIO_GPU_RESOURCE_MAP_BLOB",
137        VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB => "VIRTIO_GPU_RESOURCE_UNMAP_BLOB",
138        VIRTIO_GPU_CMD_UPDATE_CURSOR => "VIRTIO_GPU_CMD_UPDATE_CURSOR",
139        VIRTIO_GPU_CMD_MOVE_CURSOR => "VIRTIO_GPU_CMD_MOVE_CURSOR",
140        VIRTIO_GPU_RESP_OK_NODATA => "VIRTIO_GPU_RESP_OK_NODATA",
141        VIRTIO_GPU_RESP_OK_DISPLAY_INFO => "VIRTIO_GPU_RESP_OK_DISPLAY_INFO",
142        VIRTIO_GPU_RESP_OK_CAPSET_INFO => "VIRTIO_GPU_RESP_OK_CAPSET_INFO",
143        VIRTIO_GPU_RESP_OK_CAPSET => "VIRTIO_GPU_RESP_OK_CAPSET",
144        VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO => "VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO",
145        VIRTIO_GPU_RESP_OK_RESOURCE_UUID => "VIRTIO_GPU_RESP_OK_RESOURCE_UUID",
146        VIRTIO_GPU_RESP_OK_MAP_INFO => "VIRTIO_GPU_RESP_OK_MAP_INFO",
147        VIRTIO_GPU_RESP_ERR_UNSPEC => "VIRTIO_GPU_RESP_ERR_UNSPEC",
148        VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY => "VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY",
149        VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID => "VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID",
150        VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID => "VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID",
151        VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID => "VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID",
152        VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER => "VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER",
153        _ => "UNKNOWN",
154    }
155}
156
157pub const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0;
158pub const VIRTIO_GPU_FLAG_INFO_RING_IDX: u32 = 1 << 1;
159pub const VIRTIO_GPU_FLAG_FENCE_HOST_SHAREABLE: u32 = 1 << 2;
160
161#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
162#[repr(C)]
163pub struct virtio_gpu_ctrl_hdr {
164    pub type_: Le32,
165    pub flags: Le32,
166    pub fence_id: Le64,
167    pub ctx_id: Le32,
168    pub ring_idx: u8,
169    pub padding: [u8; 3],
170}
171
172/* data passed in the cursor vq */
173
174#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
175#[repr(C)]
176pub struct virtio_gpu_cursor_pos {
177    pub scanout_id: Le32,
178    pub x: Le32,
179    pub y: Le32,
180    pub padding: Le32,
181}
182
183/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
184#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
185#[repr(C)]
186pub struct virtio_gpu_update_cursor {
187    pub hdr: virtio_gpu_ctrl_hdr,
188    pub pos: virtio_gpu_cursor_pos, /* update & move */
189    pub resource_id: Le32,          /* update only */
190    pub hot_x: Le32,                /* update only */
191    pub hot_y: Le32,                /* update only */
192    pub padding: Le32,
193}
194
195/* data passed in the control vq, 2d related */
196
197#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
198#[repr(C)]
199pub struct virtio_gpu_rect {
200    pub x: Le32,
201    pub y: Le32,
202    pub width: Le32,
203    pub height: Le32,
204}
205
206/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
207#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
208#[repr(C)]
209pub struct virtio_gpu_resource_unref {
210    pub hdr: virtio_gpu_ctrl_hdr,
211    pub resource_id: Le32,
212    pub padding: Le32,
213}
214
215/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
216#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
217#[repr(C)]
218pub struct virtio_gpu_resource_create_2d {
219    pub hdr: virtio_gpu_ctrl_hdr,
220    pub resource_id: Le32,
221    pub format: Le32,
222    pub width: Le32,
223    pub height: Le32,
224}
225
226/* VIRTIO_GPU_CMD_SET_SCANOUT */
227#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
228#[repr(C)]
229pub struct virtio_gpu_set_scanout {
230    pub hdr: virtio_gpu_ctrl_hdr,
231    pub r: virtio_gpu_rect,
232    pub scanout_id: Le32,
233    pub resource_id: Le32,
234}
235
236/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
237#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
238#[repr(C)]
239pub struct virtio_gpu_resource_flush {
240    pub hdr: virtio_gpu_ctrl_hdr,
241    pub r: virtio_gpu_rect,
242    pub resource_id: Le32,
243    pub padding: Le32,
244}
245
246/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
247#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
248#[repr(C)]
249pub struct virtio_gpu_transfer_to_host_2d {
250    pub hdr: virtio_gpu_ctrl_hdr,
251    pub r: virtio_gpu_rect,
252    pub offset: Le64,
253    pub resource_id: Le32,
254    pub padding: Le32,
255}
256
257#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
258#[repr(C)]
259pub struct virtio_gpu_mem_entry {
260    pub addr: Le64,
261    pub length: Le32,
262    pub padding: Le32,
263}
264
265/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
266#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
267#[repr(C)]
268pub struct virtio_gpu_resource_attach_backing {
269    pub hdr: virtio_gpu_ctrl_hdr,
270    pub resource_id: Le32,
271    pub nr_entries: Le32,
272}
273
274/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
275#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
276#[repr(C)]
277pub struct virtio_gpu_resource_detach_backing {
278    pub hdr: virtio_gpu_ctrl_hdr,
279    pub resource_id: Le32,
280    pub padding: Le32,
281}
282
283#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
284#[repr(C)]
285pub struct virtio_gpu_display_one {
286    pub r: virtio_gpu_rect,
287    pub enabled: Le32,
288    pub flags: Le32,
289}
290
291/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
292pub const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
293#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
294#[repr(C)]
295pub struct virtio_gpu_resp_display_info {
296    pub hdr: virtio_gpu_ctrl_hdr,
297    pub pmodes: [virtio_gpu_display_one; VIRTIO_GPU_MAX_SCANOUTS],
298}
299
300/* data passed in the control vq, 3d related */
301
302#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
303#[repr(C)]
304pub struct virtio_gpu_box {
305    pub x: Le32,
306    pub y: Le32,
307    pub z: Le32,
308    pub w: Le32,
309    pub h: Le32,
310    pub d: Le32,
311}
312
313/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
314#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
315#[repr(C)]
316pub struct virtio_gpu_transfer_host_3d {
317    pub hdr: virtio_gpu_ctrl_hdr,
318    pub box_: virtio_gpu_box,
319    pub offset: Le64,
320    pub resource_id: Le32,
321    pub level: Le32,
322    pub stride: Le32,
323    pub layer_stride: Le32,
324}
325
326/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
327pub const VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP: u32 = 1 << 0;
328#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
329#[repr(C)]
330pub struct virtio_gpu_resource_create_3d {
331    pub hdr: virtio_gpu_ctrl_hdr,
332    pub resource_id: Le32,
333    pub target: Le32,
334    pub format: Le32,
335    pub bind: Le32,
336    pub width: Le32,
337    pub height: Le32,
338    pub depth: Le32,
339    pub array_size: Le32,
340    pub last_level: Le32,
341    pub nr_samples: Le32,
342    pub flags: Le32,
343    pub padding: Le32,
344}
345
346/* VIRTIO_GPU_CMD_CTX_CREATE */
347pub const VIRTIO_GPU_CONTEXT_INIT_CAPSET_ID_MASK: u32 = 1 << 0;
348#[derive(Copy, FromBytes, Immutable, IntoBytes, KnownLayout)]
349#[repr(C)]
350pub struct virtio_gpu_ctx_create {
351    pub hdr: virtio_gpu_ctrl_hdr,
352    pub nlen: Le32,
353    pub context_init: Le32,
354    pub debug_name: [u8; 64],
355}
356
357impl Default for virtio_gpu_ctx_create {
358    fn default() -> Self {
359        // SAFETY: trivially safe
360        unsafe { ::std::mem::zeroed() }
361    }
362}
363
364impl Clone for virtio_gpu_ctx_create {
365    fn clone(&self) -> virtio_gpu_ctx_create {
366        *self
367    }
368}
369
370impl fmt::Debug for virtio_gpu_ctx_create {
371    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372        let debug_name = from_utf8(&self.debug_name[..min(64, self.nlen.to_native() as usize)])
373            .unwrap_or("<invalid>");
374        f.debug_struct("virtio_gpu_ctx_create")
375            .field("hdr", &self.hdr)
376            .field("debug_name", &debug_name)
377            .finish()
378    }
379}
380
381/* VIRTIO_GPU_CMD_CTX_DESTROY */
382#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
383#[repr(C)]
384pub struct virtio_gpu_ctx_destroy {
385    pub hdr: virtio_gpu_ctrl_hdr,
386}
387
388/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
389#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
390#[repr(C)]
391pub struct virtio_gpu_ctx_resource {
392    pub hdr: virtio_gpu_ctrl_hdr,
393    pub resource_id: Le32,
394    pub padding: Le32,
395}
396
397/* VIRTIO_GPU_CMD_SUBMIT_3D */
398#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
399#[repr(C)]
400pub struct virtio_gpu_cmd_submit {
401    pub hdr: virtio_gpu_ctrl_hdr,
402    pub size: Le32,
403
404    // The in-fence IDs are prepended to the cmd_buf and memory layout
405    // of the VIRTIO_GPU_CMD_SUBMIT_3D buffer looks like this:
406    //   _________________
407    //   | CMD_SUBMIT_3D |
408    //   -----------------
409    //   |  header       |
410    //   |  in-fence IDs |
411    //   |  cmd_buf      |
412    //   -----------------
413    //
414    // This makes in-fence IDs naturally aligned to the sizeof(u64) inside
415    // of the virtio buffer.
416    pub num_in_fences: Le32,
417}
418
419pub const VIRTIO_GPU_CAPSET_VIRGL: u32 = 1;
420pub const VIRTIO_GPU_CAPSET_VIRGL2: u32 = 2;
421pub const VIRTIO_GPU_CAPSET_GFXSTREAM: u32 = 3;
422pub const VIRTIO_GPU_CAPSET_VENUS: u32 = 4;
423pub const VIRTIO_GPU_CAPSET_CROSS_DOMAIN: u32 = 5;
424
425/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
426#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
427#[repr(C)]
428pub struct virtio_gpu_get_capset_info {
429    pub hdr: virtio_gpu_ctrl_hdr,
430    pub capset_index: Le32,
431    pub padding: Le32,
432}
433
434/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
435#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
436#[repr(C)]
437pub struct virtio_gpu_resp_capset_info {
438    pub hdr: virtio_gpu_ctrl_hdr,
439    pub capset_id: Le32,
440    pub capset_max_version: Le32,
441    pub capset_max_size: Le32,
442    pub padding: Le32,
443}
444
445/* VIRTIO_GPU_CMD_GET_CAPSET */
446#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
447#[repr(C)]
448pub struct virtio_gpu_get_capset {
449    pub hdr: virtio_gpu_ctrl_hdr,
450    pub capset_id: Le32,
451    pub capset_version: Le32,
452}
453
454/* VIRTIO_GPU_RESP_OK_CAPSET */
455#[derive(Copy, Clone, Debug, Default)]
456#[repr(C)]
457pub struct virtio_gpu_resp_capset {
458    pub hdr: virtio_gpu_ctrl_hdr,
459    pub capset_data: PhantomData<[u8]>,
460}
461
462/* VIRTIO_GPU_CMD_GET_EDID */
463#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
464#[repr(C)]
465pub struct virtio_gpu_get_edid {
466    pub hdr: virtio_gpu_ctrl_hdr,
467    pub scanout: Le32,
468    pub padding: Le32,
469}
470
471/* VIRTIO_GPU_RESP_OK_EDID */
472#[derive(Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
473#[repr(C)]
474pub struct virtio_gpu_resp_get_edid {
475    pub hdr: virtio_gpu_ctrl_hdr,
476    pub size: Le32,
477    pub padding: Le32,
478    pub edid: [u8; 1024],
479}
480
481/* VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO */
482#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
483#[repr(C)]
484pub struct virtio_gpu_resp_resource_plane_info {
485    pub hdr: virtio_gpu_ctrl_hdr,
486    pub count: Le32,
487    pub padding: Le32,
488    pub format_modifier: Le64,
489    pub strides: [Le32; 4],
490    pub offsets: [Le32; 4],
491}
492
493pub const PLANE_INFO_MAX_COUNT: usize = 4;
494
495pub const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
496
497#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
498#[repr(C)]
499pub struct virtio_gpu_resource_create_blob {
500    pub hdr: virtio_gpu_ctrl_hdr,
501    pub resource_id: Le32,
502    pub blob_mem: Le32,
503    pub blob_flags: Le32,
504    pub nr_entries: Le32,
505    pub blob_id: Le64,
506    pub size: Le64,
507}
508
509#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
510#[repr(C)]
511pub struct virtio_gpu_resource_map_blob {
512    pub hdr: virtio_gpu_ctrl_hdr,
513    pub resource_id: Le32,
514    pub padding: Le32,
515    pub offset: Le64,
516}
517
518#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
519#[repr(C)]
520pub struct virtio_gpu_resource_unmap_blob {
521    pub hdr: virtio_gpu_ctrl_hdr,
522    pub resource_id: Le32,
523    pub padding: Le32,
524}
525
526#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
527#[repr(C)]
528pub struct virtio_gpu_resp_map_info {
529    pub hdr: virtio_gpu_ctrl_hdr,
530    pub map_info: Le32,
531    pub padding: u32,
532}
533
534#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
535#[repr(C)]
536pub struct virtio_gpu_resource_assign_uuid {
537    pub hdr: virtio_gpu_ctrl_hdr,
538    pub resource_id: Le32,
539    pub padding: Le32,
540}
541
542#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
543#[repr(C)]
544pub struct virtio_gpu_resp_resource_uuid {
545    pub hdr: virtio_gpu_ctrl_hdr,
546    pub uuid: [u8; 16],
547}
548
549/* VIRTIO_GPU_CMD_SET_SCANOUT_BLOB */
550#[derive(Copy, Clone, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
551#[repr(C)]
552pub struct virtio_gpu_set_scanout_blob {
553    pub hdr: virtio_gpu_ctrl_hdr,
554    pub r: virtio_gpu_rect,
555    pub scanout_id: Le32,
556    pub resource_id: Le32,
557    pub width: Le32,
558    pub height: Le32,
559    pub format: Le32,
560    pub padding: Le32,
561    pub strides: [Le32; 4],
562    pub offsets: [Le32; 4],
563}
564
565/* simple formats for fbcon/X use */
566pub const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
567pub const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2;
568pub const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3;
569pub const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4;
570pub const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67;
571pub const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68;
572pub const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121;
573pub const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134;
574
575/// A virtio gpu command and associated metadata specific to each command.
576#[derive(Copy, Clone)]
577pub enum GpuCommand {
578    GetDisplayInfo(virtio_gpu_ctrl_hdr),
579    ResourceCreate2d(virtio_gpu_resource_create_2d),
580    ResourceUnref(virtio_gpu_resource_unref),
581    SetScanout(virtio_gpu_set_scanout),
582    SetScanoutBlob(virtio_gpu_set_scanout_blob),
583    ResourceFlush(virtio_gpu_resource_flush),
584    TransferToHost2d(virtio_gpu_transfer_to_host_2d),
585    ResourceAttachBacking(virtio_gpu_resource_attach_backing),
586    ResourceDetachBacking(virtio_gpu_resource_detach_backing),
587    GetCapsetInfo(virtio_gpu_get_capset_info),
588    GetCapset(virtio_gpu_get_capset),
589    GetEdid(virtio_gpu_get_edid),
590    CtxCreate(virtio_gpu_ctx_create),
591    CtxDestroy(virtio_gpu_ctx_destroy),
592    CtxAttachResource(virtio_gpu_ctx_resource),
593    CtxDetachResource(virtio_gpu_ctx_resource),
594    ResourceCreate3d(virtio_gpu_resource_create_3d),
595    TransferToHost3d(virtio_gpu_transfer_host_3d),
596    TransferFromHost3d(virtio_gpu_transfer_host_3d),
597    CmdSubmit3d(virtio_gpu_cmd_submit),
598    ResourceCreateBlob(virtio_gpu_resource_create_blob),
599    ResourceMapBlob(virtio_gpu_resource_map_blob),
600    ResourceUnmapBlob(virtio_gpu_resource_unmap_blob),
601    UpdateCursor(virtio_gpu_update_cursor),
602    MoveCursor(virtio_gpu_update_cursor),
603    ResourceAssignUuid(virtio_gpu_resource_assign_uuid),
604}
605
606/// An error indicating something went wrong decoding a `GpuCommand`. These correspond to
607/// `VIRTIO_GPU_CMD_*`.
608#[sorted]
609#[derive(Error, Debug)]
610pub enum GpuCommandDecodeError {
611    /// The type of the command was invalid.
612    #[error("invalid command type ({0})")]
613    InvalidType(u32),
614    /// An I/O error occurred.
615    #[error("an I/O error occurred: {0}")]
616    IO(io::Error),
617}
618
619impl From<io::Error> for GpuCommandDecodeError {
620    fn from(e: io::Error) -> GpuCommandDecodeError {
621        GpuCommandDecodeError::IO(e)
622    }
623}
624
625impl fmt::Debug for GpuCommand {
626    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
627        use self::GpuCommand::*;
628        match self {
629            GetDisplayInfo(_info) => f.debug_struct("GetDisplayInfo").finish(),
630            ResourceCreate2d(_info) => f.debug_struct("ResourceCreate2d").finish(),
631            ResourceUnref(_info) => f.debug_struct("ResourceUnref").finish(),
632            SetScanout(_info) => f.debug_struct("SetScanout").finish(),
633            SetScanoutBlob(_info) => f.debug_struct("SetScanoutBlob").finish(),
634            ResourceFlush(_info) => f.debug_struct("ResourceFlush").finish(),
635            TransferToHost2d(_info) => f.debug_struct("TransferToHost2d").finish(),
636            ResourceAttachBacking(_info) => f.debug_struct("ResourceAttachBacking").finish(),
637            ResourceDetachBacking(_info) => f.debug_struct("ResourceDetachBacking").finish(),
638            GetCapsetInfo(_info) => f.debug_struct("GetCapsetInfo").finish(),
639            GetCapset(_info) => f.debug_struct("GetCapset").finish(),
640            GetEdid(_info) => f.debug_struct("GetEdid").finish(),
641            CtxCreate(_info) => f.debug_struct("CtxCreate").finish(),
642            CtxDestroy(_info) => f.debug_struct("CtxDestroy").finish(),
643            CtxAttachResource(_info) => f.debug_struct("CtxAttachResource").finish(),
644            CtxDetachResource(_info) => f.debug_struct("CtxDetachResource").finish(),
645            ResourceCreate3d(_info) => f.debug_struct("ResourceCreate3d").finish(),
646            TransferToHost3d(_info) => f.debug_struct("TransferToHost3d").finish(),
647            TransferFromHost3d(_info) => f.debug_struct("TransferFromHost3d").finish(),
648            CmdSubmit3d(_info) => f.debug_struct("CmdSubmit3d").finish(),
649            ResourceCreateBlob(_info) => f.debug_struct("ResourceCreateBlob").finish(),
650            ResourceMapBlob(_info) => f.debug_struct("ResourceMapBlob").finish(),
651            ResourceUnmapBlob(_info) => f.debug_struct("ResourceUnmapBlob").finish(),
652            UpdateCursor(_info) => f.debug_struct("UpdateCursor").finish(),
653            MoveCursor(_info) => f.debug_struct("MoveCursor").finish(),
654            ResourceAssignUuid(_info) => f.debug_struct("ResourceAssignUuid").finish(),
655        }
656    }
657}
658
659impl GpuCommand {
660    /// Decodes a command from the given chunk of memory.
661    pub fn decode(cmd: &mut Reader) -> Result<GpuCommand, GpuCommandDecodeError> {
662        use self::GpuCommand::*;
663        let hdr = cmd.peek_obj::<virtio_gpu_ctrl_hdr>()?;
664        Ok(match hdr.type_.into() {
665            VIRTIO_GPU_CMD_GET_DISPLAY_INFO => GetDisplayInfo(cmd.read_obj()?),
666            VIRTIO_GPU_CMD_RESOURCE_CREATE_2D => ResourceCreate2d(cmd.read_obj()?),
667            VIRTIO_GPU_CMD_RESOURCE_UNREF => ResourceUnref(cmd.read_obj()?),
668            VIRTIO_GPU_CMD_SET_SCANOUT => SetScanout(cmd.read_obj()?),
669            VIRTIO_GPU_CMD_SET_SCANOUT_BLOB => SetScanoutBlob(cmd.read_obj()?),
670            VIRTIO_GPU_CMD_RESOURCE_FLUSH => ResourceFlush(cmd.read_obj()?),
671            VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D => TransferToHost2d(cmd.read_obj()?),
672            VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING => ResourceAttachBacking(cmd.read_obj()?),
673            VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING => ResourceDetachBacking(cmd.read_obj()?),
674            VIRTIO_GPU_CMD_GET_CAPSET_INFO => GetCapsetInfo(cmd.read_obj()?),
675            VIRTIO_GPU_CMD_GET_CAPSET => GetCapset(cmd.read_obj()?),
676            VIRTIO_GPU_CMD_GET_EDID => GetEdid(cmd.read_obj()?),
677            VIRTIO_GPU_CMD_CTX_CREATE => CtxCreate(cmd.read_obj()?),
678            VIRTIO_GPU_CMD_CTX_DESTROY => CtxDestroy(cmd.read_obj()?),
679            VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE => CtxAttachResource(cmd.read_obj()?),
680            VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE => CtxDetachResource(cmd.read_obj()?),
681            VIRTIO_GPU_CMD_RESOURCE_CREATE_3D => ResourceCreate3d(cmd.read_obj()?),
682            VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D => TransferToHost3d(cmd.read_obj()?),
683            VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D => TransferFromHost3d(cmd.read_obj()?),
684            VIRTIO_GPU_CMD_SUBMIT_3D => CmdSubmit3d(cmd.read_obj()?),
685            VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB => ResourceCreateBlob(cmd.read_obj()?),
686            VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB => ResourceMapBlob(cmd.read_obj()?),
687            VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB => ResourceUnmapBlob(cmd.read_obj()?),
688            VIRTIO_GPU_CMD_UPDATE_CURSOR => UpdateCursor(cmd.read_obj()?),
689            VIRTIO_GPU_CMD_MOVE_CURSOR => MoveCursor(cmd.read_obj()?),
690            VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID => ResourceAssignUuid(cmd.read_obj()?),
691            _ => return Err(GpuCommandDecodeError::InvalidType(hdr.type_.into())),
692        })
693    }
694
695    /// Gets the generic `virtio_gpu_ctrl_hdr` from this command.
696    pub fn ctrl_hdr(&self) -> &virtio_gpu_ctrl_hdr {
697        use self::GpuCommand::*;
698        match self {
699            GetDisplayInfo(info) => info,
700            ResourceCreate2d(info) => &info.hdr,
701            ResourceUnref(info) => &info.hdr,
702            SetScanout(info) => &info.hdr,
703            SetScanoutBlob(info) => &info.hdr,
704            ResourceFlush(info) => &info.hdr,
705            TransferToHost2d(info) => &info.hdr,
706            ResourceAttachBacking(info) => &info.hdr,
707            ResourceDetachBacking(info) => &info.hdr,
708            GetCapsetInfo(info) => &info.hdr,
709            GetCapset(info) => &info.hdr,
710            GetEdid(info) => &info.hdr,
711            CtxCreate(info) => &info.hdr,
712            CtxDestroy(info) => &info.hdr,
713            CtxAttachResource(info) => &info.hdr,
714            CtxDetachResource(info) => &info.hdr,
715            ResourceCreate3d(info) => &info.hdr,
716            TransferToHost3d(info) => &info.hdr,
717            TransferFromHost3d(info) => &info.hdr,
718            CmdSubmit3d(info) => &info.hdr,
719            ResourceCreateBlob(info) => &info.hdr,
720            ResourceMapBlob(info) => &info.hdr,
721            ResourceUnmapBlob(info) => &info.hdr,
722            UpdateCursor(info) => &info.hdr,
723            MoveCursor(info) => &info.hdr,
724            ResourceAssignUuid(info) => &info.hdr,
725        }
726    }
727}
728
729#[derive(Debug, PartialEq, Eq)]
730pub struct GpuResponsePlaneInfo {
731    pub stride: u32,
732    pub offset: u32,
733}
734
735/// A response to a `GpuCommand`. These correspond to `VIRTIO_GPU_RESP_*`.
736#[derive(Debug)]
737pub enum GpuResponse {
738    OkNoData,
739    OkDisplayInfo(Vec<(u32, u32, bool)>),
740    OkCapsetInfo {
741        capset_id: u32,
742        version: u32,
743        size: u32,
744    },
745    OkCapset(Vec<u8>),
746    OkEdid(Box<EdidBytes>),
747    OkResourcePlaneInfo {
748        format_modifier: u64,
749        plane_info: Vec<GpuResponsePlaneInfo>,
750    },
751    OkResourceUuid {
752        uuid: [u8; 16],
753    },
754    OkMapInfo {
755        map_info: u32,
756    },
757    ErrUnspec,
758    ErrTube(TubeError),
759    ErrBase(BaseError),
760    ErrRutabaga(RutabagaError),
761    ErrDisplay(GpuDisplayError),
762    ErrScanout {
763        num_scanouts: u32,
764    },
765    ErrEdid(String),
766    ErrOutOfMemory,
767    ErrInvalidScanoutId,
768    ErrInvalidResourceId,
769    ErrInvalidContextId,
770    ErrInvalidParameter,
771    ErrUdmabuf(UdmabufError),
772}
773
774impl From<TubeError> for GpuResponse {
775    fn from(e: TubeError) -> GpuResponse {
776        GpuResponse::ErrTube(e)
777    }
778}
779
780impl From<RutabagaError> for GpuResponse {
781    fn from(e: RutabagaError) -> GpuResponse {
782        GpuResponse::ErrRutabaga(e)
783    }
784}
785
786impl From<GpuDisplayError> for GpuResponse {
787    fn from(e: GpuDisplayError) -> GpuResponse {
788        GpuResponse::ErrDisplay(e)
789    }
790}
791
792impl From<UdmabufError> for GpuResponse {
793    fn from(e: UdmabufError) -> GpuResponse {
794        GpuResponse::ErrUdmabuf(e)
795    }
796}
797
798impl Display for GpuResponse {
799    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
800        use self::GpuResponse::*;
801        match self {
802            OkNoData => write!(f, "ok no data"),
803            OkDisplayInfo(_) => write!(f, "ok display info"),
804            OkCapsetInfo { .. } => write!(f, "ok capset info"),
805            OkCapset(_) => write!(f, "ok capset"),
806            OkEdid(_) => write!(f, "ok edid"),
807            OkResourcePlaneInfo { .. } => write!(f, "ok resource plane info"),
808            OkResourceUuid { .. } => write!(f, "ok resource uuid"),
809            OkMapInfo { map_info } => write!(f, "ok map info: {map_info}"),
810            ErrUnspec => write!(f, "unspecified error"),
811            ErrTube(e) => write!(f, "tube error: {e}"),
812            ErrBase(e) => write!(f, "base error: {e}"),
813            ErrRutabaga(e) => write!(f, "renderer error: {e}"),
814            ErrDisplay(e) => write!(f, "display error: {e}"),
815            ErrScanout { num_scanouts } => write!(f, "non-zero scanout: {num_scanouts}"),
816            ErrEdid(e) => write!(f, "edid error: {e}"),
817            ErrOutOfMemory => write!(f, "out of memory error"),
818            ErrInvalidScanoutId => write!(f, "invalid scanout id"),
819            ErrInvalidResourceId => write!(f, "invalid resource id"),
820            ErrInvalidContextId => write!(f, "invalid context id"),
821            ErrInvalidParameter => write!(f, "invalid parameter"),
822            ErrUdmabuf(e) => write!(f, "udmabuf error: {e}"),
823        }
824    }
825}
826
827impl std::error::Error for GpuResponse {}
828
829/// An error indicating something went wrong decoding a `GpuCommand`.
830#[sorted]
831#[derive(Error, Debug)]
832pub enum GpuResponseEncodeError {
833    /// An I/O error occurred.
834    #[error("an I/O error occurred: {0}")]
835    IO(io::Error),
836    /// More displays than are valid were in a `OkDisplayInfo`.
837    #[error("{0} is more displays than are valid")]
838    TooManyDisplays(usize),
839    /// More planes than are valid were in a `OkResourcePlaneInfo`.
840    #[error("{0} is more planes than are valid")]
841    TooManyPlanes(usize),
842}
843
844impl From<io::Error> for GpuResponseEncodeError {
845    fn from(e: io::Error) -> GpuResponseEncodeError {
846        GpuResponseEncodeError::IO(e)
847    }
848}
849
850pub type VirtioGpuResult = std::result::Result<GpuResponse, GpuResponse>;
851
852impl GpuResponse {
853    /// Encodes a this `GpuResponse` into `resp` and the given set of metadata.
854    pub fn encode(
855        &self,
856        flags: u32,
857        fence_id: u64,
858        ctx_id: u32,
859        ring_idx: u8,
860        resp: &mut Writer,
861    ) -> Result<u32, GpuResponseEncodeError> {
862        let hdr = virtio_gpu_ctrl_hdr {
863            type_: Le32::from(self.get_type()),
864            flags: Le32::from(flags),
865            fence_id: Le64::from(fence_id),
866            ctx_id: Le32::from(ctx_id),
867            ring_idx,
868            padding: Default::default(),
869        };
870        let len = match *self {
871            GpuResponse::OkDisplayInfo(ref info) => {
872                if info.len() > VIRTIO_GPU_MAX_SCANOUTS {
873                    return Err(GpuResponseEncodeError::TooManyDisplays(info.len()));
874                }
875                let mut disp_info = virtio_gpu_resp_display_info {
876                    hdr,
877                    pmodes: Default::default(),
878                };
879                for (disp_mode, &(width, height, enabled)) in disp_info.pmodes.iter_mut().zip(info)
880                {
881                    disp_mode.r.width = Le32::from(width);
882                    disp_mode.r.height = Le32::from(height);
883                    disp_mode.enabled = Le32::from(enabled as u32);
884                }
885                resp.write_obj(disp_info)?;
886                size_of_val(&disp_info)
887            }
888            GpuResponse::OkCapsetInfo {
889                capset_id,
890                version,
891                size,
892            } => {
893                resp.write_obj(virtio_gpu_resp_capset_info {
894                    hdr,
895                    capset_id: Le32::from(capset_id),
896                    capset_max_version: Le32::from(version),
897                    capset_max_size: Le32::from(size),
898                    padding: Le32::from(0),
899                })?;
900                size_of::<virtio_gpu_resp_capset_info>()
901            }
902            GpuResponse::OkCapset(ref data) => {
903                resp.write_obj(hdr)?;
904                resp.write_all(data)?;
905                size_of_val(&hdr) + data.len()
906            }
907            GpuResponse::OkEdid(ref edid_bytes) => {
908                let mut edid_resp = virtio_gpu_resp_get_edid {
909                    hdr,
910                    size: Le32::from(1024),
911                    padding: Le32::from(0),
912                    edid: [0; 1024],
913                };
914
915                edid_resp.edid[0..edid_bytes.len()].copy_from_slice(edid_bytes.as_bytes());
916                resp.write_obj(edid_resp)?;
917                size_of::<virtio_gpu_resp_get_edid>()
918            }
919            GpuResponse::OkResourcePlaneInfo {
920                format_modifier,
921                ref plane_info,
922            } => {
923                if plane_info.len() > PLANE_INFO_MAX_COUNT {
924                    return Err(GpuResponseEncodeError::TooManyPlanes(plane_info.len()));
925                }
926                let mut strides = [Le32::default(); PLANE_INFO_MAX_COUNT];
927                let mut offsets = [Le32::default(); PLANE_INFO_MAX_COUNT];
928                for (plane_index, plane) in plane_info.iter().enumerate() {
929                    strides[plane_index] = plane.stride.into();
930                    offsets[plane_index] = plane.offset.into();
931                }
932                let plane_info = virtio_gpu_resp_resource_plane_info {
933                    hdr,
934                    count: Le32::from(plane_info.len() as u32),
935                    padding: 0.into(),
936                    format_modifier: format_modifier.into(),
937                    strides,
938                    offsets,
939                };
940                if resp.available_bytes() >= size_of_val(&plane_info) {
941                    resp.write_obj(plane_info)?;
942                    size_of_val(&plane_info)
943                } else {
944                    // In case there is too little room in the response slice to store the
945                    // entire virtio_gpu_resp_resource_plane_info, convert response to a regular
946                    // VIRTIO_GPU_RESP_OK_NODATA and attempt to return that.
947                    resp.write_obj(virtio_gpu_ctrl_hdr {
948                        type_: Le32::from(VIRTIO_GPU_RESP_OK_NODATA),
949                        ..hdr
950                    })?;
951                    size_of_val(&hdr)
952                }
953            }
954            GpuResponse::OkResourceUuid { uuid } => {
955                let resp_info = virtio_gpu_resp_resource_uuid { hdr, uuid };
956
957                resp.write_obj(resp_info)?;
958                size_of_val(&resp_info)
959            }
960            GpuResponse::OkMapInfo { map_info } => {
961                let resp_info = virtio_gpu_resp_map_info {
962                    hdr,
963                    map_info: Le32::from(map_info),
964                    padding: Default::default(),
965                };
966
967                resp.write_obj(resp_info)?;
968                size_of_val(&resp_info)
969            }
970            _ => {
971                resp.write_obj(hdr)?;
972                size_of_val(&hdr)
973            }
974        };
975        Ok(len as u32)
976    }
977
978    /// Gets the `VIRTIO_GPU_*` enum value that corresponds to this variant.
979    pub fn get_type(&self) -> u32 {
980        match self {
981            GpuResponse::OkNoData => VIRTIO_GPU_RESP_OK_NODATA,
982            GpuResponse::OkDisplayInfo(_) => VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
983            GpuResponse::OkCapsetInfo { .. } => VIRTIO_GPU_RESP_OK_CAPSET_INFO,
984            GpuResponse::OkCapset(_) => VIRTIO_GPU_RESP_OK_CAPSET,
985            GpuResponse::OkEdid(_) => VIRTIO_GPU_RESP_OK_EDID,
986            GpuResponse::OkResourcePlaneInfo { .. } => VIRTIO_GPU_RESP_OK_RESOURCE_PLANE_INFO,
987            GpuResponse::OkResourceUuid { .. } => VIRTIO_GPU_RESP_OK_RESOURCE_UUID,
988            GpuResponse::OkMapInfo { .. } => VIRTIO_GPU_RESP_OK_MAP_INFO,
989            GpuResponse::ErrUnspec => VIRTIO_GPU_RESP_ERR_UNSPEC,
990            GpuResponse::ErrTube(_) => VIRTIO_GPU_RESP_ERR_UNSPEC,
991            GpuResponse::ErrBase(_) => VIRTIO_GPU_RESP_ERR_UNSPEC,
992            GpuResponse::ErrRutabaga(_) => VIRTIO_GPU_RESP_ERR_UNSPEC,
993            GpuResponse::ErrDisplay(_) => VIRTIO_GPU_RESP_ERR_UNSPEC,
994            GpuResponse::ErrUdmabuf(_) => VIRTIO_GPU_RESP_ERR_UNSPEC,
995            GpuResponse::ErrScanout { num_scanouts: _ } => VIRTIO_GPU_RESP_ERR_UNSPEC,
996            GpuResponse::ErrEdid(_) => VIRTIO_GPU_RESP_ERR_UNSPEC,
997            GpuResponse::ErrOutOfMemory => VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
998            GpuResponse::ErrInvalidScanoutId => VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
999            GpuResponse::ErrInvalidResourceId => VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
1000            GpuResponse::ErrInvalidContextId => VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
1001            GpuResponse::ErrInvalidParameter => VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
1002        }
1003    }
1004}