• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 use std::cmp::min;
6 use std::fs::File;
7 use std::intrinsics::copy_nonoverlapping;
8 use std::io;
9 use std::mem::size_of;
10 use std::ptr::read_unaligned;
11 use std::ptr::read_volatile;
12 use std::ptr::write_unaligned;
13 use std::ptr::write_volatile;
14 use std::sync::atomic::fence;
15 use std::sync::atomic::Ordering;
16 use std::sync::OnceLock;
17 
18 use remain::sorted;
19 use serde::Deserialize;
20 use serde::Serialize;
21 use zerocopy::AsBytes;
22 use zerocopy::FromBytes;
23 
24 use crate::descriptor::AsRawDescriptor;
25 use crate::descriptor::SafeDescriptor;
26 use crate::platform::MemoryMapping as PlatformMmap;
27 use crate::SharedMemory;
28 use crate::VolatileMemory;
29 use crate::VolatileMemoryError;
30 use crate::VolatileMemoryResult;
31 use crate::VolatileSlice;
32 
33 static CACHELINE_SIZE: OnceLock<usize> = OnceLock::new();
34 
35 #[allow(unused_assignments)]
get_cacheline_size_once() -> usize36 fn get_cacheline_size_once() -> usize {
37     let mut assume_reason: &str = "unknown";
38     cfg_if::cfg_if! {
39         if #[cfg(all(any(target_os = "android", target_os = "linux"), not(target_env = "musl")))] {
40             // SAFETY:
41             // Safe because we check the return value for errors or unsupported requests
42             let linesize = unsafe { libc::sysconf(libc::_SC_LEVEL1_DCACHE_LINESIZE) };
43             if linesize > 0 {
44                 return linesize as usize;
45             } else {
46                 assume_reason = "sysconf cacheline size query failed";
47             }
48         } else {
49             assume_reason = "cacheline size query not implemented for platform/arch";
50         }
51     }
52 
53     let assumed_size = 64;
54     log::debug!(
55         "assuming cacheline_size={}; reason: {}.",
56         assumed_size,
57         assume_reason
58     );
59     assumed_size
60 }
61 
62 /// Returns the system's effective cacheline size (e.g. the granularity at which arch-specific
63 /// cacheline management, such as with the clflush instruction, is expected to occur).
64 #[inline(always)]
get_cacheline_size() -> usize65 fn get_cacheline_size() -> usize {
66     let size = *CACHELINE_SIZE.get_or_init(get_cacheline_size_once);
67     assert!(size > 0);
68     size
69 }
70 
71 #[sorted]
72 #[derive(Debug, thiserror::Error)]
73 pub enum Error {
74     #[error("`add_fd_mapping` is unsupported")]
75     AddFdMappingIsUnsupported,
76     #[error("requested memory out of range")]
77     InvalidAddress,
78     #[error("requested alignment is incompatible")]
79     InvalidAlignment,
80     #[error("invalid argument provided when creating mapping")]
81     InvalidArgument,
82     #[error("requested offset is out of range of off_t")]
83     InvalidOffset,
84     #[error("requested memory range spans past the end of the region: offset={0} count={1} region_size={2}")]
85     InvalidRange(usize, usize, usize),
86     #[error("operation is not implemented on platform/architecture: {0}")]
87     NotImplemented(&'static str),
88     #[error("requested memory is not page aligned")]
89     NotPageAligned,
90     #[error("failed to read from file to memory: {0}")]
91     ReadToMemory(#[source] io::Error),
92     #[error("`remove_mapping` is unsupported")]
93     RemoveMappingIsUnsupported,
94     #[error("system call failed while creating the mapping: {0}")]
95     StdSyscallFailed(io::Error),
96     #[error("mmap related system call failed: {0}")]
97     SystemCallFailed(#[source] crate::Error),
98     #[error("failed to write from memory to file: {0}")]
99     WriteFromMemory(#[source] io::Error),
100 }
101 pub type Result<T> = std::result::Result<T, Error>;
102 
103 /// Memory access type for anonymous shared memory mapping.
104 #[derive(Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize, Debug)]
105 pub struct Protection {
106     pub(crate) read: bool,
107     pub(crate) write: bool,
108 }
109 
110 impl Protection {
111     /// Returns Protection allowing read/write access.
112     #[inline(always)]
read_write() -> Protection113     pub fn read_write() -> Protection {
114         Protection {
115             read: true,
116             write: true,
117         }
118     }
119 
120     /// Returns Protection allowing read access.
121     #[inline(always)]
read() -> Protection122     pub fn read() -> Protection {
123         Protection {
124             read: true,
125             ..Default::default()
126         }
127     }
128 
129     /// Returns Protection allowing write access.
130     #[inline(always)]
write() -> Protection131     pub fn write() -> Protection {
132         Protection {
133             write: true,
134             ..Default::default()
135         }
136     }
137 
138     /// Set read events.
139     #[inline(always)]
set_read(self) -> Protection140     pub fn set_read(self) -> Protection {
141         Protection { read: true, ..self }
142     }
143 
144     /// Set write events.
145     #[inline(always)]
set_write(self) -> Protection146     pub fn set_write(self) -> Protection {
147         Protection {
148             write: true,
149             ..self
150         }
151     }
152 
153     /// Returns true if all access allowed by |other| is also allowed by |self|.
154     #[inline(always)]
allows(&self, other: &Protection) -> bool155     pub fn allows(&self, other: &Protection) -> bool {
156         self.read >= other.read && self.write >= other.write
157     }
158 }
159 
160 /// See [MemoryMapping](crate::platform::MemoryMapping) for struct- and method-level
161 /// documentation.
162 #[derive(Debug)]
163 pub struct MemoryMapping {
164     pub(crate) mapping: PlatformMmap,
165 
166     // File backed mappings on Windows need to keep the underlying file open while the mapping is
167     // open.
168     // This will be a None in non-windows case. The variable will not be read so the '^_'.
169     //
170     // TODO(b:230902713) There was a concern about relying on the kernel's refcounting to keep the
171     // file object's locks (e.g. exclusive read/write) in place. We need to revisit/validate that
172     // concern.
173     pub(crate) _file_descriptor: Option<SafeDescriptor>,
174 }
175 
176 #[inline(always)]
flush_one(_addr: *const u8) -> Result<()>177 unsafe fn flush_one(_addr: *const u8) -> Result<()> {
178     cfg_if::cfg_if! {
179         if #[cfg(target_arch = "x86_64")] {
180             // As per table 11-7 of the SDM, processors are not required to
181             // snoop UC mappings, so flush the target to memory.
182             // SAFETY: assumes that the caller has supplied a valid address.
183             unsafe { core::arch::x86_64::_mm_clflush(_addr) };
184             Ok(())
185         } else if #[cfg(target_arch = "aarch64")] {
186             // Data cache clean by VA to PoC.
187             std::arch::asm!("DC CVAC, {x}", x = in(reg) _addr);
188             Ok(())
189         } else if #[cfg(target_arch = "arm")] {
190             Err(Error::NotImplemented("Userspace cannot flush to PoC"))
191         } else {
192             Err(Error::NotImplemented("Cache flush not implemented"))
193         }
194     }
195 }
196 
197 impl MemoryMapping {
write_slice(&self, buf: &[u8], offset: usize) -> Result<usize>198     pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> {
199         match self.mapping.size().checked_sub(offset) {
200             Some(size_past_offset) => {
201                 let bytes_copied = min(size_past_offset, buf.len());
202                 // SAFETY:
203                 // The bytes_copied equation above ensures we don't copy bytes out of range of
204                 // either buf or this slice. We also know that the buffers do not overlap because
205                 // slices can never occupy the same memory as a volatile slice.
206                 unsafe {
207                     copy_nonoverlapping(buf.as_ptr(), self.as_ptr().add(offset), bytes_copied);
208                 }
209                 Ok(bytes_copied)
210             }
211             None => Err(Error::InvalidAddress),
212         }
213     }
214 
read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize>215     pub fn read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize> {
216         match self.size().checked_sub(offset) {
217             Some(size_past_offset) => {
218                 let bytes_copied = min(size_past_offset, buf.len());
219                 // SAFETY:
220                 // The bytes_copied equation above ensures we don't copy bytes out of range of
221                 // either buf or this slice. We also know that the buffers do not overlap because
222                 // slices can never occupy the same memory as a volatile slice.
223                 unsafe {
224                     copy_nonoverlapping(self.as_ptr().add(offset), buf.as_mut_ptr(), bytes_copied);
225                 }
226                 Ok(bytes_copied)
227             }
228             None => Err(Error::InvalidAddress),
229         }
230     }
231 
232     /// Writes an object to the memory region at the specified offset.
233     /// Returns Ok(()) if the object fits, or Err if it extends past the end.
234     ///
235     /// This method is for writing to regular memory. If writing to a mapped
236     /// I/O region, use [`MemoryMapping::write_obj_volatile`].
237     ///
238     /// # Examples
239     /// * Write a u64 at offset 16.
240     ///
241     /// ```
242     /// #   use base::MemoryMappingBuilder;
243     /// #   use base::SharedMemory;
244     /// #   let shm = SharedMemory::new("test", 1024).unwrap();
245     /// #   let mut mem_map = MemoryMappingBuilder::new(1024).from_shared_memory(&shm).build().unwrap();
246     ///     let res = mem_map.write_obj(55u64, 16);
247     ///     assert!(res.is_ok());
248     /// ```
write_obj<T: AsBytes>(&self, val: T, offset: usize) -> Result<()>249     pub fn write_obj<T: AsBytes>(&self, val: T, offset: usize) -> Result<()> {
250         self.mapping.range_end(offset, size_of::<T>())?;
251         // SAFETY:
252         // This is safe because we checked the bounds above.
253         unsafe {
254             write_unaligned(self.as_ptr().add(offset) as *mut T, val);
255         }
256         Ok(())
257     }
258 
259     /// Reads on object from the memory region at the given offset.
260     /// Reading from a volatile area isn't strictly safe as it could change
261     /// mid-read.  However, as long as the type T is plain old data and can
262     /// handle random initialization, everything will be OK.
263     ///
264     /// This method is for reading from regular memory. If reading from a
265     /// mapped I/O region, use [`MemoryMapping::read_obj_volatile`].
266     ///
267     /// # Examples
268     /// * Read a u64 written to offset 32.
269     ///
270     /// ```
271     /// #   use base::MemoryMappingBuilder;
272     /// #   let mut mem_map = MemoryMappingBuilder::new(1024).build().unwrap();
273     ///     let res = mem_map.write_obj(55u64, 32);
274     ///     assert!(res.is_ok());
275     ///     let num: u64 = mem_map.read_obj(32).unwrap();
276     ///     assert_eq!(55, num);
277     /// ```
read_obj<T: FromBytes>(&self, offset: usize) -> Result<T>278     pub fn read_obj<T: FromBytes>(&self, offset: usize) -> Result<T> {
279         self.mapping.range_end(offset, size_of::<T>())?;
280         // SAFETY:
281         // This is safe because by definition Copy types can have their bits set arbitrarily and
282         // still be valid.
283         unsafe {
284             Ok(read_unaligned(
285                 self.as_ptr().add(offset) as *const u8 as *const T
286             ))
287         }
288     }
289 
290     /// Writes an object to the memory region at the specified offset.
291     /// Returns Ok(()) if the object fits, or Err if it extends past the end.
292     ///
293     /// The write operation will be volatile, i.e. it will not be reordered by
294     /// the compiler and is suitable for I/O, but must be aligned. When writing
295     /// to regular memory, prefer [`MemoryMapping::write_obj`].
296     ///
297     /// # Examples
298     /// * Write a u32 at offset 16.
299     ///
300     /// ```
301     /// #   use base::MemoryMappingBuilder;
302     /// #   use base::SharedMemory;
303     /// #   let shm = SharedMemory::new("test", 1024).unwrap();
304     /// #   let mut mem_map = MemoryMappingBuilder::new(1024).from_shared_memory(&shm).build().unwrap();
305     ///     let res = mem_map.write_obj_volatile(0xf00u32, 16);
306     ///     assert!(res.is_ok());
307     /// ```
write_obj_volatile<T: AsBytes>(&self, val: T, offset: usize) -> Result<()>308     pub fn write_obj_volatile<T: AsBytes>(&self, val: T, offset: usize) -> Result<()> {
309         self.mapping.range_end(offset, size_of::<T>())?;
310         // Make sure writes to memory have been committed before performing I/O that could
311         // potentially depend on them.
312         fence(Ordering::SeqCst);
313         // SAFETY:
314         // This is safe because we checked the bounds above.
315         unsafe {
316             write_volatile(self.as_ptr().add(offset) as *mut T, val);
317         }
318         Ok(())
319     }
320 
321     /// Reads on object from the memory region at the given offset.
322     /// Reading from a volatile area isn't strictly safe as it could change
323     /// mid-read.  However, as long as the type T is plain old data and can
324     /// handle random initialization, everything will be OK.
325     ///
326     /// The read operation will be volatile, i.e. it will not be reordered by
327     /// the compiler and is suitable for I/O, but must be aligned. When reading
328     /// from regular memory, prefer [`MemoryMapping::read_obj`].
329     ///
330     /// # Examples
331     /// * Read a u32 written to offset 16.
332     ///
333     /// ```
334     /// #   use base::MemoryMappingBuilder;
335     /// #   use base::SharedMemory;
336     /// #   let shm = SharedMemory::new("test", 1024).unwrap();
337     /// #   let mut mem_map = MemoryMappingBuilder::new(1024).from_shared_memory(&shm).build().unwrap();
338     ///     let res = mem_map.write_obj(0xf00u32, 16);
339     ///     assert!(res.is_ok());
340     ///     let num: u32 = mem_map.read_obj_volatile(16).unwrap();
341     ///     assert_eq!(0xf00, num);
342     /// ```
read_obj_volatile<T: FromBytes>(&self, offset: usize) -> Result<T>343     pub fn read_obj_volatile<T: FromBytes>(&self, offset: usize) -> Result<T> {
344         self.mapping.range_end(offset, size_of::<T>())?;
345         // SAFETY:
346         // This is safe because by definition Copy types can have their bits set arbitrarily and
347         // still be valid.
348         unsafe {
349             Ok(read_volatile(
350                 self.as_ptr().add(offset) as *const u8 as *const T
351             ))
352         }
353     }
354 
msync(&self) -> Result<()>355     pub fn msync(&self) -> Result<()> {
356         self.mapping.msync()
357     }
358 
359     /// Flush a region of the MemoryMapping from the system's caching hierarchy.
360     /// There are several uses for flushing:
361     ///
362     /// * Cached memory which the guest may be reading through an uncached mapping:
363     ///
364     ///     Guest reads via an uncached mapping can bypass the cache and directly access main
365     ///     memory. This is outside the memory model of Rust, which means that even with proper
366     ///     synchronization, guest reads via an uncached mapping might not see updates from the
367     ///     host. As such, it is necessary to perform architectural cache maintainance to flush the
368     ///     host writes to main memory.
369     ///
370     ///     Note that this does not support writable uncached guest mappings, as doing so
371     ///     requires invalidating the cache, not flushing the cache.
372     ///
373     /// * Uncached memory which the guest may be writing through a cached mapping:
374     ///
375     ///     Guest writes via a cached mapping of a host's uncached memory may never make it to
376     ///     system/device memory prior to being read. In such cases, explicit flushing of the cached
377     ///     writes is necessary, since other managers of the host's uncached mapping (e.g. DRM) see
378     ///     no need to flush, as they believe all writes would explicitly bypass the caches.
379     ///
380     /// Currently only supported on x86_64 and aarch64. Cannot be supported on 32-bit arm.
flush_region(&self, offset: usize, len: usize) -> Result<()>381     pub fn flush_region(&self, offset: usize, len: usize) -> Result<()> {
382         let addr: *const u8 = self.as_ptr();
383         let size = self.size();
384 
385         // disallow overflow/wrapping ranges and subregion extending beyond mapped range
386         if usize::MAX - size < addr as usize || offset >= size || size - offset < len {
387             return Err(Error::InvalidRange(offset, len, size));
388         }
389 
390         // SAFETY:
391         // Safe because already validated that `next` will be an address in the mapping:
392         //     * mapped region is non-wrapping
393         //     * subregion is bounded within the mapped region
394         let mut next: *const u8 = unsafe { addr.add(offset) };
395 
396         let cacheline_size = get_cacheline_size();
397         let cacheline_count = len.div_ceil(cacheline_size);
398 
399         for _ in 0..cacheline_count {
400             // SAFETY:
401             // Safe because `next` is guaranteed to be within the mapped region (see earlier
402             // validations), and flushing the cache doesn't affect any rust safety properties.
403             unsafe { flush_one(next)? };
404 
405             // SAFETY:
406             // Safe because we never use next if it goes out of the mapped region or overflows its
407             // storage type (based on earlier validations and the loop bounds).
408             next = unsafe { next.add(cacheline_size) };
409         }
410         Ok(())
411     }
412 
413     /// Flush all backing memory for a mapping in an arch-specific manner (see `flush_region()`).
flush_all(&self) -> Result<()>414     pub fn flush_all(&self) -> Result<()> {
415         self.flush_region(0, self.size())
416     }
417 }
418 
419 pub struct MemoryMappingBuilder<'a> {
420     pub(crate) descriptor: Option<&'a dyn AsRawDescriptor>,
421     pub(crate) is_file_descriptor: bool,
422     #[cfg_attr(target_os = "macos", allow(unused))]
423     pub(crate) size: usize,
424     pub(crate) offset: Option<u64>,
425     pub(crate) align: Option<u64>,
426     pub(crate) protection: Option<Protection>,
427     #[cfg_attr(target_os = "macos", allow(unused))]
428     #[cfg_attr(windows, allow(unused))]
429     pub(crate) populate: bool,
430 }
431 
432 /// Builds a MemoryMapping object from the specified arguments.
433 impl<'a> MemoryMappingBuilder<'a> {
434     /// Creates a new builder specifying size of the memory region in bytes.
new(size: usize) -> MemoryMappingBuilder<'a>435     pub fn new(size: usize) -> MemoryMappingBuilder<'a> {
436         MemoryMappingBuilder {
437             descriptor: None,
438             size,
439             is_file_descriptor: false,
440             offset: None,
441             align: None,
442             protection: None,
443             populate: false,
444         }
445     }
446 
447     /// Build the memory mapping given the specified File to mapped memory
448     ///
449     /// Default: Create a new memory mapping.
450     ///
451     /// Note: this is a forward looking interface to accomodate platforms that
452     /// require special handling for file backed mappings.
453     #[allow(clippy::wrong_self_convention, unused_mut)]
from_file(mut self, file: &'a File) -> MemoryMappingBuilder454     pub fn from_file(mut self, file: &'a File) -> MemoryMappingBuilder {
455         // On Windows, files require special handling (next day shipping if possible).
456         self.is_file_descriptor = true;
457 
458         self.descriptor = Some(file as &dyn AsRawDescriptor);
459         self
460     }
461 
462     /// Build the memory mapping given the specified SharedMemory to mapped memory
463     ///
464     /// Default: Create a new memory mapping.
from_shared_memory(mut self, shm: &'a SharedMemory) -> MemoryMappingBuilder465     pub fn from_shared_memory(mut self, shm: &'a SharedMemory) -> MemoryMappingBuilder {
466         self.descriptor = Some(shm as &dyn AsRawDescriptor);
467         self
468     }
469 
470     /// Offset in bytes from the beginning of the mapping to start the mmap.
471     ///
472     /// Default: No offset
offset(mut self, offset: u64) -> MemoryMappingBuilder<'a>473     pub fn offset(mut self, offset: u64) -> MemoryMappingBuilder<'a> {
474         self.offset = Some(offset);
475         self
476     }
477 
478     /// Protection (e.g. readable/writable) of the memory region.
479     ///
480     /// Default: Read/write
protection(mut self, protection: Protection) -> MemoryMappingBuilder<'a>481     pub fn protection(mut self, protection: Protection) -> MemoryMappingBuilder<'a> {
482         self.protection = Some(protection);
483         self
484     }
485 
486     /// Alignment of the memory region mapping in bytes.
487     ///
488     /// Default: No alignment
align(mut self, alignment: u64) -> MemoryMappingBuilder<'a>489     pub fn align(mut self, alignment: u64) -> MemoryMappingBuilder<'a> {
490         self.align = Some(alignment);
491         self
492     }
493 }
494 
495 impl VolatileMemory for MemoryMapping {
get_slice(&self, offset: usize, count: usize) -> VolatileMemoryResult<VolatileSlice>496     fn get_slice(&self, offset: usize, count: usize) -> VolatileMemoryResult<VolatileSlice> {
497         let mem_end = offset
498             .checked_add(count)
499             .ok_or(VolatileMemoryError::Overflow {
500                 base: offset,
501                 offset: count,
502             })?;
503 
504         if mem_end > self.size() {
505             return Err(VolatileMemoryError::OutOfBounds { addr: mem_end });
506         }
507 
508         let new_addr =
509             (self.as_ptr() as usize)
510                 .checked_add(offset)
511                 .ok_or(VolatileMemoryError::Overflow {
512                     base: self.as_ptr() as usize,
513                     offset,
514                 })?;
515 
516         // SAFETY:
517         // Safe because we checked that offset + count was within our range and we only ever hand
518         // out volatile accessors.
519         Ok(unsafe { VolatileSlice::from_raw_parts(new_addr as *mut u8, count) })
520     }
521 }
522 
523 /// A range of memory that can be msynced, for abstracting over different types of memory mappings.
524 ///
525 /// # Safety
526 /// Safe when implementers guarantee `ptr`..`ptr+size` is an mmaped region owned by this object that
527 /// can't be unmapped during the `MappedRegion`'s lifetime.
528 pub unsafe trait MappedRegion: Send + Sync {
529     // SAFETY:
530     /// Returns a pointer to the beginning of the memory region. Should only be
531     /// used for passing this region to ioctls for setting guest memory.
as_ptr(&self) -> *mut u8532     fn as_ptr(&self) -> *mut u8;
533 
534     /// Returns the size of the memory region in bytes.
size(&self) -> usize535     fn size(&self) -> usize;
536 
537     /// Maps `size` bytes starting at `fd_offset` bytes from within the given `fd`
538     /// at `offset` bytes from the start of the region with `prot` protections.
539     /// `offset` must be page aligned.
540     ///
541     /// # Arguments
542     /// * `offset` - Page aligned offset into the arena in bytes.
543     /// * `size` - Size of memory region in bytes.
544     /// * `fd` - File descriptor to mmap from.
545     /// * `fd_offset` - Offset in bytes from the beginning of `fd` to start the mmap.
546     /// * `prot` - Protection (e.g. readable/writable) of the memory region.
add_fd_mapping( &mut self, _offset: usize, _size: usize, _fd: &dyn AsRawDescriptor, _fd_offset: u64, _prot: Protection, ) -> Result<()>547     fn add_fd_mapping(
548         &mut self,
549         _offset: usize,
550         _size: usize,
551         _fd: &dyn AsRawDescriptor,
552         _fd_offset: u64,
553         _prot: Protection,
554     ) -> Result<()> {
555         Err(Error::AddFdMappingIsUnsupported)
556     }
557 
558     /// Remove `size`-byte mapping starting at `offset`.
remove_mapping(&mut self, _offset: usize, _size: usize) -> Result<()>559     fn remove_mapping(&mut self, _offset: usize, _size: usize) -> Result<()> {
560         Err(Error::RemoveMappingIsUnsupported)
561     }
562 }
563 
564 // SAFETY:
565 // Safe because it exclusively forwards calls to a safe implementation.
566 unsafe impl MappedRegion for MemoryMapping {
as_ptr(&self) -> *mut u8567     fn as_ptr(&self) -> *mut u8 {
568         self.mapping.as_ptr()
569     }
570 
size(&self) -> usize571     fn size(&self) -> usize {
572         self.mapping.size()
573     }
574 }
575 
576 #[derive(Debug, PartialEq, Eq)]
577 pub struct ExternalMapping {
578     pub ptr: u64,
579     pub size: usize,
580 }
581 
582 // SAFETY:
583 // `ptr`..`ptr+size` is an mmaped region and is owned by this object. Caller
584 // needs to ensure that the region is not unmapped during the `MappedRegion`'s
585 // lifetime.
586 unsafe impl MappedRegion for ExternalMapping {
587     /// used for passing this region to ioctls for setting guest memory.
as_ptr(&self) -> *mut u8588     fn as_ptr(&self) -> *mut u8 {
589         self.ptr as *mut u8
590     }
591 
592     /// Returns the size of the memory region in bytes.
size(&self) -> usize593     fn size(&self) -> usize {
594         self.size
595     }
596 }
597