diff --git a/vhost/src/vhost_user/gpu_backend_req.rs b/vhost/src/vhost_user/gpu_backend_req.rs index 11b68e2..5bad9bb 100644 --- a/vhost/src/vhost_user/gpu_backend_req.rs +++ b/vhost/src/vhost_user/gpu_backend_req.rs @@ -63,6 +63,23 @@ impl BackendInternal { Ok(hdr) } + fn send_message_with_payload( + &mut self, + request: GpuBackendReq, + body: &T, + data: &[u8], + fds: Option<&[RawFd]>, + ) -> io::Result> { + self.check_state()?; + + let len = mem::size_of::() + data.len(); + let hdr = VhostUserGpuMsgHeader::new(request, 0, len as u32); + self.sock + .send_message_with_payload(&hdr, body, data, fds) + .map_err(io_err_convert_fn("send_message_with_payload"))?; + Ok(hdr) + } + // Note that there is no VHOST_USER_PROTOCOL_F_REPLY_ACK for this protocol, some messages always // expect a reply/ack and others don't expect a reply/ack at all. fn recv_reply( @@ -132,6 +149,16 @@ impl GpuBackend { Ok(()) } + /// Sends the VHOST_USER_GPU_UPDATE message to the frontend. Doesn't wait for a reply. + /// Updates the scanout content. The data payload contains the graphical bits. + /// The display should be flushed and presented. + pub fn update_scanout(&self, update: &VhostUserGpuUpdate, data: &[u8]) -> io::Result<()> { + let mut node = self.node(); + + node.send_message_with_payload(GpuBackendReq::UPDATE, update, data, None)?; + Ok(()) + } + /// Create a new instance from a `UnixStream` object. pub fn from_stream(sock: UnixStream) -> Self { Self::new(Endpoint::>::from_stream(sock)) @@ -274,4 +301,38 @@ mod tests { sender_thread.join().expect("Failed to send!"); } + + #[test] + fn test_update_scanout() { + let (mut frontend, backend) = frontend_backend_pair(); + + let request = VhostUserGpuUpdate { + scanout_id: 1, + x: 30, + y: 40, + width: 10, + height: 10, + }; + let payload = [1u8; 4 * 10 * 10]; + + let sender_thread = thread::spawn(move || { + let _: () = backend.update_scanout(&request, &payload).unwrap(); + }); + + let mut recv_buf = [0u8; 4096]; + let (hdr, req_body, recv_buf_len, fds) = frontend + .recv_payload_into_buf::(&mut recv_buf) + .unwrap(); + assert!(fds.is_none()); + assert_hdr( + &hdr, + GpuBackendReq::UPDATE, + size_of_val(&request) + payload.len(), + ); + assert_eq!(req_body, request); + + assert_eq!(&payload[..], &recv_buf[..recv_buf_len]); + + sender_thread.join().expect("Failed to send!"); + } } diff --git a/vhost/src/vhost_user/gpu_message.rs b/vhost/src/vhost_user/gpu_message.rs index 89b12d8..33fcb09 100644 --- a/vhost/src/vhost_user/gpu_message.rs +++ b/vhost/src/vhost_user/gpu_message.rs @@ -262,6 +262,27 @@ unsafe impl ByteValued for VhostUserGpuEdidRequest {} impl VhostUserMsgValidator for VhostUserGpuEdidRequest {} +/// The VhostUserGpuUpdate from the vhost-user-gpu specification. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +#[repr(C)] +pub struct VhostUserGpuUpdate { + /// The id of the scanout that is being updated + pub scanout_id: u32, + /// The x coordinate of the region to update + pub x: u32, + /// The y coordinate of the region to update + pub y: u32, + /// The width of the region to update + pub width: u32, + /// The height of the region to update + pub height: u32, +} + +// SAFETY: Safe because all fields are POD. +unsafe impl ByteValued for VhostUserGpuUpdate {} + +impl VhostUserMsgValidator for VhostUserGpuUpdate {} + /// The virtio_gpu_resp_edid struct from the virtio specification. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(C)]