Skip to main content

framebuffer/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Framebuffer device.
5//!
6//! Provides interfaces for reading, mapping, and managing the format of the framebuffer.
7//!
8//! The function [`framebuffer`] is used to create two structs:
9//! [`Framebuffer`], which is used to map an area of memory for the guest to
10//! write to, and [`FramebufferAccess`], which is transformed into a [`View`]
11//! to allow the VNC server to read from that memory.
12//! In OpenVMM, the Framebuffer is used to create a [`FramebufferDevice`]
13//! and a [`FramebufferLocalControl`] which share state using an inner mutex.
14//! The latter implements [`FramebufferControl`] which provides the necessary
15//! interfaces for a video device to control the framebuffer.
16//! In Underhill, the format sender is extracted from the framebuffer and used
17//! to create a different struct that implements the same trait.
18//!
19//! This is separate from the synthetic device because its lifetime is separate
20//! from that of the synthetic video VMBus channel.
21
22#![forbid(unsafe_code)]
23
24use anyhow::Context;
25use chipset_device::ChipsetDevice;
26use guestmem::GuestMemory;
27use guestmem::MappableGuestMemory;
28use guestmem::MemoryMapper;
29use inspect::Inspect;
30use inspect::InspectMut;
31use memory_range::MemoryRange;
32use mesh::MeshPayload;
33use mesh::payload::Protobuf;
34use parking_lot::Mutex;
35use sparse_mmap::Mappable;
36use sparse_mmap::SparseMapping;
37use std::convert::Infallible;
38use std::io;
39use std::sync::Arc;
40use video_core::FramebufferControl;
41use video_core::FramebufferFormat;
42use video_core::ResolvedFramebuffer;
43use video_core::SharedFramebufferHandle;
44use vm_resource::ResolveResource;
45use vm_resource::kind::FramebufferHandleKind;
46use vmcore::device_state::ChangeDeviceState;
47use vmcore::save_restore::RestoreError;
48use vmcore::save_restore::SaveError;
49use vmcore::save_restore::SaveRestore;
50use vmcore::save_restore::SavedStateRoot;
51
52fn default_framebuffer_format() -> FramebufferFormat {
53    FramebufferFormat {
54        width: 1024,
55        height: 768,
56        bytes_per_line: 1024 * 4,
57        offset: 0,
58    }
59}
60
61/// The framebuffer size. In the future, this will be variable.
62// TODO: Make framebuffer size variable. See DetermineSynthVideoVramSize() in OS repo
63pub const FRAMEBUFFER_SIZE: usize = 8 * 1024 * 1024; // 8 MB
64
65/// Creates a framebuffer and an object that can be used to read from it.
66/// The framebuffer should be allocated prior to calling this function.
67///
68/// * `vram`:   [`Mappable`] (`OwnedFd`/`OwnedHandle`) that can be mapped into guest memory
69///   and a `SparseMapping` for the VNC server to read from. In OpenVMM, this
70///   is created by `alloc_shared_memory`. In Underhill, this is `/dev/mshv_vtl_low`.
71///
72/// * `len`:    The amount of memory that was allocated for the framebuffer.
73///
74/// * `offset`: The `file_offset` that should be used when reading the framebuffer.
75///   In OpenVMM, this should be 0. In Underhill, this is the GPA of the
76///   VTL2 framebuffer mapping.
77pub fn framebuffer(
78    vram: Mappable,
79    len: usize,
80    offset: u64,
81) -> io::Result<(Framebuffer, FramebufferAccess)> {
82    assert_eq!(
83        len, FRAMEBUFFER_SIZE,
84        "no framebuffer size flexibility for now"
85    );
86
87    let (send, recv) = mesh::channel();
88
89    let fb = Framebuffer {
90        vram: vram.try_clone()?,
91        len,
92        format_send: send,
93    };
94    let access = FramebufferAccess {
95        vram,
96        len,
97        format_recv: recv,
98        offset,
99    };
100    Ok((fb, access))
101}
102
103/// The video framebuffer to be provided to the device.
104#[derive(Debug, MeshPayload)]
105pub struct Framebuffer {
106    vram: Mappable,
107    len: usize,
108    format_send: mesh::Sender<FramebufferFormat>,
109}
110
111impl Framebuffer {
112    /// Get the size of the framebuffer
113    pub fn len(&self) -> usize {
114        self.len
115    }
116
117    /// Extract format sender, consuming the framebuffer
118    pub fn format_send(self) -> mesh::Sender<FramebufferFormat> {
119        self.format_send
120    }
121}
122
123/// An accessor for the framebuffer. Can be sent cross-process via mesh.
124#[derive(Debug, MeshPayload)]
125pub struct FramebufferAccess {
126    vram: Mappable,
127    len: usize,
128    format_recv: mesh::Receiver<FramebufferFormat>,
129    offset: u64,
130}
131
132impl FramebufferAccess {
133    /// Maps the framebuffer view.
134    pub fn view(self) -> io::Result<View> {
135        let mapping = SparseMapping::new(self.len)?;
136        mapping.map_file(0, self.len, &self.vram, self.offset, false)?;
137        Ok(View {
138            mapping,
139            format_recv: self.format_recv,
140            format: None,
141            vram: self.vram,
142            len: self.len,
143            offset: self.offset,
144        })
145    }
146}
147
148/// A mapped view of the framebuffer.
149#[derive(Debug)]
150pub struct View {
151    mapping: SparseMapping,
152    format_recv: mesh::Receiver<FramebufferFormat>,
153    format: Option<FramebufferFormat>,
154    vram: Mappable,
155    len: usize,
156    offset: u64,
157}
158
159impl View {
160    /// Reads a line within the framebuffer.
161    pub fn read_line(&mut self, line: u16, data: &mut [u8]) {
162        if let Some(format) = &self.format {
163            if let Some(offset) = (line as usize)
164                .checked_mul(format.bytes_per_line)
165                .and_then(|x| x.checked_add(format.offset))
166            {
167                let len = std::cmp::min(data.len(), format.width * 4);
168                let _ = self.mapping.read_at(offset, &mut data[..len]);
169                return;
170            }
171        }
172        data.fill(0);
173    }
174
175    /// Returns the current resolution.
176    pub fn resolution(&mut self) -> (u16, u16) {
177        // Get any framebuffer updates.
178        //
179        // FUTURE-use a channel/port type that throws away all but the last
180        // message to avoid possible high memory use.
181        while let Ok(format) = self.format_recv.try_recv() {
182            self.format = Some(format);
183        }
184        if let Some(format) = &self.format {
185            (format.width as u16, format.height as u16)
186        } else {
187            (1, 1)
188        }
189    }
190
191    /// Gets the framebuffer access back.
192    pub fn access(self) -> FramebufferAccess {
193        // Put the current format at the head of the channel.
194        let (send, recv) = mesh::channel();
195        if let Some(format) = self.format {
196            send.send(format);
197        }
198        send.bridge(self.format_recv);
199        FramebufferAccess {
200            vram: self.vram,
201            len: self.len,
202            format_recv: recv,
203            offset: self.offset,
204        }
205    }
206}
207
208/// A chipset device for the framebuffer.
209#[derive(InspectMut)]
210pub struct FramebufferDevice {
211    #[inspect(flatten)]
212    inner: Arc<Mutex<FramebufferInner>>,
213    #[inspect(hex)]
214    len: usize,
215}
216
217/// Used to control a framebuffer running in the same process
218#[derive(Clone)]
219pub struct FramebufferLocalControl {
220    inner: Arc<Mutex<FramebufferInner>>,
221    len: usize,
222}
223
224#[derive(Inspect)]
225struct FramebufferInner {
226    #[inspect(skip)]
227    _mem_fixed: Option<Box<dyn MappableGuestMemory>>,
228    #[inspect(skip)]
229    framebuffer: Option<Framebuffer>,
230    mapping_state: Option<MappingState>,
231    format: FramebufferFormat,
232    #[inspect(skip)]
233    mapper: Box<dyn MemoryMapper>,
234}
235
236#[derive(Inspect)]
237struct MappingState {
238    gpa: u64,
239    subrange: MemoryRange,
240    #[inspect(skip)]
241    mem: Box<dyn MappableGuestMemory>,
242}
243
244/// Saved state.
245#[derive(Debug, Clone, Protobuf, SavedStateRoot)]
246#[mesh(package = "framebuffer")]
247pub struct SavedState {
248    #[mesh(1)]
249    mapping: Option<SavedMappingState>,
250    #[mesh(2)]
251    format: FramebufferFormat,
252}
253
254#[derive(Debug, Clone, Protobuf)]
255#[mesh(package = "framebuffer")]
256struct SavedMappingState {
257    #[mesh(1)]
258    gpa: u64,
259    #[mesh(2)]
260    subrange: MemoryRange,
261}
262
263impl FramebufferDevice {
264    /// Creates a new framebuffer device from the specified framebuffer
265    /// using the given mapper. Optionally creates a second mapping that does
266    /// not move once the VM is started. This can be used fo VTL2 to read from.
267    pub fn new(
268        mapper: Box<dyn MemoryMapper>,
269        framebuffer: Framebuffer,
270        framebuffer_gpa_base_fixed: Option<u64>,
271    ) -> anyhow::Result<Self> {
272        let len = framebuffer.len;
273
274        // If the framebuffer is going to be used by VTL2, make a secondary mapping
275        // for the VNC server to read that doesn't move once the vm is started.
276        // This allows VTL2 to avoid remapping its framebuffer view if VTL0 moves it.
277        let mem_fixed = if let Some(gpa) = framebuffer_gpa_base_fixed {
278            let (mut mem, region) = mapper
279                .new_region(len, "framebuffer-vtl2".to_owned())
280                .context("failed to create vtl2 framebuffer memory region")?;
281
282            region
283                .map(0, &framebuffer.vram, 0, len, true)
284                .context("failed to map vtl2 framebuffer memory region")?;
285
286            mem.map_to_guest(gpa, true)?;
287            Some(mem)
288        } else {
289            None
290        };
291
292        // Send the initial framebuffer format.
293        let format = default_framebuffer_format();
294        framebuffer.format_send.send(format);
295
296        Ok(Self {
297            inner: Arc::new(Mutex::new(FramebufferInner {
298                _mem_fixed: mem_fixed,
299                mapping_state: None,
300                format,
301                framebuffer: Some(framebuffer),
302                mapper,
303            })),
304            len,
305        })
306    }
307
308    /// Gets the inner framebuffer back.
309    pub fn into_framebuffer(self) -> Framebuffer {
310        self.inner.lock().framebuffer.take().unwrap()
311    }
312
313    /// Gets the control plane for the framebuffer.
314    pub fn control(&self) -> FramebufferLocalControl {
315        FramebufferLocalControl {
316            inner: self.inner.clone(),
317            len: self.len,
318        }
319    }
320}
321
322impl ChangeDeviceState for FramebufferDevice {
323    fn start(&mut self) {}
324
325    async fn stop(&mut self) {}
326
327    async fn reset(&mut self) {
328        let mut inner = self.inner.lock();
329        if let Some(mut state) = inner.mapping_state.take() {
330            state.mem.unmap_from_guest();
331        }
332
333        // TODO: clear VRAM
334    }
335}
336
337impl ChipsetDevice for FramebufferDevice {}
338
339impl SaveRestore for FramebufferDevice {
340    type SavedState = SavedState;
341
342    fn save(&mut self) -> Result<Self::SavedState, SaveError> {
343        let inner = self.inner.lock();
344        let mapping = inner.mapping_state.as_ref().map(
345            |MappingState {
346                 gpa,
347                 subrange,
348                 mem: _,
349             }| SavedMappingState {
350                gpa: *gpa,
351                subrange: *subrange,
352            },
353        );
354        Ok(SavedState {
355            format: inner.format,
356            mapping,
357        })
358    }
359
360    fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
361        let SavedState { mapping, format } = state;
362        let mut inner = self.inner.lock();
363        let inner = &mut *inner;
364        inner.format = format;
365        if let Some(mapping) = mapping {
366            inner
367                .map(mapping.gpa, Some(mapping.subrange))
368                .context("failed to map VRAM to guest")
369                .map_err(RestoreError::Other)?;
370        }
371
372        inner
373            .framebuffer
374            .as_mut()
375            .unwrap()
376            .format_send
377            .send(inner.format);
378        Ok(())
379    }
380}
381
382impl FramebufferInner {
383    fn map(&mut self, gpa: u64, framebuffer_range: Option<MemoryRange>) -> anyhow::Result<()> {
384        if let Some(mut state) = self.mapping_state.take() {
385            state.mem.unmap_from_guest();
386        }
387
388        let Some(framebuffer) = &self.framebuffer else {
389            return Ok(());
390        };
391
392        let framebuffer_range =
393            framebuffer_range.unwrap_or_else(|| MemoryRange::new(0..framebuffer.len as u64));
394
395        let (mut mem, region) = self
396            .mapper
397            .new_region(framebuffer_range.len() as usize, "framebuffer".to_owned())
398            .context("failed to create framebuffer region")?;
399
400        region
401            .map(
402                0,
403                &framebuffer.vram,
404                framebuffer_range.start(),
405                framebuffer_range.len() as usize,
406                true,
407            )
408            .context("failed to map framebuffer memory")?;
409
410        mem.map_to_guest(gpa, true)
411            .context("failed to map VRAM to guest")?;
412        self.mapping_state = Some(MappingState {
413            gpa,
414            subrange: framebuffer_range,
415            mem,
416        });
417
418        tracing::debug!("Mapped VRAM to guest at address {:#x}", gpa);
419
420        Ok(())
421    }
422}
423
424impl FramebufferLocalControl {
425    /// Maps the framebuffer to the guest at the specified GPA.
426    ///
427    /// `framebuffer_range` is an optional subrange of the framebuffer to map.
428    pub fn map(&mut self, gpa: u64, framebuffer_range: Option<MemoryRange>) {
429        if let Err(err) = self.inner.lock().map(gpa, framebuffer_range) {
430            tracing::error!(
431                gpa,
432                error = err.as_ref() as &dyn std::error::Error,
433                "failed to map framebuffer to guest"
434            );
435        }
436    }
437
438    /// Unmaps the framebuffer from the guest.
439    pub fn unmap(&mut self) {
440        let mut inner = self.inner.lock();
441        if let Some(mut state) = inner.mapping_state.take() {
442            state.mem.unmap_from_guest();
443        }
444    }
445
446    /// Returns the size of the framebuffer in bytes.
447    pub fn len(&self) -> usize {
448        self.len
449    }
450
451    /// Updates the framebuffer format.
452    pub fn set_format(&mut self, format: FramebufferFormat) {
453        let mut inner = self.inner.lock();
454        let inner = &mut *inner;
455
456        if inner.format != format {
457            inner.format = format;
458            if let Some(framebuffer) = &mut inner.framebuffer {
459                framebuffer.format_send.send(inner.format);
460            }
461        }
462    }
463
464    /// Gets a `GuestMemory` object that can be used to access the framebuffer
465    /// memory.
466    pub fn memory(&self) -> io::Result<GuestMemory> {
467        let inner = self.inner.lock();
468        let framebuffer = inner
469            .framebuffer
470            .as_ref()
471            .expect("framebuffer is still active");
472        let mapping = SparseMapping::new(framebuffer.len())?;
473        mapping.map_file(0, framebuffer.len(), &framebuffer.vram, 0, true)?;
474        Ok(GuestMemory::new("framebuffer", mapping))
475    }
476}
477
478// On the host the mapping is done immediately, but we still use the async trait
479// so the video device doesn't have to be aware of the underlying implementation.
480#[async_trait::async_trait]
481impl FramebufferControl for FramebufferLocalControl {
482    async fn map(&mut self, gpa: u64) {
483        self.map(gpa, None);
484    }
485    async fn unmap(&mut self) {
486        self.unmap();
487    }
488    async fn set_format(&mut self, format: FramebufferFormat) {
489        self.set_format(format);
490    }
491}
492
493impl ResolveResource<FramebufferHandleKind, SharedFramebufferHandle> for FramebufferLocalControl {
494    type Output = ResolvedFramebuffer;
495    type Error = Infallible;
496
497    fn resolve(
498        &self,
499        _resource: SharedFramebufferHandle,
500        _input: (),
501    ) -> Result<Self::Output, Self::Error> {
502        Ok(self.clone().into())
503    }
504}