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
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 HvLite, 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 HvLite, 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        inner.mapping_state = Default::default();
330        if let Some(mut state) = inner.mapping_state.take() {
331            state.mem.unmap_from_guest();
332        }
333
334        // TODO: clear VRAM
335    }
336}
337
338impl ChipsetDevice for FramebufferDevice {}
339
340impl SaveRestore for FramebufferDevice {
341    type SavedState = SavedState;
342
343    fn save(&mut self) -> Result<Self::SavedState, SaveError> {
344        let inner = self.inner.lock();
345        let mapping = inner.mapping_state.as_ref().map(
346            |MappingState {
347                 gpa,
348                 subrange,
349                 mem: _,
350             }| SavedMappingState {
351                gpa: *gpa,
352                subrange: *subrange,
353            },
354        );
355        Ok(SavedState {
356            format: inner.format,
357            mapping,
358        })
359    }
360
361    fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
362        let SavedState { mapping, format } = state;
363        let mut inner = self.inner.lock();
364        let inner = &mut *inner;
365        inner.format = format;
366        if let Some(mapping) = mapping {
367            inner
368                .map(mapping.gpa, Some(mapping.subrange))
369                .context("failed to map VRAM to guest")
370                .map_err(RestoreError::Other)?;
371        }
372
373        inner
374            .framebuffer
375            .as_mut()
376            .unwrap()
377            .format_send
378            .send(inner.format);
379        Ok(())
380    }
381}
382
383impl FramebufferInner {
384    fn map(&mut self, gpa: u64, framebuffer_range: Option<MemoryRange>) -> anyhow::Result<()> {
385        if let Some(mut state) = self.mapping_state.take() {
386            state.mem.unmap_from_guest();
387        }
388
389        let Some(framebuffer) = &self.framebuffer else {
390            return Ok(());
391        };
392
393        let framebuffer_range =
394            framebuffer_range.unwrap_or_else(|| MemoryRange::new(0..framebuffer.len as u64));
395
396        let (mut mem, region) = self
397            .mapper
398            .new_region(framebuffer_range.len() as usize, "framebuffer".to_owned())
399            .context("failed to create framebuffer region")?;
400
401        region
402            .map(
403                0,
404                &framebuffer.vram,
405                framebuffer_range.start(),
406                framebuffer_range.len() as usize,
407                true,
408            )
409            .context("failed to map framebuffer memory")?;
410
411        mem.map_to_guest(gpa, true)
412            .context("failed to map VRAM to guest")?;
413        self.mapping_state = Some(MappingState {
414            gpa,
415            subrange: framebuffer_range,
416            mem,
417        });
418
419        tracing::debug!("Mapped VRAM to guest at address {:#x}", gpa);
420
421        Ok(())
422    }
423}
424
425impl FramebufferLocalControl {
426    /// Maps the framebuffer to the guest at the specified GPA.
427    ///
428    /// `framebuffer_range` is an optional subrange of the framebuffer to map.
429    pub fn map(&mut self, gpa: u64, framebuffer_range: Option<MemoryRange>) {
430        if let Err(err) = self.inner.lock().map(gpa, framebuffer_range) {
431            tracing::error!(
432                gpa,
433                error = err.as_ref() as &dyn std::error::Error,
434                "failed to map framebuffer to guest"
435            );
436        }
437    }
438
439    /// Unmaps the framebuffer from the guest.
440    pub fn unmap(&mut self) {
441        let mut inner = self.inner.lock();
442        if let Some(mut state) = inner.mapping_state.take() {
443            state.mem.unmap_from_guest();
444        }
445    }
446
447    /// Returns the size of the framebuffer in bytes.
448    pub fn len(&self) -> usize {
449        self.len
450    }
451
452    /// Updates the framebuffer format.
453    pub fn set_format(&mut self, format: FramebufferFormat) {
454        let mut inner = self.inner.lock();
455        let inner = &mut *inner;
456
457        if inner.format != format {
458            inner.format = format;
459            if let Some(framebuffer) = &mut inner.framebuffer {
460                framebuffer.format_send.send(inner.format);
461            }
462        }
463    }
464
465    /// Gets a `GuestMemory` object that can be used to access the framebuffer
466    /// memory.
467    pub fn memory(&self) -> io::Result<GuestMemory> {
468        let inner = self.inner.lock();
469        let framebuffer = inner
470            .framebuffer
471            .as_ref()
472            .expect("framebuffer is still active");
473        let mapping = SparseMapping::new(framebuffer.len())?;
474        mapping.map_file(0, framebuffer.len(), &framebuffer.vram, 0, true)?;
475        Ok(GuestMemory::new("framebuffer", mapping))
476    }
477}
478
479// On the host the mapping is done immediately, but we still use the async trait
480// so the video device doesn't have to be aware of the underlying implementation.
481#[async_trait::async_trait]
482impl FramebufferControl for FramebufferLocalControl {
483    async fn map(&mut self, gpa: u64) {
484        self.map(gpa, None);
485    }
486    async fn unmap(&mut self) {
487        self.unmap();
488    }
489    async fn set_format(&mut self, format: FramebufferFormat) {
490        self.set_format(format);
491    }
492}
493
494impl ResolveResource<FramebufferHandleKind, SharedFramebufferHandle> for FramebufferLocalControl {
495    type Output = ResolvedFramebuffer;
496    type Error = Infallible;
497
498    fn resolve(
499        &self,
500        _resource: SharedFramebufferHandle,
501        _input: (),
502    ) -> Result<Self::Output, Self::Error> {
503        Ok(self.clone().into())
504    }
505}