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::FramebufferControl;
16use video_core::FramebufferFormat;
17use vmbus_async::async_dgram::AsyncRecv;
18use vmbus_async::async_dgram::AsyncRecvExt;
19use vmbus_async::async_dgram::AsyncSend;
20use vmbus_async::async_dgram::AsyncSendExt;
21use vmbus_async::pipe::MessagePipe;
22use vmbus_channel::RawAsyncChannel;
23use vmbus_channel::bus::ChannelType;
24use vmbus_channel::bus::OfferParams;
25use vmbus_channel::channel::ChannelOpenError;
26use vmbus_channel::gpadl_ring::GpadlRingMem;
27use vmbus_channel::simple::SaveRestoreSimpleVmbusDevice;
28use vmbus_channel::simple::SimpleVmbusDevice;
29use vmcore::save_restore::SavedStateRoot;
30use zerocopy::FromBytes;
31use zerocopy::Immutable;
32use zerocopy::IntoBytes;
33use zerocopy::KnownLayout;
34use zerocopy::Ref;
35
36#[derive(Debug, Error)]
37enum Error {
38    #[error("out of order packet")]
39    UnexpectedPacketOrder,
40    #[error("memory access error")]
41    Access(#[from] AccessError),
42    #[error("unknown message type: {0:#x}")]
43    UnknownMessageType(u32),
44    #[error("invalid packet")]
45    InvalidPacket,
46    #[error("channel i/o error")]
47    Io(#[source] std::io::Error),
48    #[error("failed to accept vmbus channel")]
49    Accept(#[from] vmbus_channel::offer::Error),
50}
51
52#[derive(Debug)]
53enum Request {
54    Version(protocol::Version),
55    VramLocation {
56        user_context: u64,
57        address: Option<u64>,
58    },
59    SituationUpdate {
60        user_context: u64,
61        situation: protocol::VideoOutputSituation,
62    },
63    PointerPosition {
64        is_visible: bool,
65        x: i32,
66        y: i32,
67    },
68    PointerShape,
69    Dirt(Vec<protocol::Rectangle>),
70    BiosInfo,
71    SupportedResolutions {
72        maximum_count: u8,
73    },
74    Capability,
75}
76
77fn parse_packet(buf: &[u8]) -> Result<Request, Error> {
78    let (header, buf) =
79        Ref::<_, protocol::MessageHeader>::from_prefix(buf).map_err(|_| Error::InvalidPacket)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
80    let request = match header.typ.to_ne() {
81        protocol::MESSAGE_VERSION_REQUEST => {
82            let message = protocol::VersionRequestMessage::ref_from_prefix(buf)
83                .map_err(|_| Error::InvalidPacket)?
84                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
85            Request::Version(message.version)
86        }
87        protocol::MESSAGE_VRAM_LOCATION => {
88            let message = protocol::VramLocationMessage::ref_from_prefix(buf)
89                .map_err(|_| Error::InvalidPacket)?
90                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
91            let address = if message.is_vram_gpa_address_specified != 0 {
92                Some(message.vram_gpa_address.into())
93            } else {
94                None
95            };
96            Request::VramLocation {
97                user_context: message.user_context.into(),
98                address,
99            }
100        }
101        protocol::MESSAGE_SITUATION_UPDATE => {
102            let message = protocol::SituationUpdateMessage::ref_from_prefix(buf)
103                .map_err(|_| Error::InvalidPacket)?
104                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
105            Request::SituationUpdate {
106                user_context: message.user_context.into(),
107                situation: message.video_output,
108            }
109        }
110        protocol::MESSAGE_POINTER_POSITION => {
111            let message = protocol::PointerPositionMessage::ref_from_prefix(buf)
112                .map_err(|_| Error::InvalidPacket)?
113                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
114            Request::PointerPosition {
115                is_visible: message.is_visible != 0,
116                x: message.image_x.into(),
117                y: message.image_y.into(),
118            }
119        }
120        protocol::MESSAGE_POINTER_SHAPE => {
121            //let message = protocol::PointerShapeMessage::from_bytes(buf).map_err(|_| Error::InvalidPacket)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
122            Request::PointerShape
123        }
124        protocol::MESSAGE_DIRT => {
125            let (message, buf) = Ref::<_, protocol::DirtMessage>::from_prefix(buf)
126                .map_err(|_| Error::InvalidPacket)?; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
127            Request::Dirt(
128                <[protocol::Rectangle]>::ref_from_prefix_with_elems(
129                    buf,
130                    message.dirt_count as usize,
131                )
132                .map_err(|_| Error::InvalidPacket)? // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
133                .0
134                .into(),
135            )
136        }
137        protocol::MESSAGE_BIOS_INFO_REQUEST => Request::BiosInfo,
138        protocol::MESSAGE_SUPPORTED_RESOLUTIONS_REQUEST => {
139            let message = protocol::SupportedResolutionsRequestMessage::ref_from_prefix(buf)
140                .map_err(|_| Error::InvalidPacket)?
141                .0; // TODO: zerocopy: map_err (https://github.com/microsoft/openvmm/issues/759)
142            Request::SupportedResolutions {
143                maximum_count: message.maximum_resolution_count,
144            }
145        }
146        protocol::MESSAGE_CAPABILITY_REQUEST => Request::Capability,
147        typ => return Err(Error::UnknownMessageType(typ)),
148    };
149    Ok(request)
150}
151
152/// Vmbus synthetic video device.
153pub struct Video {
154    control: Box<dyn FramebufferControl>,
155}
156
157impl Video {
158    /// Creates a new video device.
159    pub fn new(control: Box<dyn FramebufferControl>) -> anyhow::Result<Self> {
160        Ok(Self { control })
161    }
162}
163
164/// The video device saved state.
165#[derive(Protobuf, SavedStateRoot)]
166#[mesh(package = "ui.synthvid")]
167pub struct SavedState(ChannelState);
168
169/// The video task.
170pub struct VideoChannel {
171    channel: MessagePipe<GpadlRingMem>,
172    state: ChannelState,
173    packet_buf: PacketBuffer,
174}
175
176#[derive(Debug, Copy, Clone, Protobuf)]
177#[mesh(package = "ui.synthvid")]
178struct Version {
179    #[mesh(1)]
180    major: u16,
181    #[mesh(2)]
182    minor: u16,
183}
184
185impl From<protocol::Version> for Version {
186    fn from(version: protocol::Version) -> Self {
187        Self {
188            major: version.major(),
189            minor: version.minor(),
190        }
191    }
192}
193
194impl From<Version> for protocol::Version {
195    fn from(version: Version) -> Self {
196        Self::new(version.major, version.minor)
197    }
198}
199
200#[derive(Debug, Clone, Protobuf)]
201#[mesh(package = "ui.synthvid")]
202enum ChannelState {
203    #[mesh(1)]
204    ReadVersion,
205    #[mesh(2)]
206    WriteVersion {
207        #[mesh(1)]
208        version: Version,
209    },
210    #[mesh(3)]
211    Active {
212        #[mesh(1)]
213        version: Version,
214        #[mesh(2)]
215        substate: ActiveState,
216    },
217}
218
219impl Default for ChannelState {
220    fn default() -> Self {
221        Self::ReadVersion
222    }
223}
224
225#[derive(Debug, Clone, Protobuf)]
226#[mesh(package = "ui.synthvid")]
227enum ActiveState {
228    #[mesh(1)]
229    ReadRequest,
230    #[mesh(2)]
231    SendVramAck {
232        #[mesh(1)]
233        user_context: u64,
234    },
235    #[mesh(3)]
236    SendSituationUpdateAck {
237        #[mesh(1)]
238        user_context: u64,
239    },
240    #[mesh(4)]
241    SendBiosInfo,
242    #[mesh(5)]
243    SendSupportedResolutions {
244        #[mesh(1)]
245        maximum_count: u8,
246    },
247    #[mesh(6)]
248    SendCapability,
249}
250
251struct PacketBuffer {
252    buf: Vec<u8>,
253}
254
255impl PacketBuffer {
256    fn new() -> Self {
257        Self {
258            buf: vec![0; protocol::MAX_VMBUS_PACKET_SIZE],
259        }
260    }
261
262    async fn recv_packet(
263        &mut self,
264        reader: &mut (impl AsyncRecv + Unpin),
265    ) -> Result<Request, Error> {
266        let n = match reader.recv(&mut self.buf).await {
267            Ok(n) => n,
268            Err(e) => return Err(Error::Io(e)),
269        };
270        let buf = &self.buf[..n];
271        parse_packet(buf)
272    }
273}
274
275#[async_trait]
276impl SimpleVmbusDevice for Video {
277    type Runner = VideoChannel;
278    type SavedState = SavedState;
279
280    fn offer(&self) -> OfferParams {
281        OfferParams {
282            interface_name: "video".to_owned(),
283            interface_id: Guid {
284                data1: 0xda0a7802,
285                data2: 0xe377,
286                data3: 0x4aac,
287                data4: [0x8e, 0x77, 0x5, 0x58, 0xeb, 0x10, 0x73, 0xf8],
288            },
289            instance_id: Guid {
290                data1: 0x5620e0c7,
291                data2: 0x8062,
292                data3: 0x4dce,
293                data4: [0xae, 0xb7, 0x52, 0xc, 0x7e, 0xf7, 0x61, 0x71],
294            },
295            mmio_megabytes: 8,
296            channel_type: ChannelType::Device { pipe_packets: true },
297            ..Default::default()
298        }
299    }
300
301    fn inspect(&mut self, req: inspect::Request<'_>, task: Option<&mut VideoChannel>) {
302        let mut resp = req.respond();
303        if let Some(this) = task {
304            let (version, state) = match &this.state {
305                ChannelState::ReadVersion => (None, "read_version"),
306                ChannelState::WriteVersion { version } => (Some(*version), "write_version"),
307                ChannelState::Active { version, substate } => (
308                    Some(*version),
309                    match substate {
310                        ActiveState::ReadRequest => "read_request",
311                        ActiveState::SendVramAck { .. } => "send_vram_ack",
312                        ActiveState::SendSituationUpdateAck { .. } => "send_situation_update_ack",
313                        ActiveState::SendBiosInfo => "send_bios_info",
314                        ActiveState::SendSupportedResolutions { .. } => {
315                            "send_supported_resolutions"
316                        }
317                        ActiveState::SendCapability => "send_capability",
318                    },
319                ),
320            };
321            resp.field("state", state)
322                .field(
323                    "version",
324                    version.map(|v| format!("{}.{}", v.major, v.minor)),
325                )
326                .field_mut("channel", &mut this.channel);
327        }
328    }
329
330    fn open(
331        &mut self,
332        channel: RawAsyncChannel<GpadlRingMem>,
333        _guest_memory: guestmem::GuestMemory,
334    ) -> Result<Self::Runner, ChannelOpenError> {
335        let pipe = MessagePipe::new(channel)?;
336        Ok(VideoChannel::new(pipe, ChannelState::default()))
337    }
338
339    async fn run(
340        &mut self,
341        stop: &mut StopTask<'_>,
342        channel: &mut VideoChannel,
343    ) -> Result<(), task_control::Cancelled> {
344        stop.until_stopped(async {
345            match channel.process(&mut self.control).await {
346                Ok(()) => {}
347                Err(err) => tracing::error!(error = &err as &dyn std::error::Error, "video error"),
348            }
349        })
350        .await
351    }
352
353    fn supports_save_restore(
354        &mut self,
355    ) -> Option<
356        &mut dyn SaveRestoreSimpleVmbusDevice<SavedState = Self::SavedState, Runner = Self::Runner>,
357    > {
358        Some(self)
359    }
360}
361
362impl SaveRestoreSimpleVmbusDevice for Video {
363    fn save_open(&mut self, runner: &Self::Runner) -> Self::SavedState {
364        SavedState(runner.state.clone())
365    }
366
367    fn restore_open(
368        &mut self,
369        state: Self::SavedState,
370        channel: RawAsyncChannel<GpadlRingMem>,
371    ) -> Result<Self::Runner, ChannelOpenError> {
372        let pipe = MessagePipe::new(channel)?;
373        Ok(VideoChannel::new(pipe, state.0))
374    }
375}
376
377impl VideoChannel {
378    fn new(channel: MessagePipe<GpadlRingMem>, state: ChannelState) -> Self {
379        Self {
380            channel,
381            state,
382            packet_buf: PacketBuffer::new(),
383        }
384    }
385
386    async fn send_packet<T: IntoBytes + ?Sized + Immutable + KnownLayout>(
387        writer: &mut (impl AsyncSend + Unpin),
388        typ: u32,
389        packet: &T,
390    ) -> Result<(), Error> {
391        let header = protocol::MessageHeader {
392            typ: typ.into(),
393            size: (size_of_val(packet) as u32).into(),
394        };
395        writer
396            .send_vectored(&[
397                IoSlice::new(header.as_bytes()),
398                IoSlice::new(packet.as_bytes()),
399            ])
400            .await
401            .map_err(Error::Io)?;
402
403        Ok(())
404    }
405
406    async fn process(
407        &mut self,
408        framebuffer: &mut Box<dyn FramebufferControl>,
409    ) -> Result<(), Error> {
410        let mut channel = &mut self.channel;
411        loop {
412            match &mut self.state {
413                ChannelState::ReadVersion => {
414                    let version = if let Request::Version(version) =
415                        self.packet_buf.recv_packet(&mut channel).await?
416                    {
417                        version.into()
418                    } else {
419                        return Err(Error::UnexpectedPacketOrder);
420                    };
421                    self.state = ChannelState::WriteVersion { version };
422                }
423                ChannelState::WriteVersion { version } => {
424                    let server_version = Version {
425                        major: protocol::VERSION_MAJOR,
426                        minor: protocol::VERSION_MINOR_BLUE,
427                    };
428                    let is_accepted = if version.major == server_version.major {
429                        protocol::ACCEPTED_WITH_VERSION_EXCHANGE
430                    } else {
431                        0
432                    };
433                    Self::send_packet(
434                        &mut channel,
435                        protocol::MESSAGE_VERSION_RESPONSE,
436                        &protocol::VersionResponseMessage {
437                            version: (*version).into(),
438                            is_accepted,
439                            max_video_outputs: 1,
440                        },
441                    )
442                    .await?;
443                    if is_accepted != 0 {
444                        tracelimit::info_ratelimited!(?version, "video negotiation succeeded");
445                        self.state = ChannelState::Active {
446                            version: *version,
447                            substate: ActiveState::ReadRequest,
448                        };
449                    } else {
450                        tracelimit::warn_ratelimited!(?version, "video negotiation failed");
451                        self.state = ChannelState::ReadVersion;
452                    }
453                }
454                ChannelState::Active {
455                    version: _,
456                    substate,
457                } => {
458                    match *substate {
459                        ActiveState::ReadRequest => {
460                            let packet = self.packet_buf.recv_packet(&mut channel).await?;
461                            match packet {
462                                Request::VramLocation {
463                                    user_context,
464                                    address,
465                                } => {
466                                    framebuffer.unmap().await;
467                                    if let Some(address) = address {
468                                        // N.B. The mapping is preserved until explicitly torn
469                                        //      down--UEFI may open the channel, establish the
470                                        //      mapping, close the channel, and expect the guest
471                                        //      to continue to render to the framebuffer.
472                                        framebuffer.map(address).await;
473                                    }
474                                    *substate = ActiveState::SendVramAck { user_context };
475                                }
476                                Request::SituationUpdate {
477                                    user_context,
478                                    situation,
479                                } => {
480                                    framebuffer
481                                        .set_format(FramebufferFormat {
482                                            width: u32::from(situation.width_pixels) as usize,
483                                            height: u32::from(situation.height_pixels) as usize,
484                                            bytes_per_line: u32::from(situation.pitch_bytes)
485                                                as usize,
486                                            offset: u32::from(situation.primary_surface_vram_offset)
487                                                as usize,
488                                        })
489                                        .await;
490                                    *substate =
491                                        ActiveState::SendSituationUpdateAck { user_context };
492                                }
493                                Request::PointerPosition { is_visible, x, y } => {
494                                    let _ = (is_visible, x, y);
495                                }
496                                Request::PointerShape => {}
497                                Request::Dirt(_rects) => {
498                                    // TODO: Support dirt requests
499                                }
500                                Request::BiosInfo => {
501                                    *substate = ActiveState::SendBiosInfo;
502                                }
503                                Request::SupportedResolutions { maximum_count } => {
504                                    *substate =
505                                        ActiveState::SendSupportedResolutions { maximum_count };
506                                }
507                                Request::Capability => {
508                                    *substate = ActiveState::SendCapability;
509                                }
510                                Request::Version(_) => return Err(Error::UnexpectedPacketOrder),
511                            }
512                        }
513                        ActiveState::SendVramAck { user_context } => {
514                            Self::send_packet(
515                                &mut channel,
516                                protocol::MESSAGE_VRAM_LOCATION_ACK,
517                                &protocol::VramLocationAckMessage {
518                                    user_context: user_context.into(),
519                                },
520                            )
521                            .await?;
522                            *substate = ActiveState::ReadRequest;
523                        }
524                        ActiveState::SendSituationUpdateAck { user_context } => {
525                            Self::send_packet(
526                                &mut channel,
527                                protocol::MESSAGE_SITUATION_UPDATE_ACK,
528                                &protocol::SituationUpdateAckMessage {
529                                    user_context: user_context.into(),
530                                },
531                            )
532                            .await?;
533                            *substate = ActiveState::ReadRequest;
534                        }
535                        ActiveState::SendBiosInfo => {
536                            Self::send_packet(
537                                &mut channel,
538                                protocol::MESSAGE_BIOS_INFO_RESPONSE,
539                                &protocol::BiosInfoResponseMessage {
540                                    stop_device_supported: 1.into(),
541                                    reserved: [0; 12],
542                                },
543                            )
544                            .await?;
545                            *substate = ActiveState::ReadRequest;
546                        }
547                        ActiveState::SendSupportedResolutions { maximum_count } => {
548                            if maximum_count < protocol::MAXIMUM_RESOLUTIONS_COUNT {
549                                Self::send_packet(
550                                    &mut channel,
551                                    protocol::MESSAGE_SUPPORTED_RESOLUTIONS_RESPONSE,
552                                    &protocol::SupportedResolutionsResponseMessage {
553                                        edid_block: protocol::EDID_BLOCK,
554                                        resolution_count: 0,
555                                        default_resolution_index: 0,
556                                        is_standard: 0,
557                                    },
558                                )
559                                .await?;
560                            } else {
561                                const RESOLUTIONS: &[(u16, u16)] = &[(1024, 768), (1280, 1024)];
562
563                                let mut packet = Vec::new();
564                                packet.extend_from_slice(
565                                    protocol::SupportedResolutionsResponseMessage {
566                                        edid_block: protocol::EDID_BLOCK,
567                                        resolution_count: RESOLUTIONS.len().try_into().unwrap(),
568                                        default_resolution_index: 0,
569                                        is_standard: 0,
570                                    }
571                                    .as_bytes(),
572                                );
573                                for r in RESOLUTIONS {
574                                    packet.extend_from_slice(
575                                        protocol::ScreenInfo {
576                                            width: r.0.into(),
577                                            height: r.1.into(),
578                                        }
579                                        .as_bytes(),
580                                    );
581                                }
582                                Self::send_packet(
583                                    &mut channel,
584                                    protocol::MESSAGE_SUPPORTED_RESOLUTIONS_RESPONSE,
585                                    packet.as_slice(),
586                                )
587                                .await?;
588                            }
589                            *substate = ActiveState::ReadRequest;
590                        }
591                        ActiveState::SendCapability => {
592                            Self::send_packet(
593                                &mut channel,
594                                protocol::MESSAGE_CAPABILITY_RESPONSE,
595                                &protocol::CapabilityResponseMessage {
596                                    lock_on_disconnect: 0.into(),
597                                    reserved: [0.into(); 15],
598                                },
599                            )
600                            .await?;
601                            *substate = ActiveState::ReadRequest;
602                        }
603                    }
604                }
605            }
606        }
607    }
608}