Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/base/src/sys/windows/mmap_platform.rs
5394 views
1
// Copyright 2022 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
//! The mmap module provides a safe interface to map memory and ensures UnmapViewOfFile is called
6
//! when the mmap object leaves scope.
7
8
use libc::c_void;
9
use win_util::get_high_order;
10
use win_util::get_low_order;
11
use winapi::shared::minwindef::DWORD;
12
use winapi::um::memoryapi::FlushViewOfFile;
13
use winapi::um::memoryapi::MapViewOfFile;
14
use winapi::um::memoryapi::MapViewOfFileEx;
15
use winapi::um::memoryapi::UnmapViewOfFile;
16
use winapi::um::memoryapi::FILE_MAP_READ;
17
use winapi::um::memoryapi::FILE_MAP_WRITE;
18
19
use super::allocation_granularity;
20
use super::mmap::MemoryMapping;
21
use crate::descriptor::AsRawDescriptor;
22
use crate::warn;
23
use crate::MmapError as Error;
24
use crate::MmapResult as Result;
25
use crate::Protection;
26
use crate::RawDescriptor;
27
28
impl From<Protection> for DWORD {
29
#[inline(always)]
30
fn from(p: Protection) -> Self {
31
let mut value = 0;
32
if p.read {
33
value |= FILE_MAP_READ;
34
}
35
if p.write {
36
value |= FILE_MAP_WRITE;
37
}
38
value
39
}
40
}
41
42
impl MemoryMapping {
43
/// Creates an anonymous shared mapping of `size` bytes with `prot` protection.
44
///
45
/// # Arguments
46
/// * `size` - Size of memory region in bytes.
47
/// * `prot` - Protection (e.g. readable/writable) of the memory region.
48
pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> {
49
// SAFETY:
50
// This is safe because we are creating an anonymous mapping in a place not already used by
51
// any other area in this process.
52
unsafe { MemoryMapping::try_mmap(None, size, prot.into(), None) }
53
}
54
55
/// Create a Memory mapping from a raw address returned by
56
/// the kernel.
57
/// # Arguments
58
/// * `addr` - Raw pointer to memory region.
59
/// * `size` - Size of memory region in bytes.
60
pub fn from_raw_ptr(addr: *mut c_void, size: usize) -> Result<MemoryMapping> {
61
Ok(MemoryMapping { addr, size })
62
}
63
64
pub fn from_raw_handle_offset(
65
file_handle: RawDescriptor,
66
size: usize,
67
) -> Result<MemoryMapping> {
68
// SAFETY:
69
// This is safe because we are creating an anonymous mapping in a place not already used by
70
// any other area in this process.
71
unsafe {
72
MemoryMapping::try_mmap(
73
None,
74
size,
75
Protection::read_write().into(),
76
Some((file_handle, 0)),
77
)
78
}
79
}
80
81
/// Maps the `size` bytes starting at `offset` bytes of the given `descriptor` as read/write.
82
///
83
/// # Arguments
84
/// * `file_handle` - File handle to map from.
85
/// * `size` - Size of memory region in bytes.
86
/// * `offset` - Offset in bytes from the beginning of `descriptor` to start the mmap.
87
/// * `prot` - Protection (e.g. readable/writable) of the memory region.
88
pub fn from_descriptor_offset_protection(
89
file_handle: &dyn AsRawDescriptor,
90
size: usize,
91
offset: u64,
92
prot: Protection,
93
) -> Result<MemoryMapping> {
94
// SAFETY:
95
// This is safe because we are creating an anonymous mapping in a place not already used by
96
// any other area in this process.
97
unsafe {
98
MemoryMapping::try_mmap(
99
None,
100
size,
101
prot.into(),
102
Some((file_handle.as_raw_descriptor(), offset)),
103
)
104
}
105
}
106
107
/// Creates an anonymous shared mapping of `size` bytes with `prot` protection.
108
///
109
/// # Safety
110
/// Unsafe: unmaps any mmap'd regions already present at (addr..addr+size).
111
///
112
/// # Arguments
113
/// * `addr` - Memory address to mmap at.
114
/// * `size` - Size of memory region in bytes.
115
/// * `prot` - Protection (e.g. readable/writable) of the memory region.
116
pub unsafe fn new_protection_fixed(
117
addr: *mut u8,
118
size: usize,
119
prot: Protection,
120
) -> Result<MemoryMapping> {
121
MemoryMapping::try_mmap(Some(addr), size, prot.into(), None)
122
}
123
124
/// Maps the `size` bytes starting at `offset` bytes of the given `descriptor` with
125
/// `prot` protections.
126
///
127
/// # Safety
128
/// Unsafe: unmaps any mmap'd regions already present at (addr..addr+size).
129
///
130
/// # Arguments
131
/// * `addr` - Memory address to mmap at.
132
/// * `descriptor` - File descriptor to mmap from.
133
/// * `size` - Size of memory region in bytes.
134
/// * `offset` - Offset in bytes from the beginning of `descriptor` to start the mmap.
135
/// * `prot` - Protection (e.g. readable/writable) of the memory region.
136
pub unsafe fn from_descriptor_offset_protection_fixed(
137
addr: *mut u8,
138
descriptor: &dyn AsRawDescriptor,
139
size: usize,
140
offset: u64,
141
prot: Protection,
142
) -> Result<MemoryMapping> {
143
MemoryMapping::try_mmap(
144
Some(addr),
145
size,
146
prot.into(),
147
Some((descriptor.as_raw_descriptor(), offset)),
148
)
149
}
150
151
/// Calls MapViewOfFile/MapViewOfFileEx with the parameters given
152
unsafe fn try_mmap(
153
addr: Option<*mut u8>,
154
size: usize,
155
access_flags: DWORD,
156
file_handle: Option<(RawDescriptor, u64)>,
157
) -> Result<MemoryMapping> {
158
if file_handle.is_none() {
159
// TODO(mikehoyle): For now, do not allow the no-handle scenario. As per comments
160
// in guest_memory, the no-handle case is for legacy support and shouldn't have
161
// significant usage.
162
return Err(Error::InvalidAddress);
163
}
164
let file_handle = file_handle.unwrap();
165
166
// on windows, pages needed to be of fixed granular size, and the
167
// maximum valid value is an i64.
168
if file_handle.1 % allocation_granularity() != 0 || file_handle.1 > i64::MAX as u64 {
169
return Err(Error::InvalidOffset);
170
}
171
172
let created_address = match addr {
173
Some(addr) => MapViewOfFileEx(
174
file_handle.0,
175
access_flags,
176
get_high_order(file_handle.1),
177
get_low_order(file_handle.1),
178
size,
179
addr as *mut c_void,
180
),
181
None => MapViewOfFile(
182
file_handle.0,
183
access_flags,
184
get_high_order(file_handle.1),
185
get_low_order(file_handle.1),
186
size,
187
),
188
};
189
190
if created_address.is_null() {
191
return Err(Error::SystemCallFailed(super::Error::last()));
192
}
193
194
Ok(MemoryMapping {
195
addr: created_address,
196
size,
197
})
198
}
199
200
/// Calls FlushViewOfFile on the mapped memory range, ensuring all changes that would
201
/// be written to disk are written immediately
202
pub fn msync(&self) -> Result<()> {
203
// SAFETY:
204
// Safe because self can only be created as a successful memory mapping
205
unsafe {
206
if FlushViewOfFile(self.addr, self.size) == 0 {
207
return Err(Error::SystemCallFailed(super::Error::last()));
208
}
209
};
210
Ok(())
211
}
212
}
213
214
impl Drop for MemoryMapping {
215
fn drop(&mut self) {
216
// SAFETY:
217
// This is safe because we MapViewOfFile the area at addr ourselves, and nobody
218
// else is holding a reference to it.
219
unsafe {
220
if UnmapViewOfFile(self.addr) == 0 {
221
warn!("Unsuccessful unmap of file: {}", super::Error::last());
222
}
223
}
224
}
225
}
226
227
/// TODO(b/150415526): This is currently an empty implementation to allow simple usages in
228
/// shared structs. Any attempts to use it will result in compiler errors. It will need
229
/// to be ported to actually be used on Windows.
230
pub struct MemoryMappingArena();
231
232
#[cfg(test)]
233
mod tests {
234
use std::ptr;
235
236
use winapi::shared::winerror;
237
238
use super::super::pagesize;
239
use super::Error;
240
use crate::descriptor::FromRawDescriptor;
241
use crate::MappedRegion;
242
use crate::MemoryMappingBuilder;
243
use crate::SharedMemory;
244
245
#[test]
246
fn map_invalid_fd() {
247
// SAFETY: trivially safe to create an invalid File.
248
let descriptor = unsafe { std::fs::File::from_raw_descriptor(ptr::null_mut()) };
249
let res = MemoryMappingBuilder::new(1024)
250
.from_file(&descriptor)
251
.build()
252
.unwrap_err();
253
if let Error::StdSyscallFailed(e) = res {
254
assert_eq!(
255
e.raw_os_error(),
256
Some(winerror::ERROR_INVALID_HANDLE as i32)
257
);
258
} else {
259
panic!("unexpected error: {res}");
260
}
261
}
262
263
#[test]
264
fn from_descriptor_offset_invalid() {
265
let shm = SharedMemory::new("test", 1028).unwrap();
266
let res = MemoryMappingBuilder::new(4096)
267
.from_shared_memory(&shm)
268
.offset((i64::MAX as u64) + 1)
269
.build()
270
.unwrap_err();
271
match res {
272
Error::InvalidOffset => {}
273
e => panic!("unexpected error: {e}"),
274
}
275
}
276
277
#[test]
278
fn arena_msync() {
279
let size: usize = 0x40000;
280
let shm = SharedMemory::new("test", size as u64).unwrap();
281
let m = MemoryMappingBuilder::new(size)
282
.from_shared_memory(&shm)
283
.build()
284
.unwrap();
285
let ps = pagesize();
286
<dyn MappedRegion>::msync(&m, 0, ps).unwrap();
287
<dyn MappedRegion>::msync(&m, 0, size).unwrap();
288
<dyn MappedRegion>::msync(&m, ps, size - ps).unwrap();
289
let res = <dyn MappedRegion>::msync(&m, ps, size).unwrap_err();
290
match res {
291
Error::InvalidAddress => {}
292
e => panic!("unexpected error: {e}"),
293
}
294
}
295
}
296
297