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