blob: 2e5ce562a0c877ab664f1875028f9830887e1bc4 [file] [log] [blame]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Implement support for allocating large page aligned buffers via the mmap()
//! system call. Loosely adapted from https://github.com/rbranson/rust-mmap, who
//! got it from the rust standard library before it was removed.
use libc::c_void;
use anyhow::{Context, Result};
use crate::hiberutil::{get_page_size, HibernateError};
pub struct MmapBuffer {
data: *mut u8,
len: usize,
}
impl MmapBuffer {
/// Mmap() up a new anonymous buffer of at least the given size.
pub fn new(min_len: usize) -> Result<Self> {
let page_size = get_page_size();
// Align the size up to a page. The page size is assumed to be a power
// of two.
let len = (min_len + page_size - 1) & !(page_size - 1);
let addr: *const u8 = std::ptr::null();
let prot = libc::PROT_READ | libc::PROT_WRITE;
let flags = libc::MAP_PRIVATE | libc::MAP_ANON;
// Safe because anonymous mmap buffers are always returned zeroed.
let r = unsafe { libc::mmap(addr as *mut c_void, len as libc::size_t, prot, flags, -1, 0) };
if r == libc::MAP_FAILED {
Err(HibernateError::MmapError(sys_util::Error::last()))
.context("Cannot create MmapBuffer")
} else {
Ok(Self {
data: r as *mut u8,
len,
})
}
}
/// Return the effective length of the buffer, which may be more than what
/// the caller originally asked for.
pub fn len(&self) -> usize {
self.len
}
/// Return the buffer contents as an immutable u8 slice.
pub fn u8_slice(&self) -> &[u8] {
// This is safe because the buffer is known to be this large.
unsafe { std::slice::from_raw_parts(self.data, self.len) }
}
/// Return the buffer contents as a mutable u8 slice.
pub fn u8_slice_mut(&mut self) -> &mut [u8] {
// This is safe because the buffer is known to be this large.
unsafe { std::slice::from_raw_parts_mut(self.data, self.len) }
}
}
impl Drop for MmapBuffer {
fn drop(&mut self) {
if self.len == 0 {
return;
}
// This is safe because munmap() doesn't do anything other than release
// the memory.
unsafe {
libc::munmap(self.data as *mut c_void, self.len as libc::size_t);
}
}
}
// It's safe for MmapBuffer to implement Send despite using a raw pointer
// because the object behind that pointer contains to references to thread
// local variables.
unsafe impl Send for MmapBuffer {}