blob: 746d4b13a89ae7cc8dfc80758a23e3b5cab7e330 [file] [log] [blame] [edit]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fuchsia_zircon::{self as zx, HandleBased, Status};
use lazy_static::lazy_static;
use parking_lot::RwLock;
use zerocopy::{AsBytes, FromBytes};
use crate::uapi::*;
lazy_static! {
pub static ref PAGE_SIZE: u64 = zx::system_get_page_size() as u64;
}
pub struct ProgramBreak {
vmar: zx::Vmar,
vmo: zx::Vmo,
// These base address at which the data segment is mapped.
base: UserAddress,
// The current program break.
//
// The addresses from [base, current.round_up(*PAGE_SIZE)) are mapped into the
// client address space from the underlying |vmo|.
current: UserAddress,
}
impl Default for ProgramBreak {
fn default() -> ProgramBreak {
return ProgramBreak {
vmar: zx::Handle::invalid().into(),
vmo: zx::Handle::invalid().into(),
base: UserAddress::default(),
current: UserAddress::default(),
};
}
}
const PROGRAM_BREAK_LIMIT: u64 = 64 * 1024 * 1024;
pub struct MemoryManager {
/// A handle to the underlying Zircon process object.
// TODO: Remove this handle once we can read and write process memory directly.
pub process: zx::Process,
pub root_vmar: zx::Vmar,
program_break: RwLock<ProgramBreak>,
}
impl MemoryManager {
pub fn new(process: zx::Process, root_vmar: zx::Vmar) -> Self {
MemoryManager { process, root_vmar, program_break: RwLock::new(ProgramBreak::default()) }
}
pub fn set_program_break(&self, addr: UserAddress) -> Result<UserAddress, Status> {
let mut program_break = self.program_break.write();
if program_break.vmar.is_invalid_handle() {
// TODO: This allocation places the program break at a random location in the
// child's address space. However, we're supposed to put this memory directly
// above the last segment of the ELF for the child.
let (vmar, raw_addr) = self.root_vmar.allocate(
0,
PROGRAM_BREAK_LIMIT as usize,
zx::VmarFlags::CAN_MAP_SPECIFIC
| zx::VmarFlags::CAN_MAP_READ
| zx::VmarFlags::CAN_MAP_WRITE,
)?;
let vmo = zx::Vmo::create(PROGRAM_BREAK_LIMIT)?;
program_break.vmar = vmar;
program_break.vmo = vmo;
program_break.base = UserAddress::from(raw_addr);
program_break.current = program_break.base;
}
if addr < program_break.base || addr > program_break.base + PROGRAM_BREAK_LIMIT {
// The requested program break is out-of-range. We're supposed to simply
// return the current program break.
return Ok(program_break.current);
}
if addr < program_break.current {
// The client wishes to free up memory. Adjust the program break to the new
// location.
let aligned_previous = program_break.current.round_up(*PAGE_SIZE);
program_break.current = addr;
let aligned_current = program_break.current.round_up(*PAGE_SIZE);
let len = aligned_current - aligned_previous;
if len > 0 {
// If we crossed a page boundary, we can actually unmap and free up the
// unused pages.
let offset = aligned_previous - program_break.base;
unsafe { program_break.vmar.unmap(aligned_current.ptr(), len)? };
program_break.vmo.op_range(zx::VmoOp::DECOMMIT, offset as u64, len as u64)?;
}
return Ok(program_break.current);
}
// Otherwise, we've been asked to increase the page break.
let aligned_previous = program_break.current.round_up(*PAGE_SIZE);
program_break.current = addr;
let aligned_current = program_break.current.round_up(*PAGE_SIZE);
let len = aligned_current - aligned_previous;
if len > 0 {
// If we crossed a page boundary, we need to map more of the underlying VMO
// into the client's address space.
let offset = aligned_previous - program_break.base;
program_break.vmar.map(
offset,
&program_break.vmo,
offset as u64,
len,
zx::VmarFlags::PERM_READ
| zx::VmarFlags::PERM_WRITE
| zx::VmarFlags::REQUIRE_NON_RESIZABLE
| zx::VmarFlags::SPECIFIC,
)?;
}
return Ok(program_break.current);
}
pub fn read_memory(&self, addr: UserAddress, bytes: &mut [u8]) -> Result<(), Errno> {
let actual = self.process.read_memory(addr.ptr(), bytes).map_err(|_| EFAULT)?;
if actual != bytes.len() {
return Err(EFAULT);
}
Ok(())
}
#[allow(dead_code)] // Not used yet.
pub fn read_object<T: AsBytes + FromBytes>(
&self,
user: UserRef<T>,
object: &mut T,
) -> Result<(), Errno> {
self.read_memory(user.addr(), object.as_bytes_mut())
}
pub fn read_c_string<'a>(
&self,
string: UserCString,
buffer: &'a mut [u8],
) -> Result<&'a [u8], Errno> {
let actual = self.process.read_memory(string.ptr(), buffer).map_err(|_| EFAULT)?;
let buffer = &mut buffer[..actual];
let null_index = memchr::memchr(b'\0', buffer).ok_or(ENAMETOOLONG)?;
Ok(&buffer[..null_index])
}
pub fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<(), Errno> {
let actual = self.process.write_memory(addr.ptr(), bytes).map_err(|_| EFAULT)?;
if actual != bytes.len() {
return Err(EFAULT);
}
Ok(())
}
pub fn write_object<T: AsBytes + FromBytes>(
&self,
user: UserRef<T>,
object: &T,
) -> Result<(), Errno> {
self.write_memory(user.addr(), &object.as_bytes())
}
}