Skip to main content

uidevices/video/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! A vmbus synthetic video device.
5
6mod protocol;
7
8use async_trait::async_trait;
9use guestmem::AccessError;
10use guid::Guid;
11use mesh::payload::Protobuf;
12use std::io::IoSlice;
13use task_control::StopTask;
14use thiserror::Error;
15use video_core::DirtyRect;
16use video_core::FramebufferControl;
17use video_core::FramebufferFormat;
18use vmbus_async::async_dgram::AsyncRecv;
19use vmbus_async::async_dgram::AsyncRecvExt;
20use vmbus_async::async_dgram::AsyncSend;
21use vmbus_async::async_dgram::AsyncSendExt;
22use vmbus_async::pipe::MessagePipe;
23use vmbus_channel::RawAsyncChannel;
24use vmbus_channel::bus::ChannelType;
25use vmbus_channel::bus::OfferParams;
26use vmbus_channel::channel::ChannelOpenError;
27use vmbus_channel::gpadl_ring::GpadlRingMem;
28use vmbus_channel::simple::SaveRestoreSimpleVmbusDevice;
29use vmbus_channel::simple::SimpleVmbusDevice;
30use vmcore::save_restore::SavedStateRoot;
31use zerocopy::FromBytes;
32use zerocopy::Immutable;
33use zerocopy::IntoBytes;
34use zerocopy::KnownLayout;
35use zerocopy::Ref;
36
37#[derive(Debug, Error)]
38enum Error {
39    #[error("out of order packet")]
40    UnexpectedPacketOrder,
41    #[error("memory access error")]
42    Access(#[from] AccessError),
43    #[error("unknown message type: {0:#x}")]
44    UnknownMessageType(u32),
45    #[error("invalid packet")]
46    InvalidPacket,
47    #[error("channel i/o error")]
48    Io(#[source] std::io::Error),
49    #[error("failed to accept vmbus channel")]
50    Accept(#[from] vmbus_channel::offer::Error),
51}
52
53#[derive(Debug)]
54enum Request {
55    Version(protocol::Version),
56    VramLocation {
57        user_context: u64,
58        address: Option<u64>,
59    },
60    SituationUpdate {
61        user_context: u64,
62        situation: protocol::VideoOutputSituation,
63    },
64    PointerPosition {
65        is_visible: bool,
66        x: i32,
67        y: i32,
68    },
69    PointerShape,
70    Dirt(Vec<protocol::Rectangle>),
71    BiosInfo,
72    SupportedResolutions {
73        maximum_count: u8,
74    },
75    Capability,
76}
77
78fn parse_packet(buf: &[u8]) -> Result<Request, Error> {
79    let (header, buf) =
80        Ref::<_, protocol::MessageHeader>::from_prefix(buf).map_err(|_| Error::InvalidPacket)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
81    let request = match header.typ.to_ne() {
82        protocol::MESSAGE_VERSION_REQUEST => {
83            let message = protocol::VersionRequestMessage::ref_from_prefix(buf)
84                .map_err(|_| Error::InvalidPacket)?
85                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
86            Request::Version(message.version)
87        }
88        protocol::MESSAGE_VRAM_LOCATION => {
89            let message = protocol::VramLocationMessage::ref_from_prefix(buf)
90                .map_err(|_| Error::InvalidPacket)?
91                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
92            let address = if message.is_vram_gpa_address_specified != 0 {
93                Some(message.vram_gpa_address.into())
94            } else {
95                None
96            };
97            Request::VramLocation {
98                user_context: message.user_context.into(),
99                address,
100            }
101        }
102        protocol::MESSAGE_SITUATION_UPDATE => {
103            let message = protocol::SituationUpdateMessage::ref_from_prefix(buf)
104                .map_err(|_| Error::InvalidPacket)?
105                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
106            Request::SituationUpdate {
107                user_context: message.user_context.into(),
108                situation: message.video_output,
109            }
110        }
111        protocol::MESSAGE_POINTER_POSITION => {
112            let message = protocol::PointerPositionMessage::ref_from_prefix(buf)
113                .map_err(|_| Error::InvalidPacket)?
114                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
115            Request::PointerPosition {
116                is_visible: message.is_visible != 0,
117                x: message.image_x.into(),
118                y: message.image_y.into(),
119            }
120        }
121        protocol::MESSAGE_POINTER_SHAPE => {
122            //let message = protocol::PointerShapeMessage::from_bytes(buf).map_err(|_| Error::InvalidPacket)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
123            Request::PointerShape
124        }
125        protocol::MESSAGE_DIRT => {
126            let (message, buf) = Ref::<_, protocol::DirtMessage>::from_prefix(buf)
127                .map_err(|_| Error::InvalidPacket)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
128            Request::Dirt(
129                <[protocol::Rectangle]>::ref_from_prefix_with_elems(
130                    buf,
131                    message.dirt_count as usize,
132                )
133                .map_err(|_| Error::InvalidPacket)? // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
134                .0
135                .into(),
136            )
137        }
138        protocol::MESSAGE_BIOS_INFO_REQUEST => Request::BiosInfo,
139        protocol::MESSAGE_SUPPORTED_RESOLUTIONS_REQUEST => {
140            let message = protocol::SupportedResolutionsRequestMessage::ref_from_prefix(buf)
141                .map_err(|_| Error::InvalidPacket)?
142                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
143            Request::SupportedResolutions {
144                maximum_count: message.maximum_resolution_count,
145            }
146        }
147        protocol::MESSAGE_CAPABILITY_REQUEST => Request::Capability,
148        typ => return Err(Error::UnknownMessageType(typ)),
149    };
150    Ok(request)
151}
152
153/// Vmbus synthetic video device.
154pub struct Video {
155    control: Box<dyn FramebufferControl>,
156    /// Channel to forward dirty rectangles from the guest to the VNC worker.
157    dirt_send: Option<mesh::Sender<Vec<DirtyRect>>>,
158}
159
160impl Video {
161    /// Creates a new video device.
162    pub fn new(
163        control: Box<dyn FramebufferControl>,
164        dirt_send: Option<mesh::Sender<Vec<DirtyRect>>>,
165    ) -> anyhow::Result<Self> {
166        Ok(Self { control, dirt_send })
167    }
168}
169
170/// The video device saved state.
171#[derive(Protobuf, SavedStateRoot)]
172#[mesh(package = "ui.synthvid")]
173pub struct SavedState(ChannelState);
174
175/// The video task.
176pub struct VideoChannel {
177    channel: MessagePipe<GpadlRingMem>,
178    state: ChannelState,
179    packet_buf: PacketBuffer,
180}
181
182#[derive(Debug, Copy, Clone, Protobuf)]
183#[mesh(package = "ui.synthvid")]
184struct Version {
185    #[mesh(1)]
186    major: u16,
187    #[mesh(2)]
188    minor: u16,
189}
190
191impl From<protocol::Version> for Version {
192    fn from(version: protocol::Version) -> Self {
193        Self {
194            major: version.major(),
195            minor: version.minor(),
196        }
197    }
198}
199
200impl From<Version> for protocol::Version {
201    fn from(version: Version) -> Self {
202        Self::new(version.major, version.minor)
203    }
204}
205
206#[derive(Debug, Clone, Protobuf, Default)]
207#[mesh(package = "ui.synthvid")]
208enum ChannelState {
209    #[mesh(1)]
210    #[default]
211    ReadVersion,
212    #[mesh(2)]
213    WriteVersion {
214        #[mesh(1)]
215        version: Version,
216    },
217    #[mesh(3)]
218    Active {
219        #[mesh(1)]
220        version: Version,
221        #[mesh(2)]
222        substate: ActiveState,
223    },
224}
225
226#[derive(Debug, Clone, Protobuf)]
227#[mesh(package = "ui.synthvid")]
228enum ActiveState {
229    #[mesh(1)]
230    ReadRequest,
231    #[mesh(2)]
232    SendVramAck {
233        #[mesh(1)]
234        user_context: u64,
235    },
236    #[mesh(3)]
237    SendSituationUpdateAck {
238        #[mesh(1)]
239        user_context: u64,
240    },
241    #[mesh(4)]
242    SendBiosInfo,
243    #[mesh(5)]
244    SendSupportedResolutions {
245        #[mesh(1)]
246        maximum_count: u8,
247    },
248    #[mesh(6)]
249    SendCapability,
250}
251
252struct PacketBuffer {
253    buf: Vec<u8>,
254}
255
256impl PacketBuffer {
257    fn new() -> Self {
258        Self {
259            buf: vec![0; protocol::MAX_VMBUS_PACKET_SIZE],
260        }
261    }
262
263    async fn recv_packet(
264        &mut self,
265        reader: &mut (impl AsyncRecv + Unpin),
266    ) -> Result<Request, Error> {
267        let n = match reader.recv(&mut self.buf).await {
268            Ok(n) => n,
269            Err(e) => return Err(Error::Io(e)),
270        };
271        let buf = &self.buf[..n];
272        parse_packet(buf)
273    }
274}
275
276#[async_trait]
277impl SimpleVmbusDevice for Video {
278    type Runner = VideoChannel;
279    type SavedState = SavedState;
280
281    fn offer(&self) -> OfferParams {
282        OfferParams {
283            interface_name: "video".to_owned(),
284            interface_id: Guid {
285                data1: 0xda0a7802,
286                data2: 0xe377,
287                data3: 0x4aac,
288                data4: [0x8e, 0x77, 0x5, 0x58, 0xeb, 0x10, 0x73, 0xf8],
289            },
290            instance_id: Guid {
291                data1: 0x5620e0c7,
292                data2: 0x8062,
293                data3: 0x4dce,
294                data4: [0xae, 0xb7, 0x52, 0xc, 0x7e, 0xf7, 0x61, 0x71],
295            },
296            mmio_megabytes: 8,
297            channel_type: ChannelType::Device { pipe_packets: true },
298            ..Default::default()
299        }
300    }
301
302    fn inspect(&mut self, req: inspect::Request<'_>, task: Option<&mut VideoChannel>) {
303        let mut resp = req.respond();
304        if let Some(this) = task {
305            let (version, state) = match &this.state {
306                ChannelState::ReadVersion => (None, "read_version"),
307                ChannelState::WriteVersion { version } => (Some(*version), "write_version"),
308                ChannelState::Active { version, substate } => (
309                    Some(*version),
310                    match substate {
311                        ActiveState::ReadRequest => "read_request",
312                        ActiveState::SendVramAck { .. } => "send_vram_ack",
313                        ActiveState::SendSituationUpdateAck { .. } => "send_situation_update_ack",
314                        ActiveState::SendBiosInfo => "send_bios_info",
315                        ActiveState::SendSupportedResolutions { .. } => {
316                            "send_supported_resolutions"
317                        }
318                        ActiveState::SendCapability => "send_capability",
319                    },
320                ),
321            };
322            resp.field("state", state)
323                .field(
324                    "version",
325                    version.map(|v| format!("{}.{}", v.major, v.minor)),
326                )
327                .field_mut("channel", &mut this.channel);
328        }
329    }
330
331    fn open(
332        &mut self,
333        channel: RawAsyncChannel<GpadlRingMem>,
334        _guest_memory: guestmem::GuestMemory,
335    ) -> Result<Self::Runner, ChannelOpenError> {
336        let pipe = MessagePipe::new(channel)?;
337        Ok(VideoChannel::new(pipe, ChannelState::default()))
338    }
339
340    async fn run(
341        &mut self,
342        stop: &mut StopTask<'_>,
343        channel: &mut VideoChannel,
344    ) -> Result<(), task_control::Cancelled> {
345        stop.until_stopped(async {
346            match channel.process(&mut self.control, &self.dirt_send).await {
347                Ok(()) => {}
348                Err(err) => tracing::error!(error = &err as &dyn std::error::Error, "video error"),
349            }
350        })
351        .await
352    }
353
354    fn supports_save_restore(
355        &mut self,
356    ) -> Option<
357        &mut dyn SaveRestoreSimpleVmbusDevice<SavedState = Self::SavedState, Runner = Self::Runner>,
358    > {
359        Some(self)
360    }
361}
362
363impl SaveRestoreSimpleVmbusDevice for Video {
364    fn save_open(&mut self, runner: &Self::Runner) -> Self::SavedState {
365        SavedState(runner.state.clone())
366    }
367
368    fn restore_open(
369        &mut self,
370        state: Self::SavedState,
371        channel: RawAsyncChannel<GpadlRingMem>,
372    ) -> Result<Self::Runner, ChannelOpenError> {
373        let pipe = MessagePipe::new(channel)?;
374        Ok(VideoChannel::new(pipe, state.0))
375    }
376}
377
378impl VideoChannel {
379    fn new(channel: MessagePipe<GpadlRingMem>, state: ChannelState) -> Self {
380        Self {
381            channel,
382            state,
383            packet_buf: PacketBuffer::new(),
384        }
385    }
386
387    async fn send_packet<T: IntoBytes + ?Sized + Immutable + KnownLayout>(
388        writer: &mut (impl AsyncSend + Unpin),
389        typ: u32,
390        packet: &T,
391    ) -> Result<(), Error> {
392        let header = protocol::MessageHeader {
393            typ: typ.into(),
394            size: (size_of_val(packet) as u32).into(),
395        };
396        writer
397            .send_vectored(&[
398                IoSlice::new(header.as_bytes()),
399                IoSlice::new(packet.as_bytes()),
400            ])
401            .await
402            .map_err(Error::Io)?;
403
404        Ok(())
405    }
406
407    async fn process(
408        &mut self,
409        framebuffer: &mut Box<dyn FramebufferControl>,
410        dirt_send: &Option<mesh::Sender<Vec<DirtyRect>>>,
411    ) -> Result<(), Error> {
412        process_channel(
413            &mut self.channel,
414            &mut self.state,
415            &mut self.packet_buf,
416            framebuffer,
417            dirt_send,
418        )
419        .await
420    }
421}
422
423async fn process_channel(
424    channel: &mut (impl AsyncRecv + AsyncSend + Unpin),
425    state: &mut ChannelState,
426    packet_buf: &mut PacketBuffer,
427    framebuffer: &mut Box<dyn FramebufferControl>,
428    dirt_send: &Option<mesh::Sender<Vec<DirtyRect>>>,
429) -> Result<(), Error> {
430    loop {
431        match state {
432            ChannelState::ReadVersion => {
433                let version =
434                    if let Request::Version(version) = packet_buf.recv_packet(channel).await? {
435                        version.into()
436                    } else {
437                        return Err(Error::UnexpectedPacketOrder);
438                    };
439                *state = ChannelState::WriteVersion { version };
440            }
441            ChannelState::WriteVersion { version } => {
442                let server_version = Version {
443                    major: protocol::VERSION_MAJOR,
444                    minor: protocol::VERSION_MINOR_BLUE,
445                };
446                let is_accepted = if version.major == server_version.major {
447                    protocol::ACCEPTED_WITH_VERSION_EXCHANGE
448                } else {
449                    0
450                };
451                VideoChannel::send_packet(
452                    channel,
453                    protocol::MESSAGE_VERSION_RESPONSE,
454                    &protocol::VersionResponseMessage {
455                        version: (*version).into(),
456                        is_accepted,
457                        max_video_outputs: 1,
458                    },
459                )
460                .await?;
461                if is_accepted != 0 {
462                    tracelimit::info_ratelimited!(?version, "video negotiation succeeded");
463                    *state = ChannelState::Active {
464                        version: *version,
465                        substate: ActiveState::ReadRequest,
466                    };
467                } else {
468                    tracelimit::warn_ratelimited!(?version, "video negotiation failed");
469                    *state = ChannelState::ReadVersion;
470                }
471            }
472            ChannelState::Active {
473                version: _,
474                substate,
475            } => match *substate {
476                ActiveState::ReadRequest => {
477                    let packet = packet_buf.recv_packet(channel).await?;
478                    match packet {
479                        Request::VramLocation {
480                            user_context,
481                            address,
482                        } => {
483                            framebuffer.unmap().await;
484                            if let Some(address) = address {
485                                framebuffer.map(address).await;
486                            }
487                            *substate = ActiveState::SendVramAck { user_context };
488                        }
489                        Request::SituationUpdate {
490                            user_context,
491                            situation,
492                        } => {
493                            framebuffer
494                                .set_format(FramebufferFormat {
495                                    width: u32::from(situation.width_pixels) as usize,
496                                    height: u32::from(situation.height_pixels) as usize,
497                                    bytes_per_line: u32::from(situation.pitch_bytes) as usize,
498                                    offset: u32::from(situation.primary_surface_vram_offset)
499                                        as usize,
500                                })
501                                .await;
502                            *substate = ActiveState::SendSituationUpdateAck { user_context };
503                        }
504                        Request::PointerPosition { is_visible, x, y } => {
505                            let _ = (is_visible, x, y);
506                        }
507                        Request::PointerShape => {}
508                        Request::Dirt(rects) => {
509                            if let Some(send) = dirt_send {
510                                let dirty: Vec<DirtyRect> = rects
511                                    .iter()
512                                    .map(|r| DirtyRect {
513                                        left: r.left.into(),
514                                        top: r.top.into(),
515                                        right: r.right.into(),
516                                        bottom: r.bottom.into(),
517                                    })
518                                    .collect();
519                                send.send(dirty);
520                            }
521                        }
522                        Request::BiosInfo => {
523                            *substate = ActiveState::SendBiosInfo;
524                        }
525                        Request::SupportedResolutions { maximum_count } => {
526                            *substate = ActiveState::SendSupportedResolutions { maximum_count };
527                        }
528                        Request::Capability => {
529                            *substate = ActiveState::SendCapability;
530                        }
531                        Request::Version(_) => return Err(Error::UnexpectedPacketOrder),
532                    }
533                }
534                ActiveState::SendVramAck { user_context } => {
535                    VideoChannel::send_packet(
536                        channel,
537                        protocol::MESSAGE_VRAM_LOCATION_ACK,
538                        &protocol::VramLocationAckMessage {
539                            user_context: user_context.into(),
540                        },
541                    )
542                    .await?;
543                    *substate = ActiveState::ReadRequest;
544                }
545                ActiveState::SendSituationUpdateAck { user_context } => {
546                    VideoChannel::send_packet(
547                        channel,
548                        protocol::MESSAGE_SITUATION_UPDATE_ACK,
549                        &protocol::SituationUpdateAckMessage {
550                            user_context: user_context.into(),
551                        },
552                    )
553                    .await?;
554                    *substate = ActiveState::ReadRequest;
555                }
556                ActiveState::SendBiosInfo => {
557                    VideoChannel::send_packet(
558                        channel,
559                        protocol::MESSAGE_BIOS_INFO_RESPONSE,
560                        &protocol::BiosInfoResponseMessage {
561                            stop_device_supported: 1.into(),
562                            reserved: [0; 12],
563                        },
564                    )
565                    .await?;
566                    *substate = ActiveState::ReadRequest;
567                }
568                ActiveState::SendSupportedResolutions { maximum_count } => {
569                    if maximum_count < protocol::MAXIMUM_RESOLUTIONS_COUNT {
570                        VideoChannel::send_packet(
571                            channel,
572                            protocol::MESSAGE_SUPPORTED_RESOLUTIONS_RESPONSE,
573                            &protocol::SupportedResolutionsResponseMessage {
574                                edid_block: protocol::EDID_BLOCK,
575                                resolution_count: 0,
576                                default_resolution_index: 0,
577                                is_standard: 0,
578                            },
579                        )
580                        .await?;
581                    } else {
582                        const RESOLUTIONS: &[(u16, u16)] = &[(1024, 768), (1280, 1024)];
583
584                        let mut packet = Vec::new();
585                        packet.extend_from_slice(
586                            protocol::SupportedResolutionsResponseMessage {
587                                edid_block: protocol::EDID_BLOCK,
588                                resolution_count: RESOLUTIONS.len().try_into().unwrap(),
589                                default_resolution_index: 0,
590                                is_standard: 0,
591                            }
592                            .as_bytes(),
593                        );
594                        for r in RESOLUTIONS {
595                            packet.extend_from_slice(
596                                protocol::ScreenInfo {
597                                    width: r.0.into(),
598                                    height: r.1.into(),
599                                }
600                                .as_bytes(),
601                            );
602                        }
603                        VideoChannel::send_packet(
604                            channel,
605                            protocol::MESSAGE_SUPPORTED_RESOLUTIONS_RESPONSE,
606                            packet.as_slice(),
607                        )
608                        .await?;
609                    }
610                    *substate = ActiveState::ReadRequest;
611                }
612                ActiveState::SendCapability => {
613                    VideoChannel::send_packet(
614                        channel,
615                        protocol::MESSAGE_CAPABILITY_RESPONSE,
616                        &protocol::CapabilityResponseMessage {
617                            lock_on_disconnect: 0.into(),
618                            reserved: [0.into(); 15],
619                        },
620                    )
621                    .await?;
622                    *substate = ActiveState::ReadRequest;
623                }
624            },
625        }
626    }
627}
628
629#[cfg(test)]
630mod tests {
631    use super::*;
632    use framebuffer::FRAMEBUFFER_SIZE;
633    use guestmem::MappableGuestMemory;
634    use guestmem::MappedMemoryRegion;
635    use guestmem::MemoryMapper;
636    use pal_async::DefaultDriver;
637    use pal_async::async_test;
638    use pal_async::task::Spawn;
639    use pal_async::task::Task;
640    use sparse_mmap::AsMappableRef;
641    use sparse_mmap::SparseMapping;
642    use sparse_mmap::alloc_shared_memory;
643    use std::io::ErrorKind;
644    use std::sync::Arc;
645    use vmbus_async::pipe::connected_message_pipes;
646    use vmbus_ring::RingMem;
647
648    struct TestGuestMemory;
649
650    impl MappableGuestMemory for TestGuestMemory {
651        fn map_to_guest(&mut self, _gpa: u64, _writable: bool) -> std::io::Result<()> {
652            Ok(())
653        }
654
655        fn unmap_from_guest(&mut self) {}
656    }
657
658    struct TestMappedRegion(SparseMapping);
659
660    impl MappedMemoryRegion for TestMappedRegion {
661        fn map(
662            &self,
663            offset: usize,
664            section: &dyn AsMappableRef,
665            file_offset: u64,
666            len: usize,
667            writable: bool,
668        ) -> std::io::Result<()> {
669            self.0.map_file(offset, len, section, file_offset, writable)
670        }
671
672        fn unmap(&self, offset: usize, len: usize) -> std::io::Result<()> {
673            self.0.unmap(offset, len)
674        }
675    }
676
677    struct TestMemoryMapper;
678
679    impl MemoryMapper for TestMemoryMapper {
680        fn new_region(
681            &self,
682            len: usize,
683            _debug_name: String,
684        ) -> std::io::Result<(Box<dyn MappableGuestMemory>, Arc<dyn MappedMemoryRegion>)> {
685            Ok((
686                Box::new(TestGuestMemory),
687                Arc::new(TestMappedRegion(SparseMapping::new(len)?)),
688            ))
689        }
690    }
691
692    fn framebuffer_fixture() -> (
693        framebuffer::FramebufferDevice,
694        Box<dyn FramebufferControl>,
695        framebuffer::View,
696    ) {
697        let vram = alloc_shared_memory(FRAMEBUFFER_SIZE, "video-test").unwrap();
698        let (fb, access) = framebuffer::framebuffer(vram, FRAMEBUFFER_SIZE, 0).unwrap();
699        let device =
700            framebuffer::FramebufferDevice::new(Box::new(TestMemoryMapper), fb, None).unwrap();
701        let control: Box<dyn FramebufferControl> = Box::new(device.control());
702        let view = access.view().unwrap();
703        (device, control, view)
704    }
705
706    async fn send_packet<T: IntoBytes + Immutable + KnownLayout>(
707        writer: &mut (impl AsyncSend + Unpin),
708        typ: u32,
709        packet: &T,
710    ) {
711        let header = protocol::MessageHeader {
712            typ: typ.into(),
713            size: (size_of_val(packet) as u32).into(),
714        };
715        writer
716            .send_vectored(&[
717                IoSlice::new(header.as_bytes()),
718                IoSlice::new(packet.as_bytes()),
719            ])
720            .await
721            .unwrap();
722    }
723
724    async fn send_dirt_packet(
725        writer: &mut (impl AsyncSend + Unpin),
726        rects: &[protocol::Rectangle],
727    ) {
728        let header = protocol::MessageHeader {
729            typ: protocol::MESSAGE_DIRT.into(),
730            size: ((size_of::<protocol::DirtMessage>() + size_of_val(rects)) as u32).into(),
731        };
732        let dirt = protocol::DirtMessage {
733            video_output: 0,
734            dirt_count: rects.len().try_into().unwrap(),
735        };
736        writer
737            .send_vectored(&[
738                IoSlice::new(header.as_bytes()),
739                IoSlice::new(dirt.as_bytes()),
740                IoSlice::new(rects.as_bytes()),
741            ])
742            .await
743            .unwrap();
744    }
745
746    async fn recv_bytes(reader: &mut (impl AsyncRecv + Unpin + Send)) -> Vec<u8> {
747        let mut packet = vec![0; protocol::MAX_VSP_TO_VSC_MESSAGE_SIZE.max(512)];
748        let n = reader.recv(&mut packet).await.unwrap();
749        packet.truncate(n);
750        packet
751    }
752
753    fn parse_header(packet: &[u8]) -> (protocol::MessageHeader, &[u8]) {
754        let (header, rest) = Ref::<_, protocol::MessageHeader>::from_prefix(packet).unwrap();
755        (*header, rest)
756    }
757
758    fn start_worker<T: RingMem + 'static + Unpin + Send + Sync>(
759        driver: &DefaultDriver,
760        mut control: Box<dyn FramebufferControl>,
761        dirt_send: Option<mesh::Sender<Vec<DirtyRect>>>,
762        mut channel: MessagePipe<T>,
763    ) -> Task<Result<(), Error>> {
764        driver.spawn("video worker", async move {
765            let mut state = ChannelState::ReadVersion;
766            let mut packet_buf = PacketBuffer::new();
767            process_channel(
768                &mut channel,
769                &mut state,
770                &mut packet_buf,
771                &mut control,
772                &dirt_send,
773            )
774            .await
775            .or_else(|e| match e {
776                Error::Io(err) if err.kind() == ErrorKind::ConnectionReset => Ok(()),
777                _ => Err(e),
778            })
779        })
780    }
781
782    #[async_test]
783    async fn test_channel_updates_framebuffer_and_forwards_dirt(driver: DefaultDriver) {
784        let (host, mut guest) = connected_message_pipes(16384);
785        let (_device, control, mut view) = framebuffer_fixture();
786        let (dirt_send, mut dirt_recv) = mesh::channel();
787        let worker = start_worker(&driver, control, Some(dirt_send), host);
788
789        let version = protocol::Version::new(protocol::VERSION_MAJOR, protocol::VERSION_MINOR_BLUE);
790        send_packet(
791            &mut guest,
792            protocol::MESSAGE_VERSION_REQUEST,
793            &protocol::VersionRequestMessage { version },
794        )
795        .await;
796
797        let packet = recv_bytes(&mut guest).await;
798        let (header, rest) = parse_header(&packet);
799        assert_eq!(header.typ.to_ne(), protocol::MESSAGE_VERSION_RESPONSE);
800        let response = protocol::VersionResponseMessage::ref_from_prefix(rest)
801            .unwrap()
802            .0;
803        assert_eq!(response.version.major(), protocol::VERSION_MAJOR);
804        assert_eq!(response.version.minor(), protocol::VERSION_MINOR_BLUE);
805        assert_eq!(
806            response.is_accepted,
807            protocol::ACCEPTED_WITH_VERSION_EXCHANGE
808        );
809        assert_eq!(response.max_video_outputs, 1);
810
811        send_packet(
812            &mut guest,
813            protocol::MESSAGE_VRAM_LOCATION,
814            &protocol::VramLocationMessage {
815                user_context: 0x1234u64.into(),
816                is_vram_gpa_address_specified: 1,
817                vram_gpa_address: 0x4000u64.into(),
818            },
819        )
820        .await;
821
822        let packet = recv_bytes(&mut guest).await;
823        let (header, rest) = parse_header(&packet);
824        assert_eq!(header.typ.to_ne(), protocol::MESSAGE_VRAM_LOCATION_ACK);
825        let ack = protocol::VramLocationAckMessage::ref_from_prefix(rest)
826            .unwrap()
827            .0;
828        assert_eq!(ack.user_context.to_ne(), 0x1234);
829
830        send_packet(
831            &mut guest,
832            protocol::MESSAGE_SITUATION_UPDATE,
833            &protocol::SituationUpdateMessage {
834                user_context: 0x5678u64.into(),
835                video_output_count: 1,
836                video_output: protocol::VideoOutputSituation {
837                    active: 1,
838                    primary_surface_vram_offset: 0.into(),
839                    depth_bits: 32,
840                    width_pixels: 800u32.into(),
841                    height_pixels: 600u32.into(),
842                    pitch_bytes: (800u32 * 4).into(),
843                },
844            },
845        )
846        .await;
847
848        let packet = recv_bytes(&mut guest).await;
849        let (header, rest) = parse_header(&packet);
850        assert_eq!(header.typ.to_ne(), protocol::MESSAGE_SITUATION_UPDATE_ACK);
851        let ack = protocol::SituationUpdateAckMessage::ref_from_prefix(rest)
852            .unwrap()
853            .0;
854        assert_eq!(ack.user_context.to_ne(), 0x5678);
855        assert_eq!(view.resolution(), (800, 600));
856
857        let rects = [
858            protocol::Rectangle {
859                left: 1.into(),
860                top: 2.into(),
861                right: 30.into(),
862                bottom: 40.into(),
863            },
864            protocol::Rectangle {
865                left: 100.into(),
866                top: 120.into(),
867                right: 140.into(),
868                bottom: 180.into(),
869            },
870        ];
871        send_dirt_packet(&mut guest, &rects).await;
872
873        let dirt = dirt_recv.recv().await.unwrap();
874        assert_eq!(dirt.len(), 2);
875        assert_eq!(dirt[0].left, 1);
876        assert_eq!(dirt[0].top, 2);
877        assert_eq!(dirt[0].right, 30);
878        assert_eq!(dirt[0].bottom, 40);
879        assert_eq!(dirt[1].left, 100);
880        assert_eq!(dirt[1].top, 120);
881        assert_eq!(dirt[1].right, 140);
882        assert_eq!(dirt[1].bottom, 180);
883
884        drop(guest);
885        worker.await.unwrap();
886    }
887
888    #[async_test]
889    async fn test_channel_reports_bios_resolutions_and_capability(driver: DefaultDriver) {
890        let (host, mut guest) = connected_message_pipes(16384);
891        let (_device, control, _view) = framebuffer_fixture();
892        let worker = start_worker(&driver, control, None, host);
893
894        send_packet(
895            &mut guest,
896            protocol::MESSAGE_VERSION_REQUEST,
897            &protocol::VersionRequestMessage {
898                version: protocol::Version::new(
899                    protocol::VERSION_MAJOR,
900                    protocol::VERSION_MINOR_BLUE,
901                ),
902            },
903        )
904        .await;
905        let _ = recv_bytes(&mut guest).await;
906
907        send_packet(
908            &mut guest,
909            protocol::MESSAGE_BIOS_INFO_REQUEST,
910            &protocol::BiosInfoRequestMessage {},
911        )
912        .await;
913        let packet = recv_bytes(&mut guest).await;
914        let (header, rest) = parse_header(&packet);
915        assert_eq!(header.typ.to_ne(), protocol::MESSAGE_BIOS_INFO_RESPONSE);
916        let bios = protocol::BiosInfoResponseMessage::ref_from_prefix(rest)
917            .unwrap()
918            .0;
919        assert_eq!(bios.stop_device_supported.to_ne(), 1);
920
921        send_packet(
922            &mut guest,
923            protocol::MESSAGE_SUPPORTED_RESOLUTIONS_REQUEST,
924            &protocol::SupportedResolutionsRequestMessage {
925                maximum_resolution_count: protocol::MAXIMUM_RESOLUTIONS_COUNT,
926            },
927        )
928        .await;
929        let packet = recv_bytes(&mut guest).await;
930        let (header, rest) = parse_header(&packet);
931        assert_eq!(
932            header.typ.to_ne(),
933            protocol::MESSAGE_SUPPORTED_RESOLUTIONS_RESPONSE
934        );
935        let (response, rest) =
936            Ref::<_, protocol::SupportedResolutionsResponseMessage>::from_prefix(rest).unwrap();
937        assert_eq!(response.resolution_count as usize, 2);
938        let (screens, tail) = <[protocol::ScreenInfo]>::ref_from_prefix_with_elems(
939            rest,
940            response.resolution_count as usize,
941        )
942        .unwrap();
943        assert!(tail.is_empty());
944        assert_eq!(screens[0].width.to_ne(), 1024);
945        assert_eq!(screens[0].height.to_ne(), 768);
946        assert_eq!(screens[1].width.to_ne(), 1280);
947        assert_eq!(screens[1].height.to_ne(), 1024);
948
949        send_packet(
950            &mut guest,
951            protocol::MESSAGE_CAPABILITY_REQUEST,
952            &protocol::CapabilityRequestMessage {},
953        )
954        .await;
955        let packet = recv_bytes(&mut guest).await;
956        let (header, rest) = parse_header(&packet);
957        assert_eq!(header.typ.to_ne(), protocol::MESSAGE_CAPABILITY_RESPONSE);
958        let capability = protocol::CapabilityResponseMessage::ref_from_prefix(rest)
959            .unwrap()
960            .0;
961        assert_eq!(capability.lock_on_disconnect.to_ne(), 0);
962
963        drop(guest);
964        worker.await.unwrap();
965    }
966
967    #[async_test]
968    async fn test_channel_rejects_out_of_order_request(driver: DefaultDriver) {
969        let (host, mut guest) = connected_message_pipes(16384);
970        let (_device, control, _view) = framebuffer_fixture();
971        let worker = start_worker(&driver, control, None, host);
972
973        send_packet(
974            &mut guest,
975            protocol::MESSAGE_BIOS_INFO_REQUEST,
976            &protocol::BiosInfoRequestMessage {},
977        )
978        .await;
979
980        let err = worker.await.unwrap_err();
981        assert!(matches!(err, Error::UnexpectedPacketOrder));
982    }
983}