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