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, Default)]
201#[mesh(package = "ui.synthvid")]
202enum ChannelState {
203    #[mesh(1)]
204    #[default]
205    ReadVersion,
206    #[mesh(2)]
207    WriteVersion {
208        #[mesh(1)]
209        version: Version,
210    },
211    #[mesh(3)]
212    Active {
213        #[mesh(1)]
214        version: Version,
215        #[mesh(2)]
216        substate: ActiveState,
217    },
218}
219
220#[derive(Debug, Clone, Protobuf)]
221#[mesh(package = "ui.synthvid")]
222enum ActiveState {
223    #[mesh(1)]
224    ReadRequest,
225    #[mesh(2)]
226    SendVramAck {
227        #[mesh(1)]
228        user_context: u64,
229    },
230    #[mesh(3)]
231    SendSituationUpdateAck {
232        #[mesh(1)]
233        user_context: u64,
234    },
235    #[mesh(4)]
236    SendBiosInfo,
237    #[mesh(5)]
238    SendSupportedResolutions {
239        #[mesh(1)]
240        maximum_count: u8,
241    },
242    #[mesh(6)]
243    SendCapability,
244}
245
246struct PacketBuffer {
247    buf: Vec<u8>,
248}
249
250impl PacketBuffer {
251    fn new() -> Self {
252        Self {
253            buf: vec![0; protocol::MAX_VMBUS_PACKET_SIZE],
254        }
255    }
256
257    async fn recv_packet(
258        &mut self,
259        reader: &mut (impl AsyncRecv + Unpin),
260    ) -> Result<Request, Error> {
261        let n = match reader.recv(&mut self.buf).await {
262            Ok(n) => n,
263            Err(e) => return Err(Error::Io(e)),
264        };
265        let buf = &self.buf[..n];
266        parse_packet(buf)
267    }
268}
269
270#[async_trait]
271impl SimpleVmbusDevice for Video {
272    type Runner = VideoChannel;
273    type SavedState = SavedState;
274
275    fn offer(&self) -> OfferParams {
276        OfferParams {
277            interface_name: "video".to_owned(),
278            interface_id: Guid {
279                data1: 0xda0a7802,
280                data2: 0xe377,
281                data3: 0x4aac,
282                data4: [0x8e, 0x77, 0x5, 0x58, 0xeb, 0x10, 0x73, 0xf8],
283            },
284            instance_id: Guid {
285                data1: 0x5620e0c7,
286                data2: 0x8062,
287                data3: 0x4dce,
288                data4: [0xae, 0xb7, 0x52, 0xc, 0x7e, 0xf7, 0x61, 0x71],
289            },
290            mmio_megabytes: 8,
291            channel_type: ChannelType::Device { pipe_packets: true },
292            ..Default::default()
293        }
294    }
295
296    fn inspect(&mut self, req: inspect::Request<'_>, task: Option<&mut VideoChannel>) {
297        let mut resp = req.respond();
298        if let Some(this) = task {
299            let (version, state) = match &this.state {
300                ChannelState::ReadVersion => (None, "read_version"),
301                ChannelState::WriteVersion { version } => (Some(*version), "write_version"),
302                ChannelState::Active { version, substate } => (
303                    Some(*version),
304                    match substate {
305                        ActiveState::ReadRequest => "read_request",
306                        ActiveState::SendVramAck { .. } => "send_vram_ack",
307                        ActiveState::SendSituationUpdateAck { .. } => "send_situation_update_ack",
308                        ActiveState::SendBiosInfo => "send_bios_info",
309                        ActiveState::SendSupportedResolutions { .. } => {
310                            "send_supported_resolutions"
311                        }
312                        ActiveState::SendCapability => "send_capability",
313                    },
314                ),
315            };
316            resp.field("state", state)
317                .field(
318                    "version",
319                    version.map(|v| format!("{}.{}", v.major, v.minor)),
320                )
321                .field_mut("channel", &mut this.channel);
322        }
323    }
324
325    fn open(
326        &mut self,
327        channel: RawAsyncChannel<GpadlRingMem>,
328        _guest_memory: guestmem::GuestMemory,
329    ) -> Result<Self::Runner, ChannelOpenError> {
330        let pipe = MessagePipe::new(channel)?;
331        Ok(VideoChannel::new(pipe, ChannelState::default()))
332    }
333
334    async fn run(
335        &mut self,
336        stop: &mut StopTask<'_>,
337        channel: &mut VideoChannel,
338    ) -> Result<(), task_control::Cancelled> {
339        stop.until_stopped(async {
340            match channel.process(&mut self.control).await {
341                Ok(()) => {}
342                Err(err) => tracing::error!(error = &err as &dyn std::error::Error, "video error"),
343            }
344        })
345        .await
346    }
347
348    fn supports_save_restore(
349        &mut self,
350    ) -> Option<
351        &mut dyn SaveRestoreSimpleVmbusDevice<SavedState = Self::SavedState, Runner = Self::Runner>,
352    > {
353        Some(self)
354    }
355}
356
357impl SaveRestoreSimpleVmbusDevice for Video {
358    fn save_open(&mut self, runner: &Self::Runner) -> Self::SavedState {
359        SavedState(runner.state.clone())
360    }
361
362    fn restore_open(
363        &mut self,
364        state: Self::SavedState,
365        channel: RawAsyncChannel<GpadlRingMem>,
366    ) -> Result<Self::Runner, ChannelOpenError> {
367        let pipe = MessagePipe::new(channel)?;
368        Ok(VideoChannel::new(pipe, state.0))
369    }
370}
371
372impl VideoChannel {
373    fn new(channel: MessagePipe<GpadlRingMem>, state: ChannelState) -> Self {
374        Self {
375            channel,
376            state,
377            packet_buf: PacketBuffer::new(),
378        }
379    }
380
381    async fn send_packet<T: IntoBytes + ?Sized + Immutable + KnownLayout>(
382        writer: &mut (impl AsyncSend + Unpin),
383        typ: u32,
384        packet: &T,
385    ) -> Result<(), Error> {
386        let header = protocol::MessageHeader {
387            typ: typ.into(),
388            size: (size_of_val(packet) as u32).into(),
389        };
390        writer
391            .send_vectored(&[
392                IoSlice::new(header.as_bytes()),
393                IoSlice::new(packet.as_bytes()),
394            ])
395            .await
396            .map_err(Error::Io)?;
397
398        Ok(())
399    }
400
401    async fn process(
402        &mut self,
403        framebuffer: &mut Box<dyn FramebufferControl>,
404    ) -> Result<(), Error> {
405        let mut channel = &mut self.channel;
406        loop {
407            match &mut self.state {
408                ChannelState::ReadVersion => {
409                    let version = if let Request::Version(version) =
410                        self.packet_buf.recv_packet(&mut channel).await?
411                    {
412                        version.into()
413                    } else {
414                        return Err(Error::UnexpectedPacketOrder);
415                    };
416                    self.state = ChannelState::WriteVersion { version };
417                }
418                ChannelState::WriteVersion { version } => {
419                    let server_version = Version {
420                        major: protocol::VERSION_MAJOR,
421                        minor: protocol::VERSION_MINOR_BLUE,
422                    };
423                    let is_accepted = if version.major == server_version.major {
424                        protocol::ACCEPTED_WITH_VERSION_EXCHANGE
425                    } else {
426                        0
427                    };
428                    Self::send_packet(
429                        &mut channel,
430                        protocol::MESSAGE_VERSION_RESPONSE,
431                        &protocol::VersionResponseMessage {
432                            version: (*version).into(),
433                            is_accepted,
434                            max_video_outputs: 1,
435                        },
436                    )
437                    .await?;
438                    if is_accepted != 0 {
439                        tracelimit::info_ratelimited!(?version, "video negotiation succeeded");
440                        self.state = ChannelState::Active {
441                            version: *version,
442                            substate: ActiveState::ReadRequest,
443                        };
444                    } else {
445                        tracelimit::warn_ratelimited!(?version, "video negotiation failed");
446                        self.state = ChannelState::ReadVersion;
447                    }
448                }
449                ChannelState::Active {
450                    version: _,
451                    substate,
452                } => {
453                    match *substate {
454                        ActiveState::ReadRequest => {
455                            let packet = self.packet_buf.recv_packet(&mut channel).await?;
456                            match packet {
457                                Request::VramLocation {
458                                    user_context,
459                                    address,
460                                } => {
461                                    framebuffer.unmap().await;
462                                    if let Some(address) = address {
463                                        // N.B. The mapping is preserved until explicitly torn
464                                        //      down--UEFI may open the channel, establish the
465                                        //      mapping, close the channel, and expect the guest
466                                        //      to continue to render to the framebuffer.
467                                        framebuffer.map(address).await;
468                                    }
469                                    *substate = ActiveState::SendVramAck { user_context };
470                                }
471                                Request::SituationUpdate {
472                                    user_context,
473                                    situation,
474                                } => {
475                                    framebuffer
476                                        .set_format(FramebufferFormat {
477                                            width: u32::from(situation.width_pixels) as usize,
478                                            height: u32::from(situation.height_pixels) as usize,
479                                            bytes_per_line: u32::from(situation.pitch_bytes)
480                                                as usize,
481                                            offset: u32::from(situation.primary_surface_vram_offset)
482                                                as usize,
483                                        })
484                                        .await;
485                                    *substate =
486                                        ActiveState::SendSituationUpdateAck { user_context };
487                                }
488                                Request::PointerPosition { is_visible, x, y } => {
489                                    let _ = (is_visible, x, y);
490                                }
491                                Request::PointerShape => {}
492                                Request::Dirt(_rects) => {
493                                    // TODO: Support dirt requests
494                                }
495                                Request::BiosInfo => {
496                                    *substate = ActiveState::SendBiosInfo;
497                                }
498                                Request::SupportedResolutions { maximum_count } => {
499                                    *substate =
500                                        ActiveState::SendSupportedResolutions { maximum_count };
501                                }
502                                Request::Capability => {
503                                    *substate = ActiveState::SendCapability;
504                                }
505                                Request::Version(_) => return Err(Error::UnexpectedPacketOrder),
506                            }
507                        }
508                        ActiveState::SendVramAck { user_context } => {
509                            Self::send_packet(
510                                &mut channel,
511                                protocol::MESSAGE_VRAM_LOCATION_ACK,
512                                &protocol::VramLocationAckMessage {
513                                    user_context: user_context.into(),
514                                },
515                            )
516                            .await?;
517                            *substate = ActiveState::ReadRequest;
518                        }
519                        ActiveState::SendSituationUpdateAck { user_context } => {
520                            Self::send_packet(
521                                &mut channel,
522                                protocol::MESSAGE_SITUATION_UPDATE_ACK,
523                                &protocol::SituationUpdateAckMessage {
524                                    user_context: user_context.into(),
525                                },
526                            )
527                            .await?;
528                            *substate = ActiveState::ReadRequest;
529                        }
530                        ActiveState::SendBiosInfo => {
531                            Self::send_packet(
532                                &mut channel,
533                                protocol::MESSAGE_BIOS_INFO_RESPONSE,
534                                &protocol::BiosInfoResponseMessage {
535                                    stop_device_supported: 1.into(),
536                                    reserved: [0; 12],
537                                },
538                            )
539                            .await?;
540                            *substate = ActiveState::ReadRequest;
541                        }
542                        ActiveState::SendSupportedResolutions { maximum_count } => {
543                            if maximum_count < protocol::MAXIMUM_RESOLUTIONS_COUNT {
544                                Self::send_packet(
545                                    &mut channel,
546                                    protocol::MESSAGE_SUPPORTED_RESOLUTIONS_RESPONSE,
547                                    &protocol::SupportedResolutionsResponseMessage {
548                                        edid_block: protocol::EDID_BLOCK,
549                                        resolution_count: 0,
550                                        default_resolution_index: 0,
551                                        is_standard: 0,
552                                    },
553                                )
554                                .await?;
555                            } else {
556                                const RESOLUTIONS: &[(u16, u16)] = &[(1024, 768), (1280, 1024)];
557
558                                let mut packet = Vec::new();
559                                packet.extend_from_slice(
560                                    protocol::SupportedResolutionsResponseMessage {
561                                        edid_block: protocol::EDID_BLOCK,
562                                        resolution_count: RESOLUTIONS.len().try_into().unwrap(),
563                                        default_resolution_index: 0,
564                                        is_standard: 0,
565                                    }
566                                    .as_bytes(),
567                                );
568                                for r in RESOLUTIONS {
569                                    packet.extend_from_slice(
570                                        protocol::ScreenInfo {
571                                            width: r.0.into(),
572                                            height: r.1.into(),
573                                        }
574                                        .as_bytes(),
575                                    );
576                                }
577                                Self::send_packet(
578                                    &mut channel,
579                                    protocol::MESSAGE_SUPPORTED_RESOLUTIONS_RESPONSE,
580                                    packet.as_slice(),
581                                )
582                                .await?;
583                            }
584                            *substate = ActiveState::ReadRequest;
585                        }
586                        ActiveState::SendCapability => {
587                            Self::send_packet(
588                                &mut channel,
589                                protocol::MESSAGE_CAPABILITY_RESPONSE,
590                                &protocol::CapabilityResponseMessage {
591                                    lock_on_disconnect: 0.into(),
592                                    reserved: [0.into(); 15],
593                                },
594                            )
595                            .await?;
596                            *substate = ActiveState::ReadRequest;
597                        }
598                    }
599                }
600            }
601        }
602    }
603}