Skip to main content

membacking/memory_manager/
device_memory.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! This implements the [`MemoryMapper`] trait and related functionality for
5//! [`GuestMemoryManager`](super::GuestMemoryManager).
6
7use super::DEVICE_PRIORITY;
8use crate::mapping_manager::Mappable;
9use crate::mapping_manager::MappingBacking;
10use crate::region_manager::MapParams;
11use crate::region_manager::RegionHandle;
12use crate::region_manager::RegionManagerClient;
13use futures::executor::block_on;
14use guestmem::MappableGuestMemory;
15use guestmem::MappedMemoryRegion;
16use guestmem::MemoryMapper;
17use memory_range::MemoryRange;
18use parking_lot::Mutex;
19use std::io;
20use std::sync::Arc;
21
22/// A [`MemoryMapper`] implementation for
23/// [`GuestMemoryManager`](super::GuestMemoryManager).
24#[derive(Clone, Debug)]
25pub struct DeviceMemoryMapper {
26    region_manager: RegionManagerClient,
27}
28
29impl DeviceMemoryMapper {
30    pub(super) fn new(region_manager: RegionManagerClient) -> Self {
31        Self { region_manager }
32    }
33}
34
35impl MemoryMapper for DeviceMemoryMapper {
36    fn new_region(
37        &self,
38        len: usize,
39        debug_name: String,
40    ) -> io::Result<(Box<dyn MappableGuestMemory>, Arc<dyn MappedMemoryRegion>)> {
41        let region = Arc::new(DeviceMemoryRegion {
42            len,
43            debug_name,
44            region_manager: self.region_manager.clone(),
45            state: Mutex::new(DeviceRegionState {
46                handle: None,
47                mappings: Vec::new(),
48            }),
49        });
50
51        Ok((Box::new(DeviceMemoryControl(region.clone())), region))
52    }
53}
54
55#[derive(Debug)]
56struct DeviceMemoryRegion {
57    debug_name: String,
58    len: usize,
59    region_manager: RegionManagerClient,
60    state: Mutex<DeviceRegionState>,
61}
62
63#[derive(Debug)]
64struct DeviceRegionState {
65    handle: Option<RegionHandle>,
66    mappings: Vec<DeviceMapping>,
67}
68
69#[derive(Debug)]
70struct DeviceMapping {
71    range: MemoryRange,
72    file_offset: u64,
73    mappable: Mappable,
74    writable: bool,
75}
76
77impl DeviceMemoryRegion {
78    fn validated_memory_range(&self, offset: usize, len: usize) -> io::Result<MemoryRange> {
79        (offset..offset.wrapping_add(len))
80            .try_into()
81            .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))
82    }
83}
84
85impl MappedMemoryRegion for DeviceMemoryRegion {
86    fn map(
87        &self,
88        offset: usize,
89        section: &dyn sparse_mmap::AsMappableRef,
90        file_offset: u64,
91        len: usize,
92        writable: bool,
93    ) -> io::Result<()> {
94        #[cfg(unix)]
95        let mappable = section.as_fd().try_clone_to_owned()?;
96        #[cfg(windows)]
97        let mappable = section.as_handle().try_clone_to_owned()?;
98
99        let range = self.validated_memory_range(offset, len)?;
100        let new_mapping = DeviceMapping {
101            range,
102            file_offset,
103            mappable: mappable.into(),
104            writable,
105        };
106
107        let mut state = self.state.lock();
108        for mapping in &state.mappings {
109            if mapping.range.overlaps(&new_mapping.range) {
110                todo!("support overlapping mappings");
111            }
112        }
113
114        if let Some(handle) = &state.handle {
115            if let Err(e) = block_on(handle.add_mapping(
116                new_mapping.range,
117                MappingBacking::File {
118                    mappable: new_mapping.mappable.clone(),
119                    file_offset: new_mapping.file_offset,
120                },
121                new_mapping.writable,
122                None,
123            )) {
124                return Err(io::Error::other(e));
125            }
126        }
127        state.mappings.push(new_mapping);
128        Ok(())
129    }
130
131    fn unmap(&self, offset: usize, len: usize) -> io::Result<()> {
132        let range = self.validated_memory_range(offset, len)?;
133        let mut state = self.state.lock();
134        state.mappings.retain(|mapping| {
135            if !range.contains(&mapping.range) && range.overlaps(&mapping.range) {
136                todo!("support overlapping mappings");
137            }
138            range.contains(&mapping.range)
139        });
140
141        if let Some(handle) = &state.handle {
142            block_on(handle.remove_mappings(range));
143        }
144        Ok(())
145    }
146}
147
148#[derive(Debug)]
149struct DeviceMemoryControl(Arc<DeviceMemoryRegion>);
150
151impl MappableGuestMemory for DeviceMemoryControl {
152    fn map_to_guest(&mut self, gpa: u64, writable: bool) -> io::Result<()> {
153        #[expect(clippy::await_holding_lock)] // Treat all this as sync for now.
154        block_on(async {
155            let mut state = self.0.state.lock();
156            if let Some(handle) = state.handle.take() {
157                handle.teardown().await;
158            }
159            let handle = self
160                .0
161                .region_manager
162                .new_region(
163                    self.0.debug_name.clone(),
164                    MemoryRange::try_from(gpa..gpa.wrapping_add(self.0.len as u64))
165                        .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?,
166                    DEVICE_PRIORITY,
167                    crate::region_manager::MappingType::Device,
168                )
169                .await
170                .map_err(io::Error::other)?;
171
172            for mapping in &state.mappings {
173                handle
174                    .add_mapping(
175                        mapping.range,
176                        MappingBacking::File {
177                            mappable: mapping.mappable.clone(),
178                            file_offset: mapping.file_offset,
179                        },
180                        mapping.writable,
181                        None,
182                    )
183                    .await
184                    .map_err(io::Error::other)?;
185            }
186
187            handle
188                .map(MapParams {
189                    writable,
190                    executable: true,
191                    prefetch: false,
192                })
193                .await
194                .map_err(io::Error::other)?;
195
196            state.handle = Some(handle);
197            Ok(())
198        })
199    }
200
201    fn unmap_from_guest(&mut self) {
202        let mut state = self.0.state.lock();
203        if let Some(handle) = state.handle.take() {
204            block_on(handle.teardown());
205        }
206    }
207}