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