lower_vtl_permissions_guard/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Implements a VtlMemoryProtection guard that can be used to temporarily allow
5//! access to pages that were previously protected.
6
7#![cfg(target_os = "linux")]
8
9mod device_dma;
10
11pub use device_dma::LowerVtlDmaBuffer;
12
13use anyhow::Context;
14use anyhow::Result;
15use inspect::Inspect;
16use std::sync::Arc;
17use user_driver::DmaClient;
18use user_driver::memory::MemoryBlock;
19use virt::VtlMemoryProtection;
20
21/// A guard that will restore [`hvdef::HV_MAP_GPA_PERMISSIONS_NONE`] permissions
22/// on the pages when dropped.
23#[derive(Inspect)]
24struct PagesAccessibleToLowerVtl {
25    #[inspect(skip)]
26    vtl_protect: Arc<dyn VtlMemoryProtection + Send + Sync>,
27    #[inspect(hex, iter_by_index)]
28    pages: Vec<u64>,
29}
30
31impl PagesAccessibleToLowerVtl {
32    /// Creates a new guard that will lower the VTL permissions of the pages
33    /// while the returned guard is held.
34    fn new_from_pages(
35        vtl_protect: Arc<dyn VtlMemoryProtection + Send + Sync>,
36        pages: &[u64],
37    ) -> Result<Self> {
38        for pfn in pages {
39            vtl_protect
40                .modify_vtl_page_setting(*pfn, hvdef::HV_MAP_GPA_PERMISSIONS_ALL)
41                .context("failed to update VTL protections on page")?;
42        }
43        Ok(Self {
44            vtl_protect,
45            pages: pages.to_vec(),
46        })
47    }
48}
49
50impl Drop for PagesAccessibleToLowerVtl {
51    fn drop(&mut self) {
52        if let Err(err) = self
53            .pages
54            .iter()
55            .map(|pfn| {
56                self.vtl_protect
57                    .modify_vtl_page_setting(*pfn, hvdef::HV_MAP_GPA_PERMISSIONS_NONE)
58                    .context("failed to update VTL protections on page")
59            })
60            .collect::<Result<Vec<_>>>()
61        {
62            // The inability to rollback any pages is fatal. We cannot leave the
63            // pages in the state where the correct VTL protections are not
64            // applied, because that would compromise the security of the
65            // platform.
66            panic!(
67                "failed to reset page protections {}",
68                err.as_ref() as &dyn std::error::Error
69            );
70        }
71    }
72}
73
74/// A [`DmaClient`] wrapper that will lower the VTL permissions of the page
75/// on the allocated memory block.
76#[derive(Inspect)]
77pub struct LowerVtlMemorySpawner<T: DmaClient> {
78    #[inspect(skip)]
79    spawner: T,
80    #[inspect(skip)]
81    vtl_protect: Arc<dyn VtlMemoryProtection + Send + Sync>,
82}
83
84impl<T: DmaClient> LowerVtlMemorySpawner<T> {
85    /// Create a new wrapped [`DmaClient`] spawner that will lower the VTL
86    /// permissions of the returned [`MemoryBlock`].
87    pub fn new(spawner: T, vtl_protect: Arc<dyn VtlMemoryProtection + Send + Sync>) -> Self {
88        Self {
89            spawner,
90            vtl_protect,
91        }
92    }
93}
94
95impl<T: DmaClient> DmaClient for LowerVtlMemorySpawner<T> {
96    fn allocate_dma_buffer(&self, len: usize) -> Result<MemoryBlock> {
97        let mem = self.spawner.allocate_dma_buffer(len)?;
98        let vtl_guard =
99            PagesAccessibleToLowerVtl::new_from_pages(self.vtl_protect.clone(), mem.pfns())
100                .context("failed to lower VTL permissions on memory block")?;
101
102        Ok(MemoryBlock::new(LowerVtlDmaBuffer {
103            block: mem,
104            _vtl_guard: vtl_guard,
105        }))
106    }
107
108    fn attach_pending_buffers(&self) -> Result<Vec<MemoryBlock>> {
109        anyhow::bail!("restore is not supported for LowerVtlMemorySpawner")
110    }
111}