1mod 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)?; 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; 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; 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; 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; 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 Request::PointerShape
123 }
124 protocol::MESSAGE_DIRT => {
125 let (message, buf) = Ref::<_, protocol::DirtMessage>::from_prefix(buf)
126 .map_err(|_| Error::InvalidPacket)?; Request::Dirt(
128 <[protocol::Rectangle]>::ref_from_prefix_with_elems(
129 buf,
130 message.dirt_count as usize,
131 )
132 .map_err(|_| Error::InvalidPacket)? .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; 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
152pub struct Video {
154 control: Box<dyn FramebufferControl>,
155}
156
157impl Video {
158 pub fn new(control: Box<dyn FramebufferControl>) -> anyhow::Result<Self> {
160 Ok(Self { control })
161 }
162}
163
164#[derive(Protobuf, SavedStateRoot)]
166#[mesh(package = "ui.synthvid")]
167pub struct SavedState(ChannelState);
168
169pub 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 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 }
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}