236 lines
7.4 KiB
Rust
236 lines
7.4 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
//! This module defines struct `ProcessVm`
|
|
//! to represent the layout of user space process virtual memory.
|
|
//!
|
|
//! The `ProcessVm` struct contains `Vmar`,
|
|
//! which stores all existing memory mappings.
|
|
//! The `Vm` also contains
|
|
//! the basic info of process level vm segments,
|
|
//! like init stack and heap.
|
|
|
|
mod heap;
|
|
mod init_stack;
|
|
|
|
#[cfg(target_arch = "riscv64")]
|
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
use ostd::{sync::MutexGuard, task::disable_preempt};
|
|
|
|
pub use self::{
|
|
heap::{Heap, USER_HEAP_SIZE_LIMIT},
|
|
init_stack::{
|
|
INIT_STACK_SIZE, InitStack, InitStackReader, MAX_LEN_STRING_ARG, MAX_NR_STRING_ARGS,
|
|
aux_vec::{AuxKey, AuxVec},
|
|
},
|
|
};
|
|
use crate::{fs::fs_resolver::PathOrInode, prelude::*, vm::vmar::Vmar};
|
|
|
|
/*
|
|
* The user's virtual memory space layout looks like below.
|
|
* TODO: The layout of the userheap does not match the current implementation,
|
|
* And currently the initial program break is a fixed value.
|
|
*
|
|
* (high address)
|
|
* +---------------------+ <------+ The top of Vmar, which is the highest address usable
|
|
* | | Randomly padded pages
|
|
* +---------------------+ <------+ The base of the initial user stack
|
|
* | User stack |
|
|
* | |
|
|
* +---------||----------+ <------+ The user stack limit, can be extended lower
|
|
* | \/ |
|
|
* | ... |
|
|
* | |
|
|
* | MMAP Spaces |
|
|
* | |
|
|
* | ... |
|
|
* | /\ |
|
|
* +---------||----------+ <------+ The current program break
|
|
* | User heap |
|
|
* | |
|
|
* +---------------------+ <------+ The original program break
|
|
* | | Randomly padded pages
|
|
* +---------------------+ <------+ The end of the program's last segment
|
|
* | |
|
|
* | Loaded segments |
|
|
* | .text, .data, .bss |
|
|
* | , etc. |
|
|
* | |
|
|
* +---------------------+ <------+ The bottom of Vmar at 0x1_0000
|
|
* | | 64 KiB unusable space
|
|
* +---------------------+
|
|
* (low address)
|
|
*/
|
|
|
|
/// The process user space virtual memory
|
|
pub struct ProcessVm {
|
|
/// The initial portion of the main stack of a process.
|
|
init_stack: InitStack,
|
|
/// The user heap
|
|
heap: Heap,
|
|
/// The executable `PathOrInode`.
|
|
executable_file: PathOrInode,
|
|
/// The base address for vDSO segment
|
|
#[cfg(target_arch = "riscv64")]
|
|
vdso_base: AtomicUsize,
|
|
}
|
|
|
|
impl ProcessVm {
|
|
/// Creates a new `ProcessVm` without mapping anything.
|
|
fn new(executable_file: PathOrInode) -> Self {
|
|
Self {
|
|
init_stack: InitStack::new(),
|
|
heap: Heap::new(),
|
|
executable_file,
|
|
#[cfg(target_arch = "riscv64")]
|
|
vdso_base: AtomicUsize::new(0),
|
|
}
|
|
}
|
|
|
|
/// Creates a new `ProcessVm` with identical contents of an existing one.
|
|
pub fn fork_from(process_vm: &Self) -> Self {
|
|
Self {
|
|
init_stack: process_vm.init_stack.clone(),
|
|
heap: process_vm.heap.clone(),
|
|
executable_file: process_vm.executable_file.clone(),
|
|
#[cfg(target_arch = "riscv64")]
|
|
vdso_base: AtomicUsize::new(process_vm.vdso_base.load(Ordering::Relaxed)),
|
|
}
|
|
}
|
|
|
|
/// Returns the initial portion of the main stack of a process.
|
|
pub fn init_stack(&self) -> &InitStack {
|
|
&self.init_stack
|
|
}
|
|
|
|
/// Returns the user heap.
|
|
pub fn heap(&self) -> &Heap {
|
|
&self.heap
|
|
}
|
|
|
|
/// Returns a reference to the executable `PathOrInode`.
|
|
pub fn executable_file(&self) -> &PathOrInode {
|
|
&self.executable_file
|
|
}
|
|
|
|
/// Maps and writes the initial portion of the main stack of a process.
|
|
pub(super) fn map_and_write_init_stack(
|
|
&self,
|
|
vmar: &Vmar,
|
|
argv: Vec<CString>,
|
|
envp: Vec<CString>,
|
|
aux_vec: AuxVec,
|
|
) -> Result<()> {
|
|
self.init_stack().map_and_write(vmar, argv, envp, aux_vec)
|
|
}
|
|
|
|
/// Returns the base address for vDSO segment.
|
|
#[cfg(target_arch = "riscv64")]
|
|
pub(super) fn vdso_base(&self) -> Vaddr {
|
|
self.vdso_base.load(Ordering::Relaxed)
|
|
}
|
|
|
|
/// Sets the base address for vDSO segment.
|
|
#[cfg(target_arch = "riscv64")]
|
|
pub(super) fn set_vdso_base(&self, addr: Vaddr) {
|
|
self.vdso_base.store(addr, Ordering::Relaxed);
|
|
}
|
|
}
|
|
|
|
/// A guard to the [`Vmar`] used by a process.
|
|
///
|
|
/// It is bound to a [`Process`] and can only be obtained from
|
|
/// the [`Process::lock_vmar`] method.
|
|
///
|
|
/// [`Process`]: super::process::Process
|
|
/// [`Process::lock_vmar`]: super::process::Process::lock_vmar
|
|
pub struct ProcessVmarGuard<'a> {
|
|
inner: MutexGuard<'a, Option<Arc<Vmar>>>,
|
|
}
|
|
|
|
impl<'a> ProcessVmarGuard<'a> {
|
|
/// Creates a new VMAR guard from the mutex guard.
|
|
///
|
|
/// This method should only used by [`Process::lock_vmar`].
|
|
///
|
|
/// [`Process::lock_vmar`]: super::process::Process::lock_vmar
|
|
pub(super) fn new(inner: MutexGuard<'a, Option<Arc<Vmar>>>) -> Self {
|
|
Self { inner }
|
|
}
|
|
|
|
/// Unwraps and returns a reference to the process VMAR.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// This method will panic if the process has exited and its VMAR has been dropped.
|
|
pub fn unwrap(&self) -> &Vmar {
|
|
self.inner.as_ref().unwrap()
|
|
}
|
|
|
|
/// Returns a reference to the process VMAR if it exists.
|
|
///
|
|
/// Returns `None` if the process has exited and its VMAR has been dropped.
|
|
pub fn as_ref(&self) -> Option<&Vmar> {
|
|
self.inner.as_ref().map(|v| &**v)
|
|
}
|
|
|
|
/// Sets a new VMAR for the binding process.
|
|
///
|
|
/// If the `new_vmar` is `None`, this method will remove the
|
|
/// current VMAR.
|
|
pub(super) fn set_vmar(&mut self, new_vmar: Option<Arc<Vmar>>) {
|
|
*self.inner = new_vmar;
|
|
}
|
|
|
|
/// Duplicates a new VMAR from the binding process.
|
|
///
|
|
/// This method should only be used to clone the VMAR in the `Process`
|
|
/// and store it in the `ThreadLocal`.
|
|
pub(super) fn dup_vmar(&self) -> Option<Arc<Vmar>> {
|
|
self.inner.as_ref().cloned()
|
|
}
|
|
|
|
/// Returns a reader for reading contents from
|
|
/// the initial portion of the main stack of a process.
|
|
///
|
|
/// Returns `None` if the process has exited and its VMAR has been dropped.
|
|
pub fn init_stack_reader(&self) -> Option<InitStackReader<'_>> {
|
|
self.as_ref()
|
|
.map(|vmar| vmar.process_vm().init_stack.reader(vmar))
|
|
}
|
|
}
|
|
|
|
/// Creates a new VMAR and map the heap.
|
|
///
|
|
/// This method should only be used to create a VMAR for the init process.
|
|
pub(super) fn new_vmar_and_map(executable_file: PathOrInode) -> Arc<Vmar> {
|
|
let new_vmar = Vmar::new(ProcessVm::new(executable_file));
|
|
new_vmar
|
|
.process_vm()
|
|
.heap()
|
|
.alloc_and_map(new_vmar.as_ref())
|
|
.unwrap();
|
|
new_vmar
|
|
}
|
|
|
|
/// Unshares and renews the [`Vmar`] of the current process.
|
|
pub(super) fn unshare_and_renew_vmar(
|
|
ctx: &Context,
|
|
vmar: &mut ProcessVmarGuard,
|
|
executable_file: PathOrInode,
|
|
) {
|
|
let new_vmar = Vmar::new(ProcessVm::new(executable_file));
|
|
let guard = disable_preempt();
|
|
*ctx.thread_local.vmar().borrow_mut() = Some(new_vmar.clone());
|
|
new_vmar.vm_space().activate();
|
|
vmar.set_vmar(Some(new_vmar));
|
|
drop(guard);
|
|
|
|
let new_vmar = vmar.unwrap();
|
|
new_vmar
|
|
.process_vm()
|
|
.heap()
|
|
.alloc_and_map(new_vmar)
|
|
.unwrap();
|
|
}
|