mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-17 02:26:46 +00:00
Merge pull request #52 from StevenJiang1110/vm-cap
Implement vm capability and refactor current codes with vm capability
This commit is contained in:
commit
1d401fd8fc
@ -2,5 +2,5 @@
|
|||||||
We don't include the source code of busybox here since the source code is really large. The busybox can be compiled with following commands.
|
We don't include the source code of busybox here since the source code is really large. The busybox can be compiled with following commands.
|
||||||
|
|
||||||
After download the source code of busybox 1.35.0 and unzip, then cd to the directory of busybox
|
After download the source code of busybox 1.35.0 and unzip, then cd to the directory of busybox
|
||||||
1. ```make defconfig #set config to default```
|
1. `make defconfig`. We set all config as default.
|
||||||
2. change the line in .config, `#CONFIG_STATIC is not set` => `CONFIG_STATIC=y`. We need a static-compiled busybox
|
2. change the line in .config: `#CONFIG_STATIC is not set` => `CONFIG_STATIC=y`. We need a static-linked busybox binary since we does not support dynamic linking now.
|
@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:cea612414dc19fcd03b563607ea9a453a3d3390b9f3b229ef8e56b08e4d4c8c5
|
oid sha256:9bc1642390b9dc38ecc058240e529401c38aa8bb9a86bad3615e4bdad505fa8c
|
||||||
size 9592
|
size 9592
|
||||||
|
@ -6,6 +6,7 @@ use core::mem::MaybeUninit;
|
|||||||
|
|
||||||
use crate::debug;
|
use crate::debug;
|
||||||
use crate::trap::{CalleeRegs, CallerRegs, SyscallFrame, TrapFrame};
|
use crate::trap::{CalleeRegs, CallerRegs, SyscallFrame, TrapFrame};
|
||||||
|
use crate::x86_64_util::rdfsbase;
|
||||||
use pod::Pod;
|
use pod::Pod;
|
||||||
|
|
||||||
/// Defines a CPU-local variable.
|
/// Defines a CPU-local variable.
|
||||||
@ -156,7 +157,7 @@ impl From<TrapFrame> for CpuContext {
|
|||||||
rip: trap.rip,
|
rip: trap.rip,
|
||||||
rflag: trap.rflags,
|
rflag: trap.rflags,
|
||||||
},
|
},
|
||||||
fs_base: 0,
|
fs_base: rdfsbase(),
|
||||||
fp_regs: FpRegs::default(),
|
fp_regs: FpRegs::default(),
|
||||||
trap_information: TrapInformation {
|
trap_information: TrapInformation {
|
||||||
cr2: trap.cr2,
|
cr2: trap.cr2,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use super::{page_table::PageTable, *};
|
use super::{page_table::PageTable, *};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::vm::VmIo;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::PAGE_SIZE,
|
config::PAGE_SIZE,
|
||||||
mm::address::is_aligned,
|
mm::address::is_aligned,
|
||||||
|
@ -7,5 +7,6 @@ pub(crate) use alloc::sync::Arc;
|
|||||||
pub(crate) use alloc::vec::Vec;
|
pub(crate) use alloc::vec::Vec;
|
||||||
pub(crate) use core::any::Any;
|
pub(crate) use core::any::Any;
|
||||||
|
|
||||||
|
pub use crate::debug;
|
||||||
pub(crate) use crate::util::AlignExt;
|
pub(crate) use crate::util::AlignExt;
|
||||||
pub use crate::vm::{Paddr, Vaddr};
|
pub use crate::vm::{Paddr, Vaddr};
|
||||||
|
@ -38,7 +38,7 @@ __trap_entry:
|
|||||||
push r12
|
push r12
|
||||||
push rbp
|
push rbp
|
||||||
push rbx
|
push rbx
|
||||||
mov rdi, 0
|
# mov rdi, 0
|
||||||
push rdi
|
push rdi
|
||||||
save
|
save
|
||||||
# save cr2
|
# save cr2
|
||||||
|
@ -4,6 +4,7 @@ use crate::{config::PAGE_SIZE, mm::address::PhysAddr, prelude::*, Error};
|
|||||||
use pod::Pod;
|
use pod::Pod;
|
||||||
|
|
||||||
use super::VmIo;
|
use super::VmIo;
|
||||||
|
use alloc::vec;
|
||||||
|
|
||||||
use crate::mm::PhysFrame;
|
use crate::mm::PhysFrame;
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ use crate::mm::PhysFrame;
|
|||||||
/// type to represent a series of page frames is convenient because,
|
/// type to represent a series of page frames is convenient because,
|
||||||
/// more often than not, one needs to operate on a batch of frames rather
|
/// more often than not, one needs to operate on a batch of frames rather
|
||||||
/// a single frame.
|
/// a single frame.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct VmFrameVec(Vec<VmFrame>);
|
pub struct VmFrameVec(Vec<VmFrame>);
|
||||||
|
|
||||||
impl VmFrameVec {
|
impl VmFrameVec {
|
||||||
@ -31,7 +33,7 @@ impl VmFrameVec {
|
|||||||
let mut frame_list = Vec::new();
|
let mut frame_list = Vec::new();
|
||||||
for i in 0..page_size {
|
for i in 0..page_size {
|
||||||
let vm_frame = if let Some(paddr) = options.paddr {
|
let vm_frame = if let Some(paddr) = options.paddr {
|
||||||
VmFrame::alloc_with_paddr(paddr)
|
VmFrame::alloc_with_paddr(paddr + i * PAGE_SIZE)
|
||||||
} else {
|
} else {
|
||||||
VmFrame::alloc()
|
VmFrame::alloc()
|
||||||
};
|
};
|
||||||
@ -43,6 +45,11 @@ impl VmFrameVec {
|
|||||||
Ok(Self(frame_list))
|
Ok(Self(frame_list))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns an empty vmframe vec
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
/// Pushs a new frame to the collection.
|
/// Pushs a new frame to the collection.
|
||||||
pub fn push(&mut self, new_frame: VmFrame) {
|
pub fn push(&mut self, new_frame: VmFrame) {
|
||||||
self.0.push(new_frame);
|
self.0.push(new_frame);
|
||||||
@ -73,6 +80,11 @@ impl VmFrameVec {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// zero all internal vm frames
|
||||||
|
pub fn zero(&self) {
|
||||||
|
self.0.iter().for_each(|vm_frame| vm_frame.zero())
|
||||||
|
}
|
||||||
|
|
||||||
/// Truncate some frames.
|
/// Truncate some frames.
|
||||||
///
|
///
|
||||||
/// If `new_len >= self.len()`, then this method has no effect.
|
/// If `new_len >= self.len()`, then this method has no effect.
|
||||||
@ -88,6 +100,11 @@ impl VmFrameVec {
|
|||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return IntoIterator for internal frames
|
||||||
|
pub fn into_iter(self) -> alloc::vec::IntoIter<VmFrame> {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of frames.
|
/// Returns the number of frames.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
@ -104,6 +121,10 @@ impl VmFrameVec {
|
|||||||
pub fn nbytes(&self) -> usize {
|
pub fn nbytes(&self) -> usize {
|
||||||
self.0.len() * PAGE_SIZE
|
self.0.len() * PAGE_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_one_frame(frame: VmFrame) -> Self {
|
||||||
|
Self(vec![frame])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmIo for VmFrameVec {
|
impl VmIo for VmFrameVec {
|
||||||
@ -224,7 +245,7 @@ impl VmAllocOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
/// A handle to a page frame.
|
/// A handle to a page frame.
|
||||||
///
|
///
|
||||||
/// An instance of `VmFrame` is a handle to a page frame (a physical memory
|
/// An instance of `VmFrame` is a handle to a page frame (a physical memory
|
||||||
@ -239,6 +260,14 @@ pub struct VmFrame {
|
|||||||
pub(crate) physical_frame: Arc<PhysFrame>,
|
pub(crate) physical_frame: Arc<PhysFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for VmFrame {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
physical_frame: self.physical_frame.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl VmFrame {
|
impl VmFrame {
|
||||||
/// Creates a new VmFrame.
|
/// Creates a new VmFrame.
|
||||||
///
|
///
|
||||||
@ -288,6 +317,11 @@ impl VmFrame {
|
|||||||
self.physical_frame.start_pa().0
|
self.physical_frame.start_pa().0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// fill the frame with zero
|
||||||
|
pub fn zero(&self) {
|
||||||
|
unsafe { core::ptr::write_bytes(self.start_pa().kvaddr().as_ptr(), 0, PAGE_SIZE) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_pa(&self) -> PhysAddr {
|
pub fn start_pa(&self) -> PhysAddr {
|
||||||
self.physical_frame.start_pa()
|
self.physical_frame.start_pa()
|
||||||
}
|
}
|
||||||
|
@ -22,15 +22,17 @@ use super::VmIo;
|
|||||||
/// A newly-created `VmSpace` is not backed by any physical memory pages.
|
/// A newly-created `VmSpace` is not backed by any physical memory pages.
|
||||||
/// To provide memory pages for a `VmSpace`, one can allocate and map
|
/// To provide memory pages for a `VmSpace`, one can allocate and map
|
||||||
/// physical memory (`VmFrames`) to the `VmSpace`.
|
/// physical memory (`VmFrames`) to the `VmSpace`.
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct VmSpace {
|
pub struct VmSpace {
|
||||||
memory_set: Mutex<MemorySet>,
|
memory_set: Arc<Mutex<MemorySet>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmSpace {
|
impl VmSpace {
|
||||||
/// Creates a new VM address space.
|
/// Creates a new VM address space.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
memory_set: Mutex::new(MemorySet::new()),
|
memory_set: Arc::new(Mutex::new(MemorySet::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Activate the page table, load root physical address to cr3
|
/// Activate the page table, load root physical address to cr3
|
||||||
@ -55,6 +57,7 @@ impl VmSpace {
|
|||||||
if options.addr.is_none() {
|
if options.addr.is_none() {
|
||||||
return Err(Error::InvalidArgs);
|
return Err(Error::InvalidArgs);
|
||||||
}
|
}
|
||||||
|
// debug!("map to vm space: 0x{:x}", options.addr.unwrap());
|
||||||
self.memory_set.lock().map(MapArea::new(
|
self.memory_set.lock().map(MapArea::new(
|
||||||
VirtAddr(options.addr.unwrap()),
|
VirtAddr(options.addr.unwrap()),
|
||||||
frames.len() * PAGE_SIZE,
|
frames.len() * PAGE_SIZE,
|
||||||
@ -108,15 +111,6 @@ impl Default for VmSpace {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for VmSpace {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let memory_set = self.memory_set.lock().clone();
|
|
||||||
VmSpace {
|
|
||||||
memory_set: Mutex::new(memory_set),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VmIo for VmSpace {
|
impl VmIo for VmSpace {
|
||||||
fn read_bytes(&self, vaddr: usize, buf: &mut [u8]) -> Result<()> {
|
fn read_bytes(&self, vaddr: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
self.memory_set.lock().read_bytes(vaddr, buf)
|
self.memory_set.lock().read_bytes(vaddr, buf)
|
||||||
@ -129,11 +123,16 @@ impl VmIo for VmSpace {
|
|||||||
|
|
||||||
/// Options for mapping physical memory pages into a VM address space.
|
/// Options for mapping physical memory pages into a VM address space.
|
||||||
/// See `VmSpace::map`.
|
/// See `VmSpace::map`.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct VmMapOptions {
|
pub struct VmMapOptions {
|
||||||
/// start virtual address
|
/// start virtual address
|
||||||
addr: Option<Vaddr>,
|
addr: Option<Vaddr>,
|
||||||
|
/// map align
|
||||||
|
align: usize,
|
||||||
/// permission
|
/// permission
|
||||||
perm: VmPerm,
|
perm: VmPerm,
|
||||||
|
/// can overwrite
|
||||||
|
can_overwrite: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmMapOptions {
|
impl VmMapOptions {
|
||||||
@ -141,7 +140,9 @@ impl VmMapOptions {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
addr: None,
|
addr: None,
|
||||||
|
align: PAGE_SIZE,
|
||||||
perm: VmPerm::empty(),
|
perm: VmPerm::empty(),
|
||||||
|
can_overwrite: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +153,8 @@ impl VmMapOptions {
|
|||||||
///
|
///
|
||||||
/// The default value of this option is the page size.
|
/// The default value of this option is the page size.
|
||||||
pub fn align(&mut self, align: usize) -> &mut Self {
|
pub fn align(&mut self, align: usize) -> &mut Self {
|
||||||
todo!()
|
self.align = align;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the permissions of the mapping, which affects whether
|
/// Sets the permissions of the mapping, which affects whether
|
||||||
@ -181,7 +183,8 @@ impl VmMapOptions {
|
|||||||
///
|
///
|
||||||
/// The default value of this option is `false`.
|
/// The default value of this option is `false`.
|
||||||
pub fn can_overwrite(&mut self, can_overwrite: bool) -> &mut Self {
|
pub fn can_overwrite(&mut self, can_overwrite: bool) -> &mut Self {
|
||||||
todo!()
|
self.can_overwrite = can_overwrite;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +211,20 @@ impl From<core::ffi::FromBytesWithNulError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Error> for jinux_frame::Error {
|
||||||
|
fn from(error: Error) -> Self {
|
||||||
|
match error.errno {
|
||||||
|
Errno::EACCES => jinux_frame::Error::AccessDenied,
|
||||||
|
Errno::EIO => jinux_frame::Error::IoError,
|
||||||
|
Errno::ENOMEM => jinux_frame::Error::NoMemory,
|
||||||
|
Errno::EFAULT => jinux_frame::Error::PageFault,
|
||||||
|
Errno::EINVAL => jinux_frame::Error::InvalidArgs,
|
||||||
|
Errno::EBUSY => jinux_frame::Error::NotEnoughResources,
|
||||||
|
_ => jinux_frame::Error::InvalidArgs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<alloc::ffi::NulError> for Error {
|
impl From<alloc::ffi::NulError> for Error {
|
||||||
fn from(_: alloc::ffi::NulError) -> Self {
|
fn from(_: alloc::ffi::NulError) -> Self {
|
||||||
Error::with_message(Errno::E2BIG, "Cannot find null in cstring")
|
Error::with_message(Errno::E2BIG, "Cannot find null in cstring")
|
||||||
|
@ -18,7 +18,7 @@ macro_rules! define_fcntl_cmd {
|
|||||||
fn try_from(value: i32) -> Result<Self> {
|
fn try_from(value: i32) -> Result<Self> {
|
||||||
match value {
|
match value {
|
||||||
$($name => Ok(FcntlCmd::$name),)*
|
$($name => Ok(FcntlCmd::$name),)*
|
||||||
_ => return_errno!(Errno::EINVAL),
|
_ => return_errno_with_message!(Errno::EINVAL, "Unknown fcntl cmd"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,9 @@ impl FileTable {
|
|||||||
self.table.remove(&fd);
|
self.table.remove(&fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_file(&self, fd: FileDescripter) -> Option<&Arc<dyn File>> {
|
pub fn get_file(&self, fd: FileDescripter) -> Result<&Arc<dyn File>> {
|
||||||
self.table.get(&fd)
|
self.table
|
||||||
|
.get(&fd)
|
||||||
|
.ok_or(Error::with_message(Errno::EBADF, "fd not exits"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
#![feature(btree_drain_filter)]
|
#![feature(btree_drain_filter)]
|
||||||
#![feature(const_option)]
|
#![feature(const_option)]
|
||||||
#![feature(extend_one)]
|
#![feature(extend_one)]
|
||||||
|
// FIXME: This feature is used to support vm capbility now as a work around.
|
||||||
|
// Since this is an incomplete feature, use this feature is unsafe.
|
||||||
|
// We should find a proper method to replace this feature with min_specialization, which is a sound feature.
|
||||||
|
#![feature(specialization)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -31,7 +35,6 @@ extern crate alloc;
|
|||||||
pub mod driver;
|
pub mod driver;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
mod memory;
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
mod process;
|
mod process;
|
||||||
pub mod rights;
|
pub mod rights;
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
use crate::prelude::*;
|
|
||||||
use jinux_frame::vm::VmIo;
|
|
||||||
use pod::Pod;
|
|
||||||
|
|
||||||
pub mod vm_page;
|
|
||||||
|
|
||||||
/// copy bytes from user space of current process. The bytes len is the len of dest.
|
|
||||||
pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) -> Result<()> {
|
|
||||||
let current = current!();
|
|
||||||
let vm_space = current.vm_space().ok_or(Error::with_message(
|
|
||||||
Errno::ESRCH,
|
|
||||||
"[Internal error]Current should have vm space to copy bytes from user",
|
|
||||||
))?;
|
|
||||||
vm_space.read_bytes(src, dest)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// copy val (Plain of Data type) from user space of current process.
|
|
||||||
pub fn read_val_from_user<T: Pod>(src: Vaddr) -> Result<T> {
|
|
||||||
let current = current!();
|
|
||||||
let vm_space = current.vm_space().ok_or(Error::with_message(
|
|
||||||
Errno::ESRCH,
|
|
||||||
"[Internal error]Current should have vm space to copy val from user",
|
|
||||||
))?;
|
|
||||||
Ok(vm_space.read_val(src)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// write bytes from user space of current process. The bytes len is the len of src.
|
|
||||||
pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) -> Result<()> {
|
|
||||||
let current = current!();
|
|
||||||
let vm_space = current.vm_space().ok_or(Error::with_message(
|
|
||||||
Errno::ESRCH,
|
|
||||||
"[Internal error]Current should have vm space to write bytes to user",
|
|
||||||
))?;
|
|
||||||
vm_space.write_bytes(dest, src)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// write val (Plain of Data type) to user space of current process.
|
|
||||||
pub fn write_val_to_user<T: Pod>(dest: Vaddr, val: &T) -> Result<()> {
|
|
||||||
let current = current!();
|
|
||||||
let vm_space = current.vm_space().ok_or(Error::with_message(
|
|
||||||
Errno::ESRCH,
|
|
||||||
"[Internal error]Current should have vm space to write val to user",
|
|
||||||
))?;
|
|
||||||
vm_space.write_val(dest, val)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// read a cstring from user, the length of cstring should not exceed max_len(include null byte)
|
|
||||||
pub fn read_cstring_from_user(addr: Vaddr, max_len: usize) -> Result<CString> {
|
|
||||||
let mut buffer = vec![0u8; max_len];
|
|
||||||
read_bytes_from_user(addr, &mut buffer)?;
|
|
||||||
Ok(CString::from(CStr::from_bytes_until_nul(&buffer)?))
|
|
||||||
}
|
|
@ -1,160 +0,0 @@
|
|||||||
//! A Page in virtual address space
|
|
||||||
use crate::prelude::*;
|
|
||||||
use core::ops::Range;
|
|
||||||
use jinux_frame::vm::{VmAllocOptions, VmFrameVec, VmIo, VmMapOptions, VmPerm, VmSpace};
|
|
||||||
|
|
||||||
/// A set of **CONTINUOUS** virtual pages in VmSpace
|
|
||||||
pub struct VmPageRange {
|
|
||||||
start_page: VmPage,
|
|
||||||
end_page: VmPage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VmPageRange {
|
|
||||||
/// create a set of pages containing virtual address range [a, b)
|
|
||||||
pub const fn new_range(vaddr_range: Range<Vaddr>) -> Self {
|
|
||||||
let start_page = VmPage::containing_address(vaddr_range.start);
|
|
||||||
let end_page = VmPage::containing_address(vaddr_range.end - 1);
|
|
||||||
Self {
|
|
||||||
start_page,
|
|
||||||
end_page,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn new_page_range(start_page: VmPage, end_page: VmPage) -> Self {
|
|
||||||
Self {
|
|
||||||
start_page,
|
|
||||||
end_page,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the page containing the specific vaddr
|
|
||||||
pub const fn containing_address(vaddr: Vaddr) -> Self {
|
|
||||||
let page = VmPage::containing_address(vaddr);
|
|
||||||
Self {
|
|
||||||
start_page: page,
|
|
||||||
end_page: page,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn start_address(&self) -> Vaddr {
|
|
||||||
self.start_page.start_address()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the address right after the end page
|
|
||||||
pub const fn end_address(&self) -> Vaddr {
|
|
||||||
self.end_page.start_address() + PAGE_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
/// allocate a set of physical frames and map self to frames
|
|
||||||
pub fn map(&mut self, vm_space: &VmSpace, vm_perm: VmPerm) {
|
|
||||||
let options = VmAllocOptions::new(self.len());
|
|
||||||
let frames = VmFrameVec::allocate(&options).expect("allocate frame error");
|
|
||||||
self.map_to(vm_space, frames, vm_perm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// map self to a set of zeroed frames
|
|
||||||
pub fn map_zeroed(&self, vm_space: &VmSpace, vm_perm: VmPerm) {
|
|
||||||
let options = VmAllocOptions::new(self.len());
|
|
||||||
let frames = VmFrameVec::allocate(&options).expect("allocate frame error");
|
|
||||||
let buffer = vec![0u8; self.nbytes()];
|
|
||||||
self.map_to(vm_space, frames, vm_perm);
|
|
||||||
vm_space
|
|
||||||
.write_bytes(self.start_address(), &buffer)
|
|
||||||
.expect("write zero failed");
|
|
||||||
// frames.write_bytes(0, &buffer).expect("write zero failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// map self to a set of frames
|
|
||||||
pub fn map_to(&self, vm_space: &VmSpace, frames: VmFrameVec, vm_perm: VmPerm) {
|
|
||||||
assert_eq!(self.len(), frames.len());
|
|
||||||
let mut vm_map_options = VmMapOptions::new();
|
|
||||||
vm_map_options.addr(Some(self.start_address()));
|
|
||||||
vm_map_options.perm(vm_perm);
|
|
||||||
vm_space.map(frames, &vm_map_options).expect("map failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unmap(&mut self, vm_space: &VmSpace) {
|
|
||||||
vm_space
|
|
||||||
.unmap(&(self.start_address()..self.end_address()))
|
|
||||||
.expect("unmap failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_mapped(&self, vm_space: &VmSpace) -> bool {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> VmPageIter<'_> {
|
|
||||||
VmPageIter {
|
|
||||||
current: self.start_page,
|
|
||||||
page_range: self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return the number of virtual pages
|
|
||||||
pub const fn len(&self) -> usize {
|
|
||||||
self.end_page.vpn - self.start_page.vpn + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn nbytes(&self) -> usize {
|
|
||||||
self.len() * PAGE_SIZE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VmPageIter<'a> {
|
|
||||||
current: VmPage,
|
|
||||||
page_range: &'a VmPageRange,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for VmPageIter<'a> {
|
|
||||||
type Item = VmPage;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let next_page = if self.current <= self.page_range.end_page {
|
|
||||||
Some(self.current)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
self.current = self.current.next_page();
|
|
||||||
next_page
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Virtual Page
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct VmPage {
|
|
||||||
/// Virtual Page Number
|
|
||||||
vpn: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VmPage {
|
|
||||||
pub const fn containing_address(vaddr: Vaddr) -> Self {
|
|
||||||
Self {
|
|
||||||
vpn: vaddr / PAGE_SIZE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn start_address(&self) -> Vaddr {
|
|
||||||
self.vpn * PAGE_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn next_page(&self) -> VmPage {
|
|
||||||
VmPage { vpn: self.vpn + 1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether current page is mapped
|
|
||||||
pub fn is_mapped(&self, vm_space: &VmSpace) -> bool {
|
|
||||||
vm_space.is_mapped(self.start_address())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_page(&self, vm_space: &VmSpace, vm_perm: VmPerm) -> Result<()> {
|
|
||||||
let vm_alloc_option = VmAllocOptions::new(1);
|
|
||||||
let vm_frame = VmFrameVec::allocate(&vm_alloc_option)?;
|
|
||||||
|
|
||||||
let mut vm_map_options = VmMapOptions::new();
|
|
||||||
vm_map_options.addr(Some(self.start_address()));
|
|
||||||
vm_map_options.perm(vm_perm);
|
|
||||||
vm_space.map(vm_frame, &vm_map_options)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
pub(crate) use alloc::boxed::Box;
|
pub(crate) use alloc::boxed::Box;
|
||||||
pub(crate) use alloc::collections::BTreeMap;
|
pub(crate) use alloc::collections::BTreeMap;
|
||||||
|
pub(crate) use alloc::collections::BTreeSet;
|
||||||
pub(crate) use alloc::collections::LinkedList;
|
pub(crate) use alloc::collections::LinkedList;
|
||||||
pub(crate) use alloc::collections::VecDeque;
|
pub(crate) use alloc::collections::VecDeque;
|
||||||
pub(crate) use alloc::ffi::CString;
|
pub(crate) use alloc::ffi::CString;
|
||||||
|
@ -96,18 +96,9 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result<
|
|||||||
let child_pid = new_pid();
|
let child_pid = new_pid();
|
||||||
let current = Process::current();
|
let current = Process::current();
|
||||||
|
|
||||||
// child process vm space
|
// child process vmar
|
||||||
// FIXME: COPY ON WRITE can be used here
|
let parent_root_vmar = current.root_vmar().unwrap();
|
||||||
let parent_vm_space = current
|
let child_root_vmar = current.root_vmar().unwrap().fork_vmar()?;
|
||||||
.vm_space()
|
|
||||||
.expect("User task should always have vm space");
|
|
||||||
let child_vm_space = parent_vm_space.clone();
|
|
||||||
debug_check_clone_vm_space(parent_vm_space, &child_vm_space);
|
|
||||||
|
|
||||||
let child_file_name = match current.filename() {
|
|
||||||
None => None,
|
|
||||||
Some(filename) => Some(filename.clone()),
|
|
||||||
};
|
|
||||||
|
|
||||||
// child process user_vm
|
// child process user_vm
|
||||||
let child_user_vm = match current.user_vm() {
|
let child_user_vm = match current.user_vm() {
|
||||||
@ -115,18 +106,16 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result<
|
|||||||
Some(user_vm) => Some(user_vm.clone()),
|
Some(user_vm) => Some(user_vm.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// child process cpu context
|
// child process user space
|
||||||
let mut child_cpu_context = parent_context.clone();
|
let mut child_cpu_context = parent_context.clone();
|
||||||
debug!("parent context: {:x?}", parent_context);
|
|
||||||
debug!("parent gp_regs: {:x?}", child_cpu_context.gp_regs);
|
|
||||||
child_cpu_context.gp_regs.rax = 0; // Set return value of child process
|
child_cpu_context.gp_regs.rax = 0; // Set return value of child process
|
||||||
|
let child_vm_space = child_root_vmar.vm_space().clone();
|
||||||
let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context));
|
let child_user_space = Arc::new(UserSpace::new(child_vm_space, child_cpu_context));
|
||||||
debug!("before spawn child task");
|
|
||||||
debug!("current pid: {}", current.pid());
|
|
||||||
debug!("child process pid: {}", child_pid);
|
|
||||||
debug!("rip = 0x{:x}", child_cpu_context.gp_regs.rip);
|
|
||||||
|
|
||||||
|
let child_file_name = match current.filename() {
|
||||||
|
None => None,
|
||||||
|
Some(filename) => Some(filename.clone()),
|
||||||
|
};
|
||||||
let child_file_table = current.file_table.lock().clone();
|
let child_file_table = current.file_table.lock().clone();
|
||||||
|
|
||||||
// inherit parent's sig disposition
|
// inherit parent's sig disposition
|
||||||
@ -145,6 +134,7 @@ pub fn clone_child(parent_context: CpuContext, clone_args: CloneArgs) -> Result<
|
|||||||
child_file_name,
|
child_file_name,
|
||||||
child_user_vm,
|
child_user_vm,
|
||||||
Some(child_user_space),
|
Some(child_user_space),
|
||||||
|
Some(child_root_vmar),
|
||||||
None,
|
None,
|
||||||
child_file_table,
|
child_file_table,
|
||||||
child_sig_dispositions,
|
child_sig_dispositions,
|
||||||
@ -189,12 +179,11 @@ fn clone_child_clear_tid(child_process: &Arc<Process>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn clone_child_set_tid(child_process: &Arc<Process>, clone_args: CloneArgs) -> Result<()> {
|
fn clone_child_set_tid(child_process: &Arc<Process>, clone_args: CloneArgs) -> Result<()> {
|
||||||
debug!("clone child set tid");
|
|
||||||
let child_pid = child_process.pid();
|
let child_pid = child_process.pid();
|
||||||
let child_vm = child_process
|
let child_vmar = child_process
|
||||||
.vm_space()
|
.root_vmar()
|
||||||
.ok_or_else(|| Error::new(Errno::ECHILD))?;
|
.ok_or_else(|| Error::new(Errno::ECHILD))?;
|
||||||
child_vm.write_val(clone_args.child_tidptr, &child_pid)?;
|
child_vmar.write_val(clone_args.child_tidptr, &child_pid)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,339 +0,0 @@
|
|||||||
//! This module is used to parse elf file content to get elf_load_info.
|
|
||||||
//! When create a process from elf file, we will use the elf_load_info to construct the VmSpace
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
memory::vm_page::{VmPage, VmPageRange},
|
|
||||||
prelude::*,
|
|
||||||
};
|
|
||||||
use core::{cmp::Ordering, ops::Range};
|
|
||||||
use jinux_frame::vm::{VmAllocOptions, VmFrameVec, VmIo, VmPerm, VmSpace};
|
|
||||||
use xmas_elf::{
|
|
||||||
header,
|
|
||||||
program::{self, ProgramHeader, ProgramHeader64, SegmentData},
|
|
||||||
ElfFile,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::init_stack::InitStack;
|
|
||||||
|
|
||||||
pub struct ElfLoadInfo<'a> {
|
|
||||||
segments: Vec<ElfSegment<'a>>,
|
|
||||||
init_stack: InitStack,
|
|
||||||
elf_header_info: ElfHeaderInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ElfSegment<'a> {
|
|
||||||
range: Range<Vaddr>,
|
|
||||||
data: &'a [u8],
|
|
||||||
type_: program::Type,
|
|
||||||
vm_perm: VmPerm,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
/// Info parsed from elf header. The entry point is used to set rip
|
|
||||||
/// The other info is used to set auxv vectors.
|
|
||||||
pub struct ElfHeaderInfo {
|
|
||||||
/// the entry point of the elf
|
|
||||||
pub entry_point: Vaddr,
|
|
||||||
/// page header table offset
|
|
||||||
pub ph_off: u64,
|
|
||||||
/// number of program headers
|
|
||||||
pub ph_num: u16,
|
|
||||||
/// The size of a program header
|
|
||||||
pub ph_ent: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ElfSegment<'a> {
|
|
||||||
fn parse_elf_segment(segment: ProgramHeader<'a>, elf_file: &ElfFile<'a>) -> Result<Self> {
|
|
||||||
let start = segment.virtual_addr() as Vaddr;
|
|
||||||
let end = start + segment.mem_size() as Vaddr;
|
|
||||||
let type_ = match segment.get_type() {
|
|
||||||
Err(error_msg) => return_errno_with_message!(Errno::ENOEXEC, error_msg),
|
|
||||||
Ok(type_) => type_,
|
|
||||||
};
|
|
||||||
let data = read_segment_data(segment, elf_file)?;
|
|
||||||
let vm_perm = Self::parse_segment_perm(segment)?;
|
|
||||||
Ok(Self {
|
|
||||||
range: start..end,
|
|
||||||
type_,
|
|
||||||
data,
|
|
||||||
vm_perm,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_segment_perm(segment: ProgramHeader<'a>) -> Result<VmPerm> {
|
|
||||||
let flags = segment.flags();
|
|
||||||
if !flags.is_read() {
|
|
||||||
return_errno_with_message!(Errno::ENOEXEC, "unreadable segment");
|
|
||||||
}
|
|
||||||
let mut vm_perm = VmPerm::R;
|
|
||||||
if flags.is_write() {
|
|
||||||
vm_perm |= VmPerm::W;
|
|
||||||
}
|
|
||||||
if flags.is_execute() {
|
|
||||||
vm_perm |= VmPerm::X;
|
|
||||||
}
|
|
||||||
Ok(vm_perm)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_loadable(&self) -> bool {
|
|
||||||
self.type_ == program::Type::Load
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_address(&self) -> Vaddr {
|
|
||||||
self.range.start
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end_address(&self) -> Vaddr {
|
|
||||||
self.range.end
|
|
||||||
}
|
|
||||||
|
|
||||||
fn copy_and_map_segment(&self, vm_space: &VmSpace) -> Result<()> {
|
|
||||||
let start_address = self.start_address();
|
|
||||||
let page_mask = PAGE_SIZE - 1;
|
|
||||||
let segment_len = self.end_address() - self.start_address();
|
|
||||||
let data_len = self.data.len();
|
|
||||||
let zeroed_bytes = if segment_len > data_len {
|
|
||||||
vec![0u8; segment_len - data_len]
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
};
|
|
||||||
// according to linux abi, the first page may be on same page with another segment.
|
|
||||||
// So at first, we will check whether the first page is mapped.
|
|
||||||
if vm_space.is_mapped(start_address) {
|
|
||||||
// The first page is mapped. This is the rare case.
|
|
||||||
let write_len_on_first_page =
|
|
||||||
(PAGE_SIZE - (start_address & page_mask)).min(self.data.len());
|
|
||||||
vm_space
|
|
||||||
.write_bytes(start_address, &self.data[..write_len_on_first_page])
|
|
||||||
.expect("Write first page failed");
|
|
||||||
let start_page = VmPage::containing_address(start_address).next_page();
|
|
||||||
let end_page = VmPage::containing_address(self.end_address());
|
|
||||||
if end_page >= start_page {
|
|
||||||
let vm_page_range = VmPageRange::new_page_range(start_page, end_page);
|
|
||||||
let page_num = vm_page_range.len();
|
|
||||||
let vm_alloc_options = VmAllocOptions::new(page_num);
|
|
||||||
let frames = VmFrameVec::allocate(&vm_alloc_options)?;
|
|
||||||
frames.write_bytes(0, &self.data[write_len_on_first_page..])?;
|
|
||||||
if zeroed_bytes.len() > 0 {
|
|
||||||
frames.write_bytes(data_len - write_len_on_first_page, &zeroed_bytes)?;
|
|
||||||
}
|
|
||||||
vm_page_range.map_to(vm_space, frames, self.vm_perm);
|
|
||||||
} else {
|
|
||||||
if zeroed_bytes.len() > 0 {
|
|
||||||
vm_space.write_bytes(start_address + data_len, &zeroed_bytes)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The first page is not mapped. This is the common case.
|
|
||||||
let vm_page_range = VmPageRange::new_range(start_address..self.end_address());
|
|
||||||
let page_num = vm_page_range.len();
|
|
||||||
let vm_alloc_options = VmAllocOptions::new(page_num);
|
|
||||||
let frames = VmFrameVec::allocate(&vm_alloc_options)?;
|
|
||||||
let offset = start_address & page_mask;
|
|
||||||
// copy segment
|
|
||||||
frames.write_bytes(offset, &self.data)?;
|
|
||||||
// write zero bytes
|
|
||||||
if zeroed_bytes.len() > 0 {
|
|
||||||
let write_addr = offset + data_len;
|
|
||||||
frames.write_bytes(write_addr, &zeroed_bytes)?;
|
|
||||||
}
|
|
||||||
vm_page_range.map_to(vm_space, frames, self.vm_perm);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_page_aligned(&self) -> bool {
|
|
||||||
self.start_address() % PAGE_SIZE == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ElfLoadInfo<'a> {
|
|
||||||
fn with_capacity(
|
|
||||||
capacity: usize,
|
|
||||||
init_stack: InitStack,
|
|
||||||
elf_header_info: ElfHeaderInfo,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
segments: Vec::with_capacity(capacity),
|
|
||||||
init_stack,
|
|
||||||
elf_header_info,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_segment(&mut self, elf_segment: ElfSegment<'a>) {
|
|
||||||
self.segments.push(elf_segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_elf_data(
|
|
||||||
elf_file_content: &'a [u8],
|
|
||||||
filename: CString,
|
|
||||||
argv: Vec<CString>,
|
|
||||||
envp: Vec<CString>,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let elf_file = match ElfFile::new(elf_file_content) {
|
|
||||||
Err(error_msg) => return_errno_with_message!(Errno::ENOEXEC, error_msg),
|
|
||||||
Ok(elf_file) => elf_file,
|
|
||||||
};
|
|
||||||
check_elf_header(&elf_file)?;
|
|
||||||
// parse elf header
|
|
||||||
let elf_header_info = ElfHeaderInfo::parse_elf_header(&elf_file);
|
|
||||||
// FIXME: only contains load segment?
|
|
||||||
let segments_count = elf_file.program_iter().count();
|
|
||||||
let init_stack = InitStack::new_default_config(filename, argv, envp);
|
|
||||||
let mut elf_load_info =
|
|
||||||
ElfLoadInfo::with_capacity(segments_count, init_stack, elf_header_info);
|
|
||||||
|
|
||||||
// parse each segemnt
|
|
||||||
for segment in elf_file.program_iter() {
|
|
||||||
let elf_segment = ElfSegment::parse_elf_segment(segment, &elf_file)?;
|
|
||||||
if elf_segment.is_loadable() {
|
|
||||||
elf_load_info.add_segment(elf_segment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(elf_load_info)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vm_page_range(&self) -> Result<VmPageRange> {
|
|
||||||
let elf_start_address = self
|
|
||||||
.segments
|
|
||||||
.iter()
|
|
||||||
.filter(|segment| segment.is_loadable())
|
|
||||||
.map(|segment| segment.start_address())
|
|
||||||
.min()
|
|
||||||
.unwrap();
|
|
||||||
let elf_end_address = self
|
|
||||||
.segments
|
|
||||||
.iter()
|
|
||||||
.filter(|segment| segment.is_loadable())
|
|
||||||
.map(|segment| segment.end_address())
|
|
||||||
.max()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(VmPageRange::new_range(elf_start_address..elf_end_address))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// copy and map all segment
|
|
||||||
pub fn copy_and_map_segments(&self, vm_space: &VmSpace) -> Result<()> {
|
|
||||||
for segment in self.segments.iter() {
|
|
||||||
segment.copy_and_map_segment(vm_space)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_stack(&mut self, vm_space: &VmSpace) {
|
|
||||||
self.init_stack
|
|
||||||
.init(vm_space, &self.elf_header_info)
|
|
||||||
.expect("Init User Stack failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function will write the program header table to the initial stack top.
|
|
||||||
/// This function must be called after init process initial stack.
|
|
||||||
/// This infomation is used to set Auxv vectors.
|
|
||||||
pub fn write_program_header_table(&self, vm_space: &VmSpace, file_content: &[u8]) {
|
|
||||||
let write_len = PAGE_SIZE.min(file_content.len());
|
|
||||||
let write_content = &file_content[..write_len];
|
|
||||||
let write_addr = self.init_stack.init_stack_top() - PAGE_SIZE;
|
|
||||||
vm_space
|
|
||||||
.write_bytes(write_addr, write_content)
|
|
||||||
.expect("Write elf content failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn entry_point(&self) -> u64 {
|
|
||||||
self.elf_header_info.entry_point as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn user_stack_top(&self) -> u64 {
|
|
||||||
self.init_stack.user_stack_top() as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn argc(&self) -> u64 {
|
|
||||||
self.init_stack.argc()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn argv(&self) -> u64 {
|
|
||||||
self.init_stack.argv()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn envc(&self) -> u64 {
|
|
||||||
self.init_stack.envc()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn envp(&self) -> u64 {
|
|
||||||
self.init_stack.envp()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// read content from vmspace to ensure elf data is correctly copied to user space
|
|
||||||
pub fn debug_check_map_result(&self, vm_space: &VmSpace) {
|
|
||||||
for segment in self.segments.iter() {
|
|
||||||
let start_address = segment.start_address();
|
|
||||||
let len = segment.data.len();
|
|
||||||
let mut read_buffer = vec![0; len];
|
|
||||||
vm_space
|
|
||||||
.read_bytes(start_address, &mut read_buffer)
|
|
||||||
.expect("read bytes failed");
|
|
||||||
let res = segment.data.cmp(&read_buffer);
|
|
||||||
|
|
||||||
assert_eq!(res, Ordering::Equal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ElfHeaderInfo {
|
|
||||||
fn parse_elf_header(elf_file: &ElfFile) -> Self {
|
|
||||||
let entry_point = elf_file.header.pt2.entry_point() as Vaddr;
|
|
||||||
let ph_off = elf_file.header.pt2.ph_offset();
|
|
||||||
let ph_num = elf_file.header.pt2.ph_count();
|
|
||||||
let ph_ent = core::mem::size_of::<ProgramHeader64>();
|
|
||||||
ElfHeaderInfo {
|
|
||||||
entry_point,
|
|
||||||
ph_off,
|
|
||||||
ph_num,
|
|
||||||
ph_ent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_elf_header(elf_file: &ElfFile) -> Result<()> {
|
|
||||||
let elf_header = elf_file.header;
|
|
||||||
// 64bit
|
|
||||||
debug_assert_eq!(elf_header.pt1.class(), header::Class::SixtyFour);
|
|
||||||
if elf_header.pt1.class() != header::Class::SixtyFour {
|
|
||||||
return_errno!(Errno::ENOEXEC);
|
|
||||||
}
|
|
||||||
// little endian
|
|
||||||
debug_assert_eq!(elf_header.pt1.data(), header::Data::LittleEndian);
|
|
||||||
if elf_header.pt1.data() != header::Data::LittleEndian {
|
|
||||||
return_errno!(Errno::ENOEXEC);
|
|
||||||
}
|
|
||||||
// system V ABI
|
|
||||||
// debug_assert_eq!(elf_header.pt1.os_abi(), header::OsAbi::SystemV);
|
|
||||||
// if elf_header.pt1.os_abi() != header::OsAbi::SystemV {
|
|
||||||
// return Error::new(Errno::ENOEXEC);
|
|
||||||
// }
|
|
||||||
// x86_64 architecture
|
|
||||||
debug_assert_eq!(
|
|
||||||
elf_header.pt2.machine().as_machine(),
|
|
||||||
header::Machine::X86_64
|
|
||||||
);
|
|
||||||
if elf_header.pt2.machine().as_machine() != header::Machine::X86_64 {
|
|
||||||
return_errno!(Errno::ENOEXEC);
|
|
||||||
}
|
|
||||||
// Executable file
|
|
||||||
debug_assert_eq!(elf_header.pt2.type_().as_type(), header::Type::Executable);
|
|
||||||
if elf_header.pt2.type_().as_type() != header::Type::Executable {
|
|
||||||
return_errno!(Errno::ENOEXEC);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_segment_data<'a>(segment: ProgramHeader<'a>, elf_file: &ElfFile<'a>) -> Result<&'a [u8]> {
|
|
||||||
match segment.get_data(&elf_file) {
|
|
||||||
Err(msg) => return_errno_with_message!(Errno::ENOEXEC, msg),
|
|
||||||
Ok(data) => match data {
|
|
||||||
SegmentData::Note64(_, data) | SegmentData::Undefined(data) => Ok(data),
|
|
||||||
_ => return_errno_with_message!(Errno::ENOEXEC, "Unkonwn segment data type"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
141
src/services/libs/jinux-std/src/process/elf/elf_file.rs
Normal file
141
src/services/libs/jinux-std/src/process/elf/elf_file.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/// A wrapper of xmas_elf's elf parsing
|
||||||
|
use xmas_elf::{
|
||||||
|
header::{self, Header, HeaderPt1, HeaderPt2, HeaderPt2_, Machine_, Type_},
|
||||||
|
program::ProgramHeader64,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
pub struct Elf {
|
||||||
|
pub elf_header: ElfHeader,
|
||||||
|
pub program_headers: Vec<ProgramHeader64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Elf {
|
||||||
|
pub fn parse_elf(input: &[u8]) -> Result<Self> {
|
||||||
|
// first parse elf header
|
||||||
|
// The elf header is usually 64 bytes. pt1 is 16bytes and pt2 is 48 bytes.
|
||||||
|
// We require 128 bytes here is to keep consistency with linux implementations.
|
||||||
|
debug_assert!(input.len() >= 128);
|
||||||
|
let header =
|
||||||
|
xmas_elf::header::parse_header(input).map_err(|_| Error::new(Errno::ENOEXEC))?;
|
||||||
|
let elf_header = ElfHeader::parse_elf_header(header)?;
|
||||||
|
check_elf_header(&elf_header)?;
|
||||||
|
// than parse the program headers table
|
||||||
|
// FIXME: we should acquire enough pages before parse
|
||||||
|
let ph_offset = elf_header.pt2.ph_offset;
|
||||||
|
let ph_count = elf_header.pt2.ph_count;
|
||||||
|
let ph_entry_size = elf_header.pt2.ph_entry_size;
|
||||||
|
debug_assert!(
|
||||||
|
input.len() >= ph_offset as usize + ph_count as usize * ph_entry_size as usize
|
||||||
|
);
|
||||||
|
let mut program_headers = Vec::with_capacity(ph_count as usize);
|
||||||
|
for index in 0..ph_count {
|
||||||
|
let program_header = xmas_elf::program::parse_program_header(input, header, index)
|
||||||
|
.map_err(|_| Error::new(Errno::ENOEXEC))?;
|
||||||
|
let ph64 = match program_header {
|
||||||
|
xmas_elf::program::ProgramHeader::Ph64(ph64) => ph64.clone(),
|
||||||
|
xmas_elf::program::ProgramHeader::Ph32(_) => {
|
||||||
|
return_errno_with_message!(Errno::ENOEXEC, "Not 64 byte executable")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
program_headers.push(ph64);
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
elf_header,
|
||||||
|
program_headers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ElfHeader {
|
||||||
|
pub pt1: HeaderPt1,
|
||||||
|
pub pt2: HeaderPt2_64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfHeader {
|
||||||
|
fn parse_elf_header(header: Header) -> Result<Self> {
|
||||||
|
let pt1 = header.pt1.clone();
|
||||||
|
let pt2 = match header.pt2 {
|
||||||
|
HeaderPt2::Header64(header_pt2) => {
|
||||||
|
let HeaderPt2_ {
|
||||||
|
type_,
|
||||||
|
machine,
|
||||||
|
version,
|
||||||
|
entry_point,
|
||||||
|
ph_offset,
|
||||||
|
sh_offset,
|
||||||
|
flags,
|
||||||
|
header_size,
|
||||||
|
ph_entry_size,
|
||||||
|
ph_count,
|
||||||
|
sh_entry_size,
|
||||||
|
sh_count,
|
||||||
|
sh_str_index,
|
||||||
|
} = header_pt2;
|
||||||
|
HeaderPt2_64 {
|
||||||
|
type_: *type_,
|
||||||
|
machine: *machine,
|
||||||
|
version: *version,
|
||||||
|
entry_point: *entry_point,
|
||||||
|
ph_offset: *ph_offset,
|
||||||
|
sh_offset: *sh_offset,
|
||||||
|
flags: *flags,
|
||||||
|
header_size: *header_size,
|
||||||
|
ph_entry_size: *ph_entry_size,
|
||||||
|
ph_count: *ph_count,
|
||||||
|
sh_entry_size: *sh_entry_size,
|
||||||
|
sh_count: *sh_count,
|
||||||
|
sh_str_index: *sh_str_index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return_errno_with_message!(Errno::ENOEXEC, "parse elf header failed"),
|
||||||
|
};
|
||||||
|
Ok(ElfHeader { pt1, pt2 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HeaderPt2_64 {
|
||||||
|
pub type_: Type_,
|
||||||
|
pub machine: Machine_,
|
||||||
|
pub version: u32,
|
||||||
|
pub entry_point: u64,
|
||||||
|
pub ph_offset: u64,
|
||||||
|
pub sh_offset: u64,
|
||||||
|
pub flags: u32,
|
||||||
|
pub header_size: u16,
|
||||||
|
pub ph_entry_size: u16,
|
||||||
|
pub ph_count: u16,
|
||||||
|
pub sh_entry_size: u16,
|
||||||
|
pub sh_count: u16,
|
||||||
|
pub sh_str_index: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_elf_header(elf_header: &ElfHeader) -> Result<()> {
|
||||||
|
// 64bit
|
||||||
|
debug_assert_eq!(elf_header.pt1.class(), header::Class::SixtyFour);
|
||||||
|
if elf_header.pt1.class() != header::Class::SixtyFour {
|
||||||
|
return_errno_with_message!(Errno::ENOEXEC, "Not 64 byte executable");
|
||||||
|
}
|
||||||
|
// little endian
|
||||||
|
debug_assert_eq!(elf_header.pt1.data(), header::Data::LittleEndian);
|
||||||
|
if elf_header.pt1.data() != header::Data::LittleEndian {
|
||||||
|
return_errno_with_message!(Errno::ENOEXEC, "Not little endian executable");
|
||||||
|
}
|
||||||
|
// system V ABI
|
||||||
|
// debug_assert_eq!(elf_header.pt1.os_abi(), header::OsAbi::SystemV);
|
||||||
|
// if elf_header.pt1.os_abi() != header::OsAbi::SystemV {
|
||||||
|
// return Error::new(Errno::ENOEXEC);
|
||||||
|
// }
|
||||||
|
// x86_64 architecture
|
||||||
|
debug_assert_eq!(elf_header.pt2.machine.as_machine(), header::Machine::X86_64);
|
||||||
|
if elf_header.pt2.machine.as_machine() != header::Machine::X86_64 {
|
||||||
|
return_errno_with_message!(Errno::ENOEXEC, "Not x86_64 executable");
|
||||||
|
}
|
||||||
|
// Executable file
|
||||||
|
debug_assert_eq!(elf_header.pt2.type_.as_type(), header::Type::Executable);
|
||||||
|
if elf_header.pt2.type_.as_type() != header::Type::Executable {
|
||||||
|
return_errno_with_message!(Errno::ENOEXEC, "Not executable file");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use crate::vm::vmar::{get_intersected_range, is_intersected};
|
||||||
|
use jinux_frame::vm::{VmAllocOptions, VmFrameVec, VmIo};
|
||||||
|
use jinux_frame::AlignExt;
|
||||||
|
|
||||||
|
use crate::vm::vmo::Pager;
|
||||||
|
|
||||||
|
use super::load_elf::ElfSegment;
|
||||||
|
|
||||||
|
/// The pager behind a elf segment
|
||||||
|
pub struct ElfSegmentPager {
|
||||||
|
/// The pager size
|
||||||
|
pager_size: usize,
|
||||||
|
/// data for current segment
|
||||||
|
segment_data: &'static [u8],
|
||||||
|
/// The offset for the segment data.
|
||||||
|
/// The pager always starts at page-align address, while the segment data may start at any address.
|
||||||
|
/// So the offset will be the segment data start address % PAGE_SIZE
|
||||||
|
offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfSegmentPager {
|
||||||
|
pub fn new(elf_file_content: &'static [u8], elf_segment: &ElfSegment) -> Self {
|
||||||
|
let start = elf_segment.start_address().align_down(PAGE_SIZE);
|
||||||
|
let end = elf_segment.end_address().align_up(PAGE_SIZE);
|
||||||
|
let pager_size = end - start;
|
||||||
|
let offset = elf_segment.start_address() % PAGE_SIZE;
|
||||||
|
let elf_file_segment =
|
||||||
|
&elf_file_content[elf_segment.offset..elf_segment.offset + elf_segment.file_size];
|
||||||
|
Self {
|
||||||
|
pager_size,
|
||||||
|
segment_data: elf_file_segment,
|
||||||
|
offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pager for ElfSegmentPager {
|
||||||
|
fn commit_page(&self, offset: usize) -> Result<jinux_frame::vm::VmFrame> {
|
||||||
|
if offset >= self.pager_size {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "offset exceeds pager size");
|
||||||
|
}
|
||||||
|
let vm_alloc_option = VmAllocOptions::new(1);
|
||||||
|
let mut vm_frames = VmFrameVec::allocate(&vm_alloc_option)?;
|
||||||
|
vm_frames.zero();
|
||||||
|
let page_start = offset.align_down(PAGE_SIZE);
|
||||||
|
let page_end = page_start + PAGE_SIZE;
|
||||||
|
let page_range = page_start..page_end;
|
||||||
|
let data_range = self.offset..self.offset + self.segment_data.len();
|
||||||
|
if is_intersected(&page_range, &data_range) {
|
||||||
|
let intersected_range = get_intersected_range(&page_range, &data_range);
|
||||||
|
let data_write_range =
|
||||||
|
(intersected_range.start - self.offset)..(intersected_range.end - self.offset);
|
||||||
|
let write_content = &self.segment_data[data_write_range];
|
||||||
|
let write_offset = intersected_range.start % PAGE_SIZE;
|
||||||
|
vm_frames.write_bytes(write_offset, write_content)?;
|
||||||
|
}
|
||||||
|
let vm_frame = vm_frames.pop().unwrap();
|
||||||
|
Ok(vm_frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_page(&self, offset: usize) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decommit_page(&self, offset: usize) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,21 @@
|
|||||||
//! The process initial stack, contains arguments, environmental variables and auxiliary vectors
|
//! The process initial stack, contains arguments, environmental variables and auxiliary vectors
|
||||||
//! The data layout of init stack can be seen in Figure 3.9 in https://uclibc.org/docs/psABI-x86_64.pdf
|
//! The data layout of init stack can be seen in Figure 3.9 in https://uclibc.org/docs/psABI-x86_64.pdf
|
||||||
|
|
||||||
use crate::{memory::vm_page::VmPageRange, prelude::*};
|
use crate::rights::Rights;
|
||||||
|
use crate::vm::perms::VmPerms;
|
||||||
|
use crate::{
|
||||||
|
prelude::*,
|
||||||
|
rights::Full,
|
||||||
|
vm::{vmar::Vmar, vmo::VmoOptions},
|
||||||
|
};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use jinux_frame::{
|
use jinux_frame::{
|
||||||
vm::{VmIo, VmPerm, VmSpace},
|
vm::{VmIo, VmPerm},
|
||||||
AlignExt,
|
AlignExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::aux_vec::{AuxKey, AuxVec};
|
use super::aux_vec::{AuxKey, AuxVec};
|
||||||
use super::elf::ElfHeaderInfo;
|
use super::load_elf::ElfHeaderInfo;
|
||||||
|
|
||||||
pub const INIT_STACK_BASE: Vaddr = 0x0000_0000_2000_0000;
|
pub const INIT_STACK_BASE: Vaddr = 0x0000_0000_2000_0000;
|
||||||
pub const INIT_STACK_SIZE: usize = 0x1000 * 16; // 64KB
|
pub const INIT_STACK_SIZE: usize = 0x1000 * 16; // 64KB
|
||||||
@ -90,10 +96,9 @@ impl InitStack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This function only work for first process
|
/// This function only work for first process
|
||||||
pub fn new_default_config(filename: CString, argv: Vec<CString>, envp: Vec<CString>) -> Self {
|
pub fn new_default_config(argv: Vec<CString>, envp: Vec<CString>) -> Self {
|
||||||
let init_stack_top = INIT_STACK_BASE - PAGE_SIZE;
|
let init_stack_top = INIT_STACK_BASE - PAGE_SIZE;
|
||||||
let init_stack_size = INIT_STACK_SIZE;
|
let init_stack_size = INIT_STACK_SIZE;
|
||||||
// InitStack::new(filename, init_stack_top, init_stack_size, argv, envp)
|
|
||||||
InitStack::new(init_stack_top, init_stack_size, argv, envp)
|
InitStack::new(init_stack_top, init_stack_size, argv, envp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,18 +115,28 @@ impl InitStack {
|
|||||||
self.init_stack_top - self.init_stack_size
|
self.init_stack_top - self.init_stack_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&mut self, vm_space: &VmSpace, elf_header_info: &ElfHeaderInfo) -> Result<()> {
|
pub fn init(
|
||||||
self.map_and_zeroed(vm_space);
|
&mut self,
|
||||||
self.write_zero_page(vm_space); // This page is used to store page header table
|
root_vmar: &Vmar<Full>,
|
||||||
self.write_stack_content(vm_space, elf_header_info)?;
|
elf_header_info: &ElfHeaderInfo,
|
||||||
self.debug_print_stack_content(vm_space);
|
ph_addr: Vaddr,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.map_and_zeroed(root_vmar)?;
|
||||||
|
self.write_stack_content(root_vmar, elf_header_info, ph_addr)?;
|
||||||
|
self.debug_print_stack_content(root_vmar);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_and_zeroed(&self, vm_space: &VmSpace) {
|
fn map_and_zeroed(&self, root_vmar: &Vmar<Full>) -> Result<()> {
|
||||||
let vm_page_range = VmPageRange::new_range(self.user_stack_bottom()..self.user_stack_top());
|
let vmo_options = VmoOptions::<Rights>::new(self.init_stack_size);
|
||||||
let vm_perm = InitStack::perm();
|
let vmo = vmo_options.alloc()?;
|
||||||
vm_page_range.map_zeroed(vm_space, vm_perm);
|
vmo.clear(0..vmo.size())?;
|
||||||
|
let perms = VmPerms::READ | VmPerms::WRITE;
|
||||||
|
let vmar_map_options = root_vmar
|
||||||
|
.new_map(vmo, perms)?
|
||||||
|
.offset(self.user_stack_bottom());
|
||||||
|
vmar_map_options.build().unwrap();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Libc ABI requires 16-byte alignment of the stack entrypoint.
|
/// Libc ABI requires 16-byte alignment of the stack entrypoint.
|
||||||
@ -129,60 +144,54 @@ impl InitStack {
|
|||||||
/// to meet the requirement if necessary.
|
/// to meet the requirement if necessary.
|
||||||
fn adjust_stack_alignment(
|
fn adjust_stack_alignment(
|
||||||
&mut self,
|
&mut self,
|
||||||
vm_space: &VmSpace,
|
root_vmar: &Vmar<Full>,
|
||||||
envp_pointers: &Vec<u64>,
|
envp_pointers: &Vec<u64>,
|
||||||
argv_pointers: &Vec<u64>,
|
argv_pointers: &Vec<u64>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// ensure 8-byte alignment
|
// ensure 8-byte alignment
|
||||||
self.write_u64(0, vm_space)?;
|
self.write_u64(0, root_vmar)?;
|
||||||
let auxvec_size = (self.aux_vec.table().len() + 1) * (mem::size_of::<u64>() * 2);
|
let auxvec_size = (self.aux_vec.table().len() + 1) * (mem::size_of::<u64>() * 2);
|
||||||
let envp_pointers_size = (envp_pointers.len() + 1) * mem::size_of::<u64>();
|
let envp_pointers_size = (envp_pointers.len() + 1) * mem::size_of::<u64>();
|
||||||
let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::<u64>();
|
let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::<u64>();
|
||||||
let argc_size = mem::size_of::<u64>();
|
let argc_size = mem::size_of::<u64>();
|
||||||
let to_write_size = auxvec_size + envp_pointers_size + argv_pointers_size + argc_size;
|
let to_write_size = auxvec_size + envp_pointers_size + argv_pointers_size + argc_size;
|
||||||
if (self.pos - to_write_size) % 16 != 0 {
|
if (self.pos - to_write_size) % 16 != 0 {
|
||||||
self.write_u64(0, vm_space)?;
|
self.write_u64(0, root_vmar)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_zero_page(&mut self, vm_space: &VmSpace) {
|
|
||||||
self.pos -= PAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_stack_content(
|
fn write_stack_content(
|
||||||
&mut self,
|
&mut self,
|
||||||
vm_space: &VmSpace,
|
root_vmar: &Vmar<Full>,
|
||||||
elf_header_info: &ElfHeaderInfo,
|
elf_header_info: &ElfHeaderInfo,
|
||||||
|
ph_addr: Vaddr,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// write envp string
|
// write envp string
|
||||||
let envp_pointers = self.write_envp_strings(vm_space)?;
|
let envp_pointers = self.write_envp_strings(root_vmar)?;
|
||||||
// write argv string
|
// write argv string
|
||||||
let argv_pointers = self.write_argv_strings(vm_space)?;
|
let argv_pointers = self.write_argv_strings(root_vmar)?;
|
||||||
// write random value
|
// write random value
|
||||||
let random_value = generate_random_for_aux_vec();
|
let random_value = generate_random_for_aux_vec();
|
||||||
let random_value_pointer = self.write_bytes(&random_value, vm_space)?;
|
let random_value_pointer = self.write_bytes(&random_value, root_vmar)?;
|
||||||
self.aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?;
|
self.aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?;
|
||||||
self.aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?;
|
self.aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?;
|
||||||
self.aux_vec.set(
|
self.aux_vec.set(AuxKey::AT_PHDR, ph_addr as u64)?;
|
||||||
AuxKey::AT_PHDR,
|
|
||||||
self.init_stack_top as u64 - PAGE_SIZE as u64 + elf_header_info.ph_off,
|
|
||||||
)?;
|
|
||||||
self.aux_vec
|
self.aux_vec
|
||||||
.set(AuxKey::AT_PHNUM, elf_header_info.ph_num as u64)?;
|
.set(AuxKey::AT_PHNUM, elf_header_info.ph_num as u64)?;
|
||||||
self.aux_vec
|
self.aux_vec
|
||||||
.set(AuxKey::AT_PHENT, elf_header_info.ph_ent as u64)?;
|
.set(AuxKey::AT_PHENT, elf_header_info.ph_ent as u64)?;
|
||||||
self.adjust_stack_alignment(vm_space, &envp_pointers, &argv_pointers)?;
|
self.adjust_stack_alignment(root_vmar, &envp_pointers, &argv_pointers)?;
|
||||||
self.write_aux_vec(vm_space)?;
|
self.write_aux_vec(root_vmar)?;
|
||||||
self.write_envp_pointers(vm_space, envp_pointers)?;
|
self.write_envp_pointers(root_vmar, envp_pointers)?;
|
||||||
self.write_argv_pointers(vm_space, argv_pointers)?;
|
self.write_argv_pointers(root_vmar, argv_pointers)?;
|
||||||
// write argc
|
// write argc
|
||||||
let argc = self.argc();
|
let argc = self.argc();
|
||||||
self.write_u64(argc, vm_space)?;
|
self.write_u64(argc, root_vmar)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_envp_strings(&mut self, vm_space: &VmSpace) -> Result<Vec<u64>> {
|
fn write_envp_strings(&mut self, root_vmar: &Vmar<Full>) -> Result<Vec<u64>> {
|
||||||
let envp = self
|
let envp = self
|
||||||
.envp
|
.envp
|
||||||
.iter()
|
.iter()
|
||||||
@ -190,13 +199,13 @@ impl InitStack {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut envp_pointers = Vec::with_capacity(envp.len());
|
let mut envp_pointers = Vec::with_capacity(envp.len());
|
||||||
for envp in envp.iter() {
|
for envp in envp.iter() {
|
||||||
let pointer = self.write_cstring(envp, vm_space)?;
|
let pointer = self.write_cstring(envp, root_vmar)?;
|
||||||
envp_pointers.push(pointer);
|
envp_pointers.push(pointer);
|
||||||
}
|
}
|
||||||
Ok(envp_pointers)
|
Ok(envp_pointers)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_argv_strings(&mut self, vm_space: &VmSpace) -> Result<Vec<u64>> {
|
fn write_argv_strings(&mut self, root_vmar: &Vmar<Full>) -> Result<Vec<u64>> {
|
||||||
let argv = self
|
let argv = self
|
||||||
.argv
|
.argv
|
||||||
.iter()
|
.iter()
|
||||||
@ -204,17 +213,17 @@ impl InitStack {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut argv_pointers = Vec::with_capacity(argv.len());
|
let mut argv_pointers = Vec::with_capacity(argv.len());
|
||||||
for argv in argv.iter().rev() {
|
for argv in argv.iter().rev() {
|
||||||
let pointer = self.write_cstring(argv, vm_space)?;
|
let pointer = self.write_cstring(argv, root_vmar)?;
|
||||||
argv_pointers.push(pointer);
|
argv_pointers.push(pointer);
|
||||||
}
|
}
|
||||||
argv_pointers.reverse();
|
argv_pointers.reverse();
|
||||||
Ok(argv_pointers)
|
Ok(argv_pointers)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_aux_vec(&mut self, vm_space: &VmSpace) -> Result<()> {
|
fn write_aux_vec(&mut self, root_vmar: &Vmar<Full>) -> Result<()> {
|
||||||
// Write NULL auxilary
|
// Write NULL auxilary
|
||||||
self.write_u64(0, vm_space)?;
|
self.write_u64(0, root_vmar)?;
|
||||||
self.write_u64(AuxKey::AT_NULL as u64, vm_space)?;
|
self.write_u64(AuxKey::AT_NULL as u64, root_vmar)?;
|
||||||
// Write Auxiliary vectors
|
// Write Auxiliary vectors
|
||||||
let aux_vec: Vec<_> = self
|
let aux_vec: Vec<_> = self
|
||||||
.aux_vec
|
.aux_vec
|
||||||
@ -223,38 +232,38 @@ impl InitStack {
|
|||||||
.map(|(aux_key, aux_value)| (*aux_key, *aux_value))
|
.map(|(aux_key, aux_value)| (*aux_key, *aux_value))
|
||||||
.collect();
|
.collect();
|
||||||
for (aux_key, aux_value) in aux_vec.iter() {
|
for (aux_key, aux_value) in aux_vec.iter() {
|
||||||
self.write_u64(*aux_value, vm_space)?;
|
self.write_u64(*aux_value, root_vmar)?;
|
||||||
self.write_u64(*aux_key as u64, vm_space)?;
|
self.write_u64(*aux_key as u64, root_vmar)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_envp_pointers(
|
fn write_envp_pointers(
|
||||||
&mut self,
|
&mut self,
|
||||||
vm_space: &VmSpace,
|
root_vmar: &Vmar<Full>,
|
||||||
mut envp_pointers: Vec<u64>,
|
mut envp_pointers: Vec<u64>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// write NULL pointer
|
// write NULL pointer
|
||||||
self.write_u64(0, vm_space)?;
|
self.write_u64(0, root_vmar)?;
|
||||||
// write envp pointers
|
// write envp pointers
|
||||||
envp_pointers.reverse();
|
envp_pointers.reverse();
|
||||||
for envp_pointer in envp_pointers {
|
for envp_pointer in envp_pointers {
|
||||||
self.write_u64(envp_pointer, vm_space)?;
|
self.write_u64(envp_pointer, root_vmar)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_argv_pointers(
|
fn write_argv_pointers(
|
||||||
&mut self,
|
&mut self,
|
||||||
vm_space: &VmSpace,
|
root_vmar: &Vmar<Full>,
|
||||||
mut argv_pointers: Vec<u64>,
|
mut argv_pointers: Vec<u64>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// write 0
|
// write 0
|
||||||
self.write_u64(0, vm_space)?;
|
self.write_u64(0, root_vmar)?;
|
||||||
// write argv pointers
|
// write argv pointers
|
||||||
argv_pointers.reverse();
|
argv_pointers.reverse();
|
||||||
for argv_pointer in argv_pointers {
|
for argv_pointer in argv_pointers {
|
||||||
self.write_u64(argv_pointer, vm_space)?;
|
self.write_u64(argv_pointer, root_vmar)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -286,35 +295,35 @@ impl InitStack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// returns the u64 start address
|
/// returns the u64 start address
|
||||||
fn write_u64(&mut self, val: u64, vm_space: &VmSpace) -> Result<u64> {
|
fn write_u64(&mut self, val: u64, root_vmar: &Vmar<Full>) -> Result<u64> {
|
||||||
let start_address = (self.pos - 8).align_down(8);
|
let start_address = (self.pos - 8).align_down(8);
|
||||||
self.pos = start_address;
|
self.pos = start_address;
|
||||||
vm_space.write_val(start_address, &val)?;
|
root_vmar.write_val(start_address, &val)?;
|
||||||
Ok(self.pos as u64)
|
Ok(self.pos as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bytes(&mut self, bytes: &[u8], vm_space: &VmSpace) -> Result<u64> {
|
fn write_bytes(&mut self, bytes: &[u8], root_vmar: &Vmar<Full>) -> Result<u64> {
|
||||||
let len = bytes.len();
|
let len = bytes.len();
|
||||||
self.pos -= len;
|
self.pos -= len;
|
||||||
vm_space.write_bytes(self.pos, bytes)?;
|
root_vmar.write_bytes(self.pos, bytes)?;
|
||||||
Ok(self.pos as u64)
|
Ok(self.pos as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the string start address
|
/// returns the string start address
|
||||||
/// cstring will with end null byte.
|
/// cstring will with end null byte.
|
||||||
fn write_cstring(&mut self, val: &CString, vm_space: &VmSpace) -> Result<u64> {
|
fn write_cstring(&mut self, val: &CString, root_vmar: &Vmar<Full>) -> Result<u64> {
|
||||||
let bytes = val.as_bytes_with_nul();
|
let bytes = val.as_bytes_with_nul();
|
||||||
self.write_bytes(bytes, vm_space)
|
self.write_bytes(bytes, root_vmar)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn perm() -> VmPerm {
|
pub const fn perm() -> VmPerm {
|
||||||
VmPerm::RWU
|
VmPerm::RWU
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_print_stack_content(&self, vm_space: &VmSpace) {
|
fn debug_print_stack_content(&self, root_vmar: &Vmar<Full>) {
|
||||||
debug!("print stack content:");
|
debug!("print stack content:");
|
||||||
let stack_top = self.user_stack_top();
|
let stack_top = self.user_stack_top();
|
||||||
let argc = vm_space.read_val::<u64>(stack_top).unwrap();
|
let argc = root_vmar.read_val::<u64>(stack_top).unwrap();
|
||||||
debug!("argc = {}", argc);
|
debug!("argc = {}", argc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
250
src/services/libs/jinux-std/src/process/elf/load_elf.rs
Normal file
250
src/services/libs/jinux-std/src/process/elf/load_elf.rs
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
//! This module is used to parse elf file content to get elf_load_info.
|
||||||
|
//! When create a process from elf file, we will use the elf_load_info to construct the VmSpace
|
||||||
|
|
||||||
|
use crate::vm::perms::VmPerms;
|
||||||
|
use crate::vm::vmo::VmoRightsOp;
|
||||||
|
use crate::{
|
||||||
|
prelude::*,
|
||||||
|
rights::Full,
|
||||||
|
vm::{
|
||||||
|
vmar::Vmar,
|
||||||
|
vmo::{Pager, Vmo, VmoOptions},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use jinux_frame::vm::VmPerm;
|
||||||
|
use jinux_frame::AlignExt;
|
||||||
|
use xmas_elf::program::{self, ProgramHeader64};
|
||||||
|
|
||||||
|
use super::elf_file::Elf;
|
||||||
|
use super::elf_segment_pager::ElfSegmentPager;
|
||||||
|
use super::init_stack::InitStack;
|
||||||
|
|
||||||
|
pub struct ElfLoadInfo {
|
||||||
|
segments: Vec<ElfSegment>,
|
||||||
|
init_stack: InitStack,
|
||||||
|
elf_header_info: ElfHeaderInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ElfSegment {
|
||||||
|
/// The virtual addr where to put the segment.
|
||||||
|
pub virtual_addr: Vaddr,
|
||||||
|
/// The segment's size in memory, in bytes.
|
||||||
|
pub mem_size: usize,
|
||||||
|
/// The segment's offset in origin elf file
|
||||||
|
pub offset: usize,
|
||||||
|
/// The size the segment has in origin elf file, in bytes
|
||||||
|
pub file_size: usize,
|
||||||
|
type_: program::Type,
|
||||||
|
vm_perm: VmPerm,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
/// Info parsed from elf header. The entry point is used to set rip
|
||||||
|
/// The other info is used to set auxv vectors.
|
||||||
|
pub struct ElfHeaderInfo {
|
||||||
|
/// the entry point of the elf
|
||||||
|
pub entry_point: Vaddr,
|
||||||
|
/// page header table offset
|
||||||
|
pub ph_off: u64,
|
||||||
|
/// number of program headers
|
||||||
|
pub ph_num: u16,
|
||||||
|
/// The size of a program header
|
||||||
|
pub ph_ent: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfSegment {
|
||||||
|
fn parse_elf_segment(program_header: ProgramHeader64) -> Result<Self> {
|
||||||
|
let start = program_header.virtual_addr as Vaddr;
|
||||||
|
let end = start + program_header.mem_size as Vaddr;
|
||||||
|
let type_ = program_header
|
||||||
|
.get_type()
|
||||||
|
.map_err(|_| Error::new(Errno::ENOEXEC))?;
|
||||||
|
let vm_perm = Self::parse_segment_perm(program_header.flags)?;
|
||||||
|
Ok(Self {
|
||||||
|
virtual_addr: program_header.virtual_addr as _,
|
||||||
|
mem_size: program_header.mem_size as usize,
|
||||||
|
offset: program_header.offset as usize,
|
||||||
|
file_size: program_header.file_size as usize,
|
||||||
|
type_,
|
||||||
|
vm_perm,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_segment_perm(flags: xmas_elf::program::Flags) -> Result<VmPerm> {
|
||||||
|
if !flags.is_read() {
|
||||||
|
return_errno_with_message!(Errno::ENOEXEC, "unreadable segment");
|
||||||
|
}
|
||||||
|
let mut vm_perm = VmPerm::R;
|
||||||
|
if flags.is_write() {
|
||||||
|
vm_perm |= VmPerm::W;
|
||||||
|
}
|
||||||
|
if flags.is_execute() {
|
||||||
|
vm_perm |= VmPerm::X;
|
||||||
|
}
|
||||||
|
Ok(vm_perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_program_headers_table(&self, ph_offset: usize) -> bool {
|
||||||
|
// program headers table is at ph_offset of elf file
|
||||||
|
self.offset <= ph_offset && ph_offset < self.offset + self.file_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If this segment contains ph table, then returns the ph table addr
|
||||||
|
/// Otherwise, returns None
|
||||||
|
pub fn program_headers_table_addr(&self, ph_offset: usize) -> Option<Vaddr> {
|
||||||
|
if self.contains_program_headers_table(ph_offset) {
|
||||||
|
Some(ph_offset - self.offset + self.virtual_addr)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_loadable(&self) -> bool {
|
||||||
|
self.type_ == program::Type::Load
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_address(&self) -> Vaddr {
|
||||||
|
self.virtual_addr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_address(&self) -> Vaddr {
|
||||||
|
self.virtual_addr + self.mem_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_segment_vmo(&self, elf_file_content: &'static [u8]) -> Vmo<Full> {
|
||||||
|
let vmo_start = self.start_address().align_down(PAGE_SIZE);
|
||||||
|
let vmo_end = self.end_address().align_up(PAGE_SIZE);
|
||||||
|
let segment_len = vmo_end - vmo_start;
|
||||||
|
let pager = Arc::new(ElfSegmentPager::new(elf_file_content, self)) as Arc<dyn Pager>;
|
||||||
|
let vmo_alloc_options: VmoOptions<Full> = VmoOptions::new(segment_len).pager(pager);
|
||||||
|
vmo_alloc_options.alloc().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// create vmo for each segment and map the segment to root_vmar
|
||||||
|
fn map_segment_vmo(
|
||||||
|
&self,
|
||||||
|
root_vmar: &Vmar<Full>,
|
||||||
|
elf_file_content: &'static [u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
let vmo = self.init_segment_vmo(elf_file_content).to_dyn();
|
||||||
|
let perms = VmPerms::from(self.vm_perm);
|
||||||
|
// The segment may not be aligned to page
|
||||||
|
let offset = self.start_address().align_down(PAGE_SIZE);
|
||||||
|
let vm_map_options = root_vmar.new_map(vmo, perms)?.offset(offset);
|
||||||
|
let map_addr = vm_map_options.build()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfLoadInfo {
|
||||||
|
fn with_capacity(
|
||||||
|
capacity: usize,
|
||||||
|
init_stack: InitStack,
|
||||||
|
elf_header_info: ElfHeaderInfo,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
segments: Vec::with_capacity(capacity),
|
||||||
|
init_stack,
|
||||||
|
elf_header_info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_segment(&mut self, elf_segment: ElfSegment) {
|
||||||
|
self.segments.push(elf_segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_elf_data(
|
||||||
|
elf_file_content: &'static [u8],
|
||||||
|
argv: Vec<CString>,
|
||||||
|
envp: Vec<CString>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let elf_file = Elf::parse_elf(elf_file_content)?;
|
||||||
|
// parse elf header
|
||||||
|
let elf_header_info = ElfHeaderInfo::parse_elf_header(&elf_file);
|
||||||
|
// FIXME: only contains load segment?
|
||||||
|
let ph_count = elf_file.program_headers.len();
|
||||||
|
let init_stack = InitStack::new_default_config(argv, envp);
|
||||||
|
let mut elf_load_info = ElfLoadInfo::with_capacity(ph_count, init_stack, elf_header_info);
|
||||||
|
|
||||||
|
// parse each segemnt
|
||||||
|
for program_header in elf_file.program_headers {
|
||||||
|
let elf_segment = ElfSegment::parse_elf_segment(program_header)?;
|
||||||
|
if elf_segment.is_loadable() {
|
||||||
|
elf_load_info.add_segment(elf_segment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(elf_load_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// init vmo for each segment and then map segment to root vmar
|
||||||
|
pub fn map_segment_vmos(
|
||||||
|
&self,
|
||||||
|
root_vmar: &Vmar<Full>,
|
||||||
|
elf_file_content: &'static [u8],
|
||||||
|
) -> Result<()> {
|
||||||
|
for segment in &self.segments {
|
||||||
|
segment.map_segment_vmo(root_vmar, elf_file_content)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_stack(&mut self, root_vmar: &Vmar<Full>, file_content: &[u8]) -> Result<()> {
|
||||||
|
let ph_addr = self.program_headers_table_addr()?;
|
||||||
|
self.init_stack
|
||||||
|
.init(root_vmar, &self.elf_header_info, ph_addr)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_headers_table_addr(&self) -> Result<Vaddr> {
|
||||||
|
let ph_offset = self.elf_header_info.ph_off as usize;
|
||||||
|
for segment in &self.segments {
|
||||||
|
if let Some(ph_addr) = segment.program_headers_table_addr(ph_offset) {
|
||||||
|
return Ok(ph_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::ENOEXEC,
|
||||||
|
"can not find program header table address in elf"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entry_point(&self) -> u64 {
|
||||||
|
self.elf_header_info.entry_point as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_stack_top(&self) -> u64 {
|
||||||
|
self.init_stack.user_stack_top() as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn argc(&self) -> u64 {
|
||||||
|
self.init_stack.argc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn argv(&self) -> u64 {
|
||||||
|
self.init_stack.argv()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn envc(&self) -> u64 {
|
||||||
|
self.init_stack.envc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn envp(&self) -> u64 {
|
||||||
|
self.init_stack.envp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ElfHeaderInfo {
|
||||||
|
fn parse_elf_header(elf_file: &Elf) -> Self {
|
||||||
|
let entry_point = elf_file.elf_header.pt2.entry_point as Vaddr;
|
||||||
|
let ph_off = elf_file.elf_header.pt2.ph_offset;
|
||||||
|
let ph_num = elf_file.elf_header.pt2.ph_count;
|
||||||
|
let ph_ent = elf_file.elf_header.pt2.ph_entry_size;
|
||||||
|
ElfHeaderInfo {
|
||||||
|
entry_point,
|
||||||
|
ph_off,
|
||||||
|
ph_num,
|
||||||
|
ph_ent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,26 @@
|
|||||||
pub mod aux_vec;
|
pub mod aux_vec;
|
||||||
pub mod elf;
|
pub mod elf_file;
|
||||||
|
pub mod elf_segment_pager;
|
||||||
pub mod init_stack;
|
pub mod init_stack;
|
||||||
|
pub mod load_elf;
|
||||||
|
|
||||||
use jinux_frame::vm::VmSpace;
|
use self::load_elf::ElfLoadInfo;
|
||||||
|
use crate::{prelude::*, rights::Full, vm::vmar::Vmar};
|
||||||
|
|
||||||
use self::elf::ElfLoadInfo;
|
/// load elf to the root vmar. this function will
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
/// load elf to a given vm_space. this function will
|
|
||||||
/// 1. read the vaddr of each segment to get all elf pages.
|
/// 1. read the vaddr of each segment to get all elf pages.
|
||||||
/// 2. allocate physical frames and copy elf data to these frames
|
/// 2. create a vmo for each elf segment, create a backup pager for each segment. Then map the vmo to the root vmar.
|
||||||
/// 3. map frames to the correct vaddr
|
/// 3. write proper content to the init stack.
|
||||||
/// 4. (allocate frams and) map the user stack
|
pub fn load_elf_to_root_vmar(
|
||||||
pub fn load_elf_to_vm_space<'a>(
|
|
||||||
filename: CString,
|
filename: CString,
|
||||||
elf_file_content: &'a [u8],
|
elf_file_content: &'static [u8],
|
||||||
vm_space: &VmSpace,
|
root_vmar: &Vmar<Full>,
|
||||||
argv: Vec<CString>,
|
argv: Vec<CString>,
|
||||||
envp: Vec<CString>,
|
envp: Vec<CString>,
|
||||||
) -> Result<ElfLoadInfo<'a>> {
|
) -> Result<ElfLoadInfo> {
|
||||||
let mut elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content, filename, argv, envp)?;
|
let mut elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content, argv, envp)?;
|
||||||
elf_load_info.copy_and_map_segments(vm_space)?;
|
elf_load_info.map_segment_vmos(root_vmar, elf_file_content)?;
|
||||||
elf_load_info.debug_check_map_result(vm_space);
|
elf_load_info.init_stack(root_vmar, elf_file_content)?;
|
||||||
elf_load_info.init_stack(vm_space);
|
|
||||||
elf_load_info.write_program_header_table(vm_space, elf_file_content);
|
|
||||||
debug!("load elf succeeds.");
|
debug!("load elf succeeds.");
|
||||||
|
|
||||||
Ok(elf_load_info)
|
Ok(elf_load_info)
|
||||||
|
@ -1,14 +1,114 @@
|
|||||||
use jinux_frame::cpu::CpuContext;
|
use jinux_frame::{
|
||||||
|
cpu::{CpuContext, TrapInformation},
|
||||||
|
trap::*,
|
||||||
|
vm::VmIo,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::vm::page_fault_handler::PageFaultHandler;
|
||||||
use crate::{prelude::*, process::signal::signals::fault::FaultSignal};
|
use crate::{prelude::*, process::signal::signals::fault::FaultSignal};
|
||||||
|
|
||||||
/// We can't handle most exceptions, just send self a fault signal before return to user space.
|
/// We can't handle most exceptions, just send self a fault signal before return to user space.
|
||||||
pub fn handle_exception(context: &mut CpuContext) {
|
pub fn handle_exception(context: &mut CpuContext) {
|
||||||
let trap_info = context.trap_information.clone();
|
let trap_info = context.trap_information.clone();
|
||||||
|
log_trap_info(&trap_info);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let pid = current.pid();
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
debug!("trap info = {:x?}", trap_info);
|
|
||||||
debug!("cpu context = {:x?}", context);
|
match trap_info.id {
|
||||||
let signal = Box::new(FaultSignal::new(&trap_info));
|
PAGE_FAULT => handle_page_fault(&trap_info),
|
||||||
|
_ => {
|
||||||
|
// We current do nothing about other exceptions
|
||||||
|
generate_fault_signal(&trap_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_page_fault(trap_info: &TrapInformation) {
|
||||||
|
const PAGE_NOT_PRESENT_ERROR_MASK: u64 = 0x1 << 0;
|
||||||
|
const WRITE_ACCESS_MASK: u64 = 0x1 << 1;
|
||||||
|
let not_present = trap_info.err & PAGE_NOT_PRESENT_ERROR_MASK == 0;
|
||||||
|
let write = trap_info.err & WRITE_ACCESS_MASK != 0;
|
||||||
|
if not_present || write {
|
||||||
|
// If page is not present or due to write access, we should ask the vmar try to commit this page
|
||||||
|
let current = current!();
|
||||||
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
|
let page_fault_addr = trap_info.cr2 as Vaddr;
|
||||||
|
debug!(
|
||||||
|
"Page fault address: 0x{:x}, write access: {}",
|
||||||
|
page_fault_addr, write
|
||||||
|
);
|
||||||
|
if let Err(_) = root_vmar.handle_page_fault(page_fault_addr, not_present, write) {
|
||||||
|
generate_fault_signal(trap_info);
|
||||||
|
} else {
|
||||||
|
// ensure page fault is successfully handled
|
||||||
|
// FIXME: this check can be removed
|
||||||
|
let vm_space = root_vmar.vm_space();
|
||||||
|
let _: u8 = vm_space.read_val(page_fault_addr).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, the page fault cannot be handled
|
||||||
|
generate_fault_signal(trap_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generate a fault signal for current process.
|
||||||
|
fn generate_fault_signal(trap_info: &TrapInformation) {
|
||||||
|
let current = current!();
|
||||||
|
let signal = Box::new(FaultSignal::new(trap_info));
|
||||||
current.sig_queues().lock().enqueue(signal);
|
current.sig_queues().lock().enqueue(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! log_trap_common {
|
||||||
|
($exception_name: ident, $trap_info: ident) => {
|
||||||
|
debug!(
|
||||||
|
"[Trap][{}][err = {}]",
|
||||||
|
stringify!($exception_name),
|
||||||
|
$trap_info.err
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_trap_info(trap_info: &TrapInformation) {
|
||||||
|
match trap_info.id {
|
||||||
|
DIVIDE_BY_ZERO => log_trap_common!(DIVIDE_BY_ZERO, trap_info),
|
||||||
|
DEBUG => log_trap_common!(DEBUG, trap_info),
|
||||||
|
NON_MASKABLE_INTERRUPT => log_trap_common!(NON_MASKABLE_INTERRUPT, trap_info),
|
||||||
|
BREAKPOINT => log_trap_common!(BREAKPOINT, trap_info),
|
||||||
|
OVERFLOW => log_trap_common!(OVERFLOW, trap_info),
|
||||||
|
BOUND_RANGE_EXCEEDED => log_trap_common!(BOUND_RANGE_EXCEEDED, trap_info),
|
||||||
|
INVALID_OPCODE => log_trap_common!(INVALID_OPCODE, trap_info),
|
||||||
|
DEVICE_NOT_AVAILABLE => log_trap_common!(DEVICE_NOT_AVAILABLE, trap_info),
|
||||||
|
DOUBLE_FAULT => log_trap_common!(DOUBLE_FAULT, trap_info),
|
||||||
|
COPROCESSOR_SEGMENT_OVERRUN => log_trap_common!(COPROCESSOR_SEGMENT_OVERRUN, trap_info),
|
||||||
|
INVAILD_TSS => log_trap_common!(INVAILD_TSS, trap_info),
|
||||||
|
SEGMENT_NOT_PRESENT => log_trap_common!(SEGMENT_NOT_PRESENT, trap_info),
|
||||||
|
STACK_SEGMENT_FAULT => log_trap_common!(STACK_SEGMENT_FAULT, trap_info),
|
||||||
|
GENERAL_PROTECTION_FAULT => log_trap_common!(GENERAL_PROTECTION_FAULT, trap_info),
|
||||||
|
PAGE_FAULT => {
|
||||||
|
debug!(
|
||||||
|
"[Trap][{}][page fault addr = 0x{:x}, err = {}]",
|
||||||
|
stringify!(PAGE_FAULT),
|
||||||
|
trap_info.cr2,
|
||||||
|
trap_info.err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 15 reserved
|
||||||
|
X87_FLOATING_POINT_EXCEPTION => log_trap_common!(X87_FLOATING_POINT_EXCEPTION, trap_info),
|
||||||
|
ALIGNMENT_CHECK => log_trap_common!(ALIGNMENT_CHECK, trap_info),
|
||||||
|
MACHINE_CHECK => log_trap_common!(MACHINE_CHECK, trap_info),
|
||||||
|
SIMD_FLOATING_POINT_EXCEPTION => log_trap_common!(SIMD_FLOATING_POINT_EXCEPTION, trap_info),
|
||||||
|
VIRTUALIZATION_EXCEPTION => log_trap_common!(VIRTUALIZATION_EXCEPTION, trap_info),
|
||||||
|
CONTROL_PROTECTION_EXCEPTION => log_trap_common!(CONTROL_PROTECTION_EXCEPTION, trap_info),
|
||||||
|
HYPERVISOR_INJECTION_EXCEPTION => {
|
||||||
|
log_trap_common!(HYPERVISOR_INJECTION_EXCEPTION, trap_info)
|
||||||
|
}
|
||||||
|
VMM_COMMUNICATION_EXCEPTION => log_trap_common!(VMM_COMMUNICATION_EXCEPTION, trap_info),
|
||||||
|
SECURITY_EXCEPTION => log_trap_common!(SECURITY_EXCEPTION, trap_info),
|
||||||
|
_ => {
|
||||||
|
info!(
|
||||||
|
"[Trap][Unknown trap type][id = {}, err = {}]",
|
||||||
|
trap_info.id, trap_info.err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,6 @@ use core::sync::atomic::{AtomicI32, Ordering};
|
|||||||
|
|
||||||
use self::name::ProcessName;
|
use self::name::ProcessName;
|
||||||
use self::process_group::ProcessGroup;
|
use self::process_group::ProcessGroup;
|
||||||
use self::process_vm::mmap_area::MmapArea;
|
|
||||||
use self::process_vm::user_heap::UserHeap;
|
use self::process_vm::user_heap::UserHeap;
|
||||||
use self::process_vm::UserVm;
|
use self::process_vm::UserVm;
|
||||||
use self::signal::constants::SIGCHLD;
|
use self::signal::constants::SIGCHLD;
|
||||||
@ -14,9 +13,11 @@ use self::status::ProcessStatus;
|
|||||||
use self::task::create_user_task_from_elf;
|
use self::task::create_user_task_from_elf;
|
||||||
use crate::fs::file_table::FileTable;
|
use crate::fs::file_table::FileTable;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::rights::Full;
|
||||||
use crate::tty::get_console;
|
use crate::tty::get_console;
|
||||||
|
use crate::vm::vmar::Vmar;
|
||||||
use jinux_frame::sync::WaitQueue;
|
use jinux_frame::sync::WaitQueue;
|
||||||
use jinux_frame::{task::Task, user::UserSpace, vm::VmSpace};
|
use jinux_frame::{task::Task, user::UserSpace};
|
||||||
|
|
||||||
pub mod clone;
|
pub mod clone;
|
||||||
pub mod elf;
|
pub mod elf;
|
||||||
@ -47,6 +48,7 @@ pub struct Process {
|
|||||||
filename: Option<CString>,
|
filename: Option<CString>,
|
||||||
user_space: Option<Arc<UserSpace>>,
|
user_space: Option<Arc<UserSpace>>,
|
||||||
user_vm: Option<UserVm>,
|
user_vm: Option<UserVm>,
|
||||||
|
root_vmar: Option<Vmar<Full>>,
|
||||||
/// wait for child status changed
|
/// wait for child status changed
|
||||||
waiting_children: WaitQueue,
|
waiting_children: WaitQueue,
|
||||||
/// wait for io events
|
/// wait for io events
|
||||||
@ -97,6 +99,7 @@ impl Process {
|
|||||||
exec_filename: Option<CString>,
|
exec_filename: Option<CString>,
|
||||||
user_vm: Option<UserVm>,
|
user_vm: Option<UserVm>,
|
||||||
user_space: Option<Arc<UserSpace>>,
|
user_space: Option<Arc<UserSpace>>,
|
||||||
|
root_vmar: Option<Vmar<Full>>,
|
||||||
process_group: Option<Weak<ProcessGroup>>,
|
process_group: Option<Weak<ProcessGroup>>,
|
||||||
file_table: FileTable,
|
file_table: FileTable,
|
||||||
sig_dispositions: SigDispositions,
|
sig_dispositions: SigDispositions,
|
||||||
@ -104,10 +107,8 @@ impl Process {
|
|||||||
sig_mask: SigMask,
|
sig_mask: SigMask,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let parent = if pid == 0 {
|
let parent = if pid == 0 {
|
||||||
debug!("Init process does not has parent");
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
debug!("All process except init should have parent");
|
|
||||||
let current_process = current!();
|
let current_process = current!();
|
||||||
Some(Arc::downgrade(¤t_process))
|
Some(Arc::downgrade(¤t_process))
|
||||||
};
|
};
|
||||||
@ -125,6 +126,7 @@ impl Process {
|
|||||||
filename: exec_filename,
|
filename: exec_filename,
|
||||||
user_space,
|
user_space,
|
||||||
user_vm,
|
user_vm,
|
||||||
|
root_vmar,
|
||||||
waiting_children,
|
waiting_children,
|
||||||
poll_queue,
|
poll_queue,
|
||||||
exit_code: AtomicI32::new(0),
|
exit_code: AtomicI32::new(0),
|
||||||
@ -189,8 +191,15 @@ impl Process {
|
|||||||
let user_process = Arc::new_cyclic(|weak_process_ref| {
|
let user_process = Arc::new_cyclic(|weak_process_ref| {
|
||||||
let weak_process = weak_process_ref.clone();
|
let weak_process = weak_process_ref.clone();
|
||||||
let cloned_filename = Some(filename.clone());
|
let cloned_filename = Some(filename.clone());
|
||||||
let task =
|
let root_vmar = Vmar::<Full>::new_root().unwrap();
|
||||||
create_user_task_from_elf(filename, elf_file_content, weak_process, argv, envp);
|
let task = create_user_task_from_elf(
|
||||||
|
&root_vmar,
|
||||||
|
filename,
|
||||||
|
elf_file_content,
|
||||||
|
weak_process,
|
||||||
|
argv,
|
||||||
|
envp,
|
||||||
|
);
|
||||||
let user_space = task.user_space().map(|user_space| user_space.clone());
|
let user_space = task.user_space().map(|user_space| user_space.clone());
|
||||||
let user_vm = UserVm::new();
|
let user_vm = UserVm::new();
|
||||||
let file_table = FileTable::new_with_stdio();
|
let file_table = FileTable::new_with_stdio();
|
||||||
@ -203,6 +212,7 @@ impl Process {
|
|||||||
cloned_filename,
|
cloned_filename,
|
||||||
Some(user_vm),
|
Some(user_vm),
|
||||||
user_space,
|
user_space,
|
||||||
|
Some(root_vmar),
|
||||||
None,
|
None,
|
||||||
file_table,
|
file_table,
|
||||||
sig_dispositions,
|
sig_dispositions,
|
||||||
@ -239,6 +249,7 @@ impl Process {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
file_table,
|
file_table,
|
||||||
sig_dispositions,
|
sig_dispositions,
|
||||||
sig_queues,
|
sig_queues,
|
||||||
@ -372,19 +383,16 @@ impl Process {
|
|||||||
self.user_space.as_ref()
|
self.user_space.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the vm space if the process does have, otherwise None
|
|
||||||
pub fn vm_space(&self) -> Option<&VmSpace> {
|
|
||||||
match self.user_space {
|
|
||||||
None => None,
|
|
||||||
Some(ref user_space) => Some(user_space.vm_space()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the user_vm
|
/// returns the user_vm
|
||||||
pub fn user_vm(&self) -> Option<&UserVm> {
|
pub fn user_vm(&self) -> Option<&UserVm> {
|
||||||
self.user_vm.as_ref()
|
self.user_vm.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns the root vmar
|
||||||
|
pub fn root_vmar(&self) -> Option<&Vmar<Full>> {
|
||||||
|
self.root_vmar.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// returns the user heap if the process does have, otherwise None
|
/// returns the user heap if the process does have, otherwise None
|
||||||
pub fn user_heap(&self) -> Option<&UserHeap> {
|
pub fn user_heap(&self) -> Option<&UserHeap> {
|
||||||
match self.user_vm {
|
match self.user_vm {
|
||||||
@ -393,14 +401,6 @@ impl Process {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the mmap area if the process does have, otherwise None
|
|
||||||
pub fn mmap_area(&self) -> Option<&MmapArea> {
|
|
||||||
match self.user_vm {
|
|
||||||
None => None,
|
|
||||||
Some(ref user_vm) => Some(user_vm.mmap_area()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// free zombie child with pid, returns the exit code of child process.
|
/// free zombie child with pid, returns the exit code of child process.
|
||||||
/// remove process from process group.
|
/// remove process from process group.
|
||||||
pub fn reap_zombie_child(&self, pid: Pid) -> i32 {
|
pub fn reap_zombie_child(&self, pid: Pid) -> i32 {
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
use crate::{memory::vm_page::VmPageRange, prelude::*, process::elf::init_stack::INIT_STACK_BASE};
|
|
||||||
use jinux_frame::vm::{VmPerm, VmSpace};
|
|
||||||
|
|
||||||
// The definition of MMapFlags is from occlum
|
|
||||||
bitflags! {
|
|
||||||
pub struct MMapFlags : u32 {
|
|
||||||
const MAP_FILE = 0x0;
|
|
||||||
const MAP_SHARED = 0x1;
|
|
||||||
const MAP_PRIVATE = 0x2;
|
|
||||||
const MAP_SHARED_VALIDATE = 0x3;
|
|
||||||
const MAP_TYPE = 0xf;
|
|
||||||
const MAP_FIXED = 0x10;
|
|
||||||
const MAP_ANONYMOUS = 0x20;
|
|
||||||
const MAP_GROWSDOWN = 0x100;
|
|
||||||
const MAP_DENYWRITE = 0x800;
|
|
||||||
const MAP_EXECUTABLE = 0x1000;
|
|
||||||
const MAP_LOCKED = 0x2000;
|
|
||||||
const MAP_NORESERVE = 0x4000;
|
|
||||||
const MAP_POPULATE = 0x8000;
|
|
||||||
const MAP_NONBLOCK = 0x10000;
|
|
||||||
const MAP_STACK = 0x20000;
|
|
||||||
const MAP_HUGETLB = 0x40000;
|
|
||||||
const MAP_SYNC = 0x80000;
|
|
||||||
const MAP_FIXED_NOREPLACE = 0x100000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u64> for MMapFlags {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: u64) -> Result<Self> {
|
|
||||||
MMapFlags::from_bits(value as u32)
|
|
||||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "unknown mmap flags"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct MmapArea {
|
|
||||||
base_addr: Vaddr,
|
|
||||||
current: AtomicUsize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MmapArea {
|
|
||||||
pub const fn new() -> MmapArea {
|
|
||||||
MmapArea {
|
|
||||||
base_addr: INIT_STACK_BASE,
|
|
||||||
current: AtomicUsize::new(INIT_STACK_BASE),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mmap(
|
|
||||||
&self,
|
|
||||||
len: usize,
|
|
||||||
offset: usize,
|
|
||||||
vm_perm: VmPerm,
|
|
||||||
flags: MMapFlags,
|
|
||||||
vm_space: &VmSpace,
|
|
||||||
) -> Vaddr {
|
|
||||||
// TODO: how to respect flags?
|
|
||||||
if flags.complement().contains(MMapFlags::MAP_ANONYMOUS)
|
|
||||||
| flags.complement().contains(MMapFlags::MAP_PRIVATE)
|
|
||||||
{
|
|
||||||
panic!("Unsupported mmap flags {:?} now", flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if len % PAGE_SIZE != 0 {
|
|
||||||
panic!("Mmap only support page-aligned len");
|
|
||||||
}
|
|
||||||
if offset % PAGE_SIZE != 0 {
|
|
||||||
panic!("Mmap only support page-aligned offset");
|
|
||||||
}
|
|
||||||
|
|
||||||
let current = self.current.load(Ordering::Relaxed);
|
|
||||||
let vm_page_range = VmPageRange::new_range(current..(current + len));
|
|
||||||
vm_page_range.map_zeroed(vm_space, vm_perm);
|
|
||||||
self.current.store(current + len, Ordering::Relaxed);
|
|
||||||
debug!("mmap area start: 0x{:x}, size: {}", current, len);
|
|
||||||
current
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set mmap area to the default status. i.e., point current to base.
|
|
||||||
pub fn set_default(&self) {
|
|
||||||
self.current.store(self.base_addr, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for MmapArea {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let current = self.current.load(Ordering::Relaxed);
|
|
||||||
Self {
|
|
||||||
base_addr: self.base_addr.clone(),
|
|
||||||
current: AtomicUsize::new(current),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,34 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
// The definition of MMapFlags is from occlum
|
||||||
|
bitflags! {
|
||||||
|
pub struct MMapFlags : u32 {
|
||||||
|
const MAP_FILE = 0x0;
|
||||||
|
const MAP_SHARED = 0x1;
|
||||||
|
const MAP_PRIVATE = 0x2;
|
||||||
|
const MAP_SHARED_VALIDATE = 0x3;
|
||||||
|
const MAP_TYPE = 0xf;
|
||||||
|
const MAP_FIXED = 0x10;
|
||||||
|
const MAP_ANONYMOUS = 0x20;
|
||||||
|
const MAP_GROWSDOWN = 0x100;
|
||||||
|
const MAP_DENYWRITE = 0x800;
|
||||||
|
const MAP_EXECUTABLE = 0x1000;
|
||||||
|
const MAP_LOCKED = 0x2000;
|
||||||
|
const MAP_NORESERVE = 0x4000;
|
||||||
|
const MAP_POPULATE = 0x8000;
|
||||||
|
const MAP_NONBLOCK = 0x10000;
|
||||||
|
const MAP_STACK = 0x20000;
|
||||||
|
const MAP_HUGETLB = 0x40000;
|
||||||
|
const MAP_SYNC = 0x80000;
|
||||||
|
const MAP_FIXED_NOREPLACE = 0x100000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u64> for MMapFlags {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: u64) -> Result<Self> {
|
||||||
|
MMapFlags::from_bits(value as u32)
|
||||||
|
.ok_or_else(|| Error::with_message(Errno::EINVAL, "unknown mmap flags"))
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,9 @@
|
|||||||
//! So we define a UserVm struct to store such infomation.
|
//! So we define a UserVm struct to store such infomation.
|
||||||
//! Briefly, it contains the exact usage of each segment of virtual spaces.
|
//! Briefly, it contains the exact usage of each segment of virtual spaces.
|
||||||
|
|
||||||
pub mod mmap_area;
|
pub mod mmap_flags;
|
||||||
pub mod user_heap;
|
pub mod user_heap;
|
||||||
|
|
||||||
use mmap_area::MmapArea;
|
|
||||||
use user_heap::UserHeap;
|
use user_heap::UserHeap;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -41,30 +40,20 @@ use user_heap::UserHeap;
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UserVm {
|
pub struct UserVm {
|
||||||
user_heap: UserHeap,
|
user_heap: UserHeap,
|
||||||
mmap_area: MmapArea,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserVm {
|
impl UserVm {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
let user_heap = UserHeap::new();
|
let user_heap = UserHeap::new();
|
||||||
let mmap_area = MmapArea::new();
|
UserVm { user_heap }
|
||||||
UserVm {
|
|
||||||
user_heap,
|
|
||||||
mmap_area,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_heap(&self) -> &UserHeap {
|
pub fn user_heap(&self) -> &UserHeap {
|
||||||
&self.user_heap
|
&self.user_heap
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mmap_area(&self) -> &MmapArea {
|
|
||||||
&self.mmap_area
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set user vm to the init status
|
/// Set user vm to the init status
|
||||||
pub fn set_default(&self) {
|
pub fn set_default(&self) {
|
||||||
self.user_heap.set_default();
|
self.user_heap.set_default();
|
||||||
self.mmap_area.set_default();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use crate::vm::perms::VmPerms;
|
||||||
use crate::{
|
use crate::{
|
||||||
memory::vm_page::{VmPage, VmPageRange},
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
rights::Rights,
|
||||||
|
vm::vmo::{VmoFlags, VmoOptions},
|
||||||
};
|
};
|
||||||
use jinux_frame::vm::{VmPerm, VmSpace};
|
use jinux_frame::AlignExt;
|
||||||
|
|
||||||
pub const USER_HEAP_BASE: Vaddr = 0x0000_0000_1000_0000;
|
pub const USER_HEAP_BASE: Vaddr = 0x0000_0000_1000_0000;
|
||||||
|
pub const USER_HEAP_SIZE_LIMIT: usize = PAGE_SIZE * 1000;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UserHeap {
|
pub struct UserHeap {
|
||||||
@ -23,37 +26,38 @@ impl UserHeap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn brk(&self, new_heap_end: Option<Vaddr>, vm_space: &VmSpace) -> Vaddr {
|
pub fn brk(&self, new_heap_end: Option<Vaddr>) -> Result<Vaddr> {
|
||||||
|
let current = current!();
|
||||||
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
match new_heap_end {
|
match new_heap_end {
|
||||||
None => return self.current_heap_end.load(Ordering::Relaxed),
|
None => {
|
||||||
|
// create a heap vmo for current process
|
||||||
|
let perms = VmPerms::READ | VmPerms::WRITE;
|
||||||
|
let vmo_options = VmoOptions::<Rights>::new(0).flags(VmoFlags::RESIZABLE);
|
||||||
|
let heap_vmo = vmo_options.alloc().unwrap();
|
||||||
|
let vmar_map_options = root_vmar
|
||||||
|
.new_map(heap_vmo, perms)
|
||||||
|
.unwrap()
|
||||||
|
.offset(USER_HEAP_BASE)
|
||||||
|
.size(USER_HEAP_SIZE_LIMIT);
|
||||||
|
vmar_map_options.build().unwrap();
|
||||||
|
return Ok(self.current_heap_end.load(Ordering::Relaxed));
|
||||||
|
}
|
||||||
Some(new_heap_end) => {
|
Some(new_heap_end) => {
|
||||||
let current_heap_end = self.current_heap_end.load(Ordering::Acquire);
|
let current_heap_end = self.current_heap_end.load(Ordering::Acquire);
|
||||||
if new_heap_end < current_heap_end {
|
if new_heap_end < current_heap_end {
|
||||||
return current_heap_end;
|
// FIXME: should we allow shrink current user heap?
|
||||||
|
return Ok(current_heap_end);
|
||||||
}
|
}
|
||||||
|
let new_size = (new_heap_end - self.heap_base).align_up(PAGE_SIZE);
|
||||||
|
let heap_vmo = root_vmar.get_mapped_vmo(USER_HEAP_BASE)?;
|
||||||
|
heap_vmo.resize(new_size)?;
|
||||||
self.current_heap_end.store(new_heap_end, Ordering::Release);
|
self.current_heap_end.store(new_heap_end, Ordering::Release);
|
||||||
let start_page = VmPage::containing_address(current_heap_end - 1).next_page();
|
return Ok(new_heap_end);
|
||||||
let end_page = VmPage::containing_address(new_heap_end);
|
|
||||||
if end_page >= start_page {
|
|
||||||
let vm_pages = VmPageRange::new_page_range(start_page, end_page);
|
|
||||||
let vm_perm = UserHeap::user_heap_perm();
|
|
||||||
vm_pages.map_zeroed(vm_space, vm_perm);
|
|
||||||
debug!(
|
|
||||||
"map address: 0x{:x} - 0x{:x}",
|
|
||||||
vm_pages.start_address(),
|
|
||||||
vm_pages.end_address()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return new_heap_end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
const fn user_heap_perm() -> VmPerm {
|
|
||||||
VmPerm::RWXU
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set heap to the default status. i.e., point the heap end to heap base.
|
/// Set heap to the default status. i.e., point the heap end to heap base.
|
||||||
pub fn set_default(&self) {
|
pub fn set_default(&self) {
|
||||||
self.current_heap_end
|
self.current_heap_end
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
|
||||||
use jinux_frame::{cpu::GpRegs, offset_of};
|
use jinux_frame::cpu::GpRegs;
|
||||||
use jinux_util::{read_union_fields, union_read_ptr::UnionReadPtr};
|
use jinux_util::{read_union_fields, union_read_ptr::UnionReadPtr};
|
||||||
|
|
||||||
use crate::{prelude::*, process::Pid};
|
use crate::{prelude::*, process::Pid};
|
||||||
|
@ -15,9 +15,9 @@ use jinux_frame::{cpu::CpuContext, task::Task};
|
|||||||
use self::c_types::siginfo_t;
|
use self::c_types::siginfo_t;
|
||||||
use self::sig_mask::SigMask;
|
use self::sig_mask::SigMask;
|
||||||
use self::sig_num::SigNum;
|
use self::sig_num::SigNum;
|
||||||
use crate::memory::{write_bytes_to_user, write_val_to_user};
|
|
||||||
use crate::process::signal::c_types::ucontext_t;
|
use crate::process::signal::c_types::ucontext_t;
|
||||||
use crate::process::signal::sig_action::SigActionFlags;
|
use crate::process::signal::sig_action::SigActionFlags;
|
||||||
|
use crate::util::{write_bytes_to_user, write_val_to_user};
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::signal::sig_action::{SigAction, SigDefaultAction},
|
process::signal::sig_action::{SigAction, SigDefaultAction},
|
||||||
|
@ -4,36 +4,37 @@ use jinux_frame::{
|
|||||||
cpu::CpuContext,
|
cpu::CpuContext,
|
||||||
task::Task,
|
task::Task,
|
||||||
user::{UserEvent, UserMode, UserSpace},
|
user::{UserEvent, UserMode, UserSpace},
|
||||||
vm::VmSpace,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::{exception::handle_exception, signal::handle_pending_signal},
|
process::{exception::handle_exception, signal::handle_pending_signal},
|
||||||
|
rights::Full,
|
||||||
|
vm::vmar::Vmar,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::syscall::handle_syscall;
|
use crate::syscall::handle_syscall;
|
||||||
|
|
||||||
use super::{elf::load_elf_to_vm_space, Process};
|
use super::{elf::load_elf_to_root_vmar, Process};
|
||||||
|
|
||||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
pub fn create_user_task_from_elf(
|
pub fn create_user_task_from_elf(
|
||||||
|
root_vmar: &Vmar<Full>,
|
||||||
filename: CString,
|
filename: CString,
|
||||||
elf_file_content: &[u8],
|
elf_file_content: &'static [u8],
|
||||||
parent: Weak<Process>,
|
parent: Weak<Process>,
|
||||||
argv: Vec<CString>,
|
argv: Vec<CString>,
|
||||||
envp: Vec<CString>,
|
envp: Vec<CString>,
|
||||||
) -> Arc<Task> {
|
) -> Arc<Task> {
|
||||||
let vm_space = VmSpace::new();
|
let elf_load_info = load_elf_to_root_vmar(filename, elf_file_content, &root_vmar, argv, envp)
|
||||||
let elf_load_info = load_elf_to_vm_space(filename, elf_file_content, &vm_space, argv, envp)
|
|
||||||
.expect("Load Elf failed");
|
.expect("Load Elf failed");
|
||||||
|
let vm_space = root_vmar.vm_space().clone();
|
||||||
let mut cpu_ctx = CpuContext::default();
|
let mut cpu_ctx = CpuContext::default();
|
||||||
// set entry point
|
// set entry point
|
||||||
cpu_ctx.gp_regs.rip = elf_load_info.entry_point();
|
cpu_ctx.gp_regs.rip = elf_load_info.entry_point();
|
||||||
// set user stack
|
// set user stack
|
||||||
cpu_ctx.gp_regs.rsp = elf_load_info.user_stack_top();
|
cpu_ctx.gp_regs.rsp = elf_load_info.user_stack_top();
|
||||||
|
|
||||||
let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx));
|
let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx));
|
||||||
create_new_task(user_space, parent)
|
create_new_task(user_space, parent)
|
||||||
}
|
}
|
||||||
@ -44,8 +45,7 @@ pub fn create_new_task(userspace: Arc<UserSpace>, parent: Weak<Process>) -> Arc<
|
|||||||
let cur = Task::current();
|
let cur = Task::current();
|
||||||
let user_space = cur.user_space().expect("user task should have user space");
|
let user_space = cur.user_space().expect("user task should have user space");
|
||||||
let mut user_mode = UserMode::new(user_space);
|
let mut user_mode = UserMode::new(user_space);
|
||||||
debug!("In new task");
|
debug!("In user task entry:");
|
||||||
debug!("[new task] pid = {}", current!().pid());
|
|
||||||
debug!("[new task] rip = 0x{:x}", user_space.cpu_ctx.gp_regs.rip);
|
debug!("[new task] rip = 0x{:x}", user_space.cpu_ctx.gp_regs.rip);
|
||||||
debug!("[new task] rsp = 0x{:x}", user_space.cpu_ctx.gp_regs.rsp);
|
debug!("[new task] rsp = 0x{:x}", user_space.cpu_ctx.gp_regs.rsp);
|
||||||
debug!("[new task] rax = 0x{:x}", user_space.cpu_ctx.gp_regs.rax);
|
debug!("[new task] rax = 0x{:x}", user_space.cpu_ctx.gp_regs.rax);
|
||||||
@ -72,7 +72,7 @@ pub fn create_new_task(userspace: Arc<UserSpace>, parent: Weak<Process>) -> Arc<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("exit user loop");
|
debug!("exit user loop");
|
||||||
// Work around: exit in kernel task entry may be not called. Why this will happen?
|
// FIXME: This is a work around: exit in kernel task entry may be not called. Why this will happen?
|
||||||
Task::current().exit();
|
Task::current().exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use super::{constants::*, SyscallReturn};
|
use super::{constants::*, SyscallReturn};
|
||||||
use crate::{memory::read_cstring_from_user, prelude::*, syscall::SYS_ACCESS};
|
use crate::{log_syscall_entry, prelude::*, syscall::SYS_ACCESS, util::read_cstring_from_user};
|
||||||
|
|
||||||
pub fn sys_access(filename_ptr: Vaddr, file_mode: u64) -> Result<SyscallReturn> {
|
pub fn sys_access(filename_ptr: Vaddr, file_mode: u64) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_ACCESS]", SYS_ACCESS);
|
log_syscall_entry!(SYS_ACCESS);
|
||||||
let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
|
let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
|
||||||
debug!("filename: {:?}, file_mode = {}", filename, file_mode);
|
debug!("filename: {:?}, file_mode = {}", filename, file_mode);
|
||||||
// TODO: access currenly does not check and just return success
|
// TODO: access currenly does not check and just return success
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use jinux_frame::cpu::CpuContext;
|
use jinux_frame::cpu::CpuContext;
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::syscall::SYS_ARCH_PRCTL;
|
use crate::syscall::SYS_ARCH_PRCTL;
|
||||||
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ impl TryFrom<u64> for ArchPrctlCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_arch_prctl(code: u64, addr: u64, context: &mut CpuContext) -> Result<SyscallReturn> {
|
pub fn sys_arch_prctl(code: u64, addr: u64, context: &mut CpuContext) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_ARCH_PRCTL]", SYS_ARCH_PRCTL);
|
log_syscall_entry!(SYS_ARCH_PRCTL);
|
||||||
let arch_prctl_code = ArchPrctlCode::try_from(code)?;
|
let arch_prctl_code = ArchPrctlCode::try_from(code)?;
|
||||||
debug!(
|
debug!(
|
||||||
"arch_prctl_code: {:?}, addr = 0x{:x}",
|
"arch_prctl_code: {:?}, addr = 0x{:x}",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::log_syscall_entry;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use crate::syscall::SyscallReturn;
|
use crate::syscall::SyscallReturn;
|
||||||
@ -5,7 +6,7 @@ use crate::syscall::SYS_BRK;
|
|||||||
|
|
||||||
/// expand the user heap to new heap end, returns the new heap end if expansion succeeds.
|
/// expand the user heap to new heap end, returns the new heap end if expansion succeeds.
|
||||||
pub fn sys_brk(heap_end: u64) -> Result<SyscallReturn> {
|
pub fn sys_brk(heap_end: u64) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_BRK]", SYS_BRK);
|
log_syscall_entry!(SYS_BRK);
|
||||||
let new_heap_end = if heap_end == 0 {
|
let new_heap_end = if heap_end == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -13,13 +14,8 @@ pub fn sys_brk(heap_end: u64) -> Result<SyscallReturn> {
|
|||||||
};
|
};
|
||||||
debug!("new heap end = {:x?}", heap_end);
|
debug!("new heap end = {:x?}", heap_end);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let user_heap = current
|
let user_heap = current.user_heap().unwrap();
|
||||||
.user_heap()
|
let new_heap_end = user_heap.brk(new_heap_end)?;
|
||||||
.expect("brk should work on process with user heap");
|
|
||||||
let vm_space = current
|
|
||||||
.vm_space()
|
|
||||||
.expect("brk should work on process with user space");
|
|
||||||
let new_heap_end = user_heap.brk(new_heap_end, vm_space);
|
|
||||||
|
|
||||||
Ok(SyscallReturn::Return(new_heap_end as _))
|
Ok(SyscallReturn::Return(new_heap_end as _))
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use jinux_frame::cpu::CpuContext;
|
use jinux_frame::cpu::CpuContext;
|
||||||
|
|
||||||
|
use crate::log_syscall_entry;
|
||||||
use crate::process::clone::{clone_child, CloneArgs, CloneFlags};
|
use crate::process::clone::{clone_child, CloneArgs, CloneFlags};
|
||||||
use crate::{prelude::*, syscall::SYS_CLONE};
|
use crate::{prelude::*, syscall::SYS_CLONE};
|
||||||
|
|
||||||
@ -15,11 +16,12 @@ pub fn sys_clone(
|
|||||||
tls: usize,
|
tls: usize,
|
||||||
parent_context: CpuContext,
|
parent_context: CpuContext,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_CLONE]", SYS_CLONE);
|
log_syscall_entry!(SYS_CLONE);
|
||||||
let clone_flags = CloneFlags::from(clone_flags);
|
let clone_flags = CloneFlags::from(clone_flags);
|
||||||
debug!("flags = {:?}, child_stack_ptr = 0x{:x}, parent_tid_ptr = 0x{:x}, child tid ptr = 0x{:x}, tls = 0x{:x}", clone_flags, new_sp, parent_tidptr, child_tidptr, tls);
|
debug!("flags = {:?}, child_stack_ptr = 0x{:x}, parent_tid_ptr = 0x{:x}, child tid ptr = 0x{:x}, tls = 0x{:x}", clone_flags, new_sp, parent_tidptr, child_tidptr, tls);
|
||||||
let clone_args = CloneArgs::new(new_sp, parent_tidptr, child_tidptr, tls, clone_flags);
|
let clone_args = CloneArgs::new(new_sp, parent_tidptr, child_tidptr, tls, clone_flags);
|
||||||
let child_process = clone_child(parent_context, clone_args).unwrap();
|
let child_process = clone_child(parent_context, clone_args).unwrap();
|
||||||
|
|
||||||
let child_pid = child_process.pid();
|
let child_pid = child_process.pid();
|
||||||
let pid = current!().pid();
|
let pid = current!().pid();
|
||||||
debug!("*********schedule child process, pid = {}**********", pid);
|
debug!("*********schedule child process, pid = {}**********", pid);
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_CLOSE;
|
use super::SYS_CLOSE;
|
||||||
|
use crate::log_syscall_entry;
|
||||||
use crate::{fs::file::FileDescripter, prelude::*};
|
use crate::{fs::file::FileDescripter, prelude::*};
|
||||||
|
|
||||||
pub fn sys_close(fd: FileDescripter) -> Result<SyscallReturn> {
|
pub fn sys_close(fd: FileDescripter) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_CLOSE]", SYS_CLOSE);
|
log_syscall_entry!(SYS_CLOSE);
|
||||||
debug!("fd = {}", fd);
|
debug!("fd = {}", fd);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let mut file_table = current.file_table().lock();
|
let mut file_table = current.file_table().lock();
|
||||||
match file_table.get_file(fd) {
|
let _ = file_table.get_file(fd)?;
|
||||||
None => return_errno!(Errno::EBADF),
|
|
||||||
Some(_) => {
|
|
||||||
file_table.close_file(fd);
|
file_table.close_file(fd);
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use jinux_frame::cpu::CpuContext;
|
use jinux_frame::cpu::CpuContext;
|
||||||
|
|
||||||
use super::{constants::*, SyscallReturn};
|
use super::{constants::*, SyscallReturn};
|
||||||
use crate::memory::{read_cstring_from_user, read_val_from_user};
|
use crate::log_syscall_entry;
|
||||||
use crate::process::elf::load_elf_to_vm_space;
|
use crate::process::elf::load_elf_to_root_vmar;
|
||||||
|
use crate::util::{read_cstring_from_user, read_val_from_user};
|
||||||
use crate::{prelude::*, syscall::SYS_EXECVE};
|
use crate::{prelude::*, syscall::SYS_EXECVE};
|
||||||
|
|
||||||
pub fn sys_execve(
|
pub fn sys_execve(
|
||||||
@ -11,7 +12,7 @@ pub fn sys_execve(
|
|||||||
envp_ptr_ptr: Vaddr,
|
envp_ptr_ptr: Vaddr,
|
||||||
context: &mut CpuContext,
|
context: &mut CpuContext,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_EXECVE]", SYS_EXECVE);
|
log_syscall_entry!(SYS_EXECVE);
|
||||||
let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
|
let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
|
||||||
let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?;
|
let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?;
|
||||||
let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?;
|
let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?;
|
||||||
@ -25,17 +26,17 @@ pub fn sys_execve(
|
|||||||
|
|
||||||
let elf_file_content = crate::user_apps::read_execve_hello_content();
|
let elf_file_content = crate::user_apps::read_execve_hello_content();
|
||||||
let current = current!();
|
let current = current!();
|
||||||
// Set process vm space to default
|
// destroy root vmars
|
||||||
let vm_space = current
|
let root_vmar = current
|
||||||
.vm_space()
|
.root_vmar()
|
||||||
.expect("[Internal Error] User process should have vm space");
|
.expect("[Internal Error] User process should have vm space");
|
||||||
vm_space.clear();
|
root_vmar.clear()?;
|
||||||
let user_vm = current
|
let user_vm = current
|
||||||
.user_vm()
|
.user_vm()
|
||||||
.expect("[Internal Error] User process should have user vm");
|
.expect("[Internal Error] User process should have user vm");
|
||||||
user_vm.set_default();
|
user_vm.set_default();
|
||||||
// load elf content to new vm space
|
// load elf content to new vm space
|
||||||
let elf_load_info = load_elf_to_vm_space(filename, elf_file_content, &vm_space, argv, envp)
|
let elf_load_info = load_elf_to_root_vmar(filename, elf_file_content, root_vmar, argv, envp)
|
||||||
.expect("load elf failed");
|
.expect("load elf failed");
|
||||||
debug!("load elf in execve succeeds");
|
debug!("load elf in execve succeeds");
|
||||||
// set signal disposition to default
|
// set signal disposition to default
|
||||||
@ -74,7 +75,7 @@ fn read_cstring_vec(
|
|||||||
res.push(cstring);
|
res.push(cstring);
|
||||||
}
|
}
|
||||||
if !find_null {
|
if !find_null {
|
||||||
return_errno!(Errno::E2BIG);
|
return_errno_with_message!(Errno::E2BIG, "Cannot find null pointer in vector");
|
||||||
}
|
}
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::syscall::{SyscallReturn, SYS_EXIT};
|
use crate::syscall::{SyscallReturn, SYS_EXIT};
|
||||||
|
|
||||||
pub fn sys_exit(exit_code: i32) -> Result<SyscallReturn> {
|
pub fn sys_exit(exit_code: i32) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_EXIT]", SYS_EXIT);
|
log_syscall_entry!(SYS_EXIT);
|
||||||
current!().exit(exit_code);
|
current!().exit(exit_code);
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::syscall::{SyscallReturn, SYS_EXIT_GROUP};
|
use crate::syscall::{SyscallReturn, SYS_EXIT_GROUP};
|
||||||
|
|
||||||
/// Exit all thread in a process.
|
/// Exit all thread in a process.
|
||||||
pub fn sys_exit_group(exit_code: u64) -> Result<SyscallReturn> {
|
pub fn sys_exit_group(exit_code: u64) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_EXIT_GROUP]", SYS_EXIT_GROUP);
|
log_syscall_entry!(SYS_EXIT_GROUP);
|
||||||
current!().exit(exit_code as _);
|
current!().exit(exit_code as _);
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use super::{SyscallReturn, SYS_FCNTL};
|
use super::{SyscallReturn, SYS_FCNTL};
|
||||||
use crate::fs::fcntl::FcntlCmd;
|
use crate::fs::fcntl::FcntlCmd;
|
||||||
|
use crate::log_syscall_entry;
|
||||||
use crate::{fs::file::FileDescripter, prelude::*};
|
use crate::{fs::file::FileDescripter, prelude::*};
|
||||||
|
|
||||||
pub fn sys_fcntl(fd: FileDescripter, cmd: i32, arg: u64) -> Result<SyscallReturn> {
|
pub fn sys_fcntl(fd: FileDescripter, cmd: i32, arg: u64) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_FCNTL]", SYS_FCNTL);
|
log_syscall_entry!(SYS_FCNTL);
|
||||||
let fcntl_cmd = FcntlCmd::try_from(cmd)?;
|
let fcntl_cmd = FcntlCmd::try_from(cmd)?;
|
||||||
debug!("fd = {}, cmd = {:?}, arg = {}", fd, fcntl_cmd, arg);
|
debug!("fd = {}, cmd = {:?}, arg = {}", fd, fcntl_cmd, arg);
|
||||||
match fcntl_cmd {
|
match fcntl_cmd {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
log_syscall_entry,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::clone::{clone_child, CloneArgs},
|
process::clone::{clone_child, CloneArgs},
|
||||||
};
|
};
|
||||||
@ -9,7 +10,7 @@ use crate::{process::Process, syscall::SYS_FORK};
|
|||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_fork(parent_context: CpuContext) -> Result<SyscallReturn> {
|
pub fn sys_fork(parent_context: CpuContext) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_FORK]", SYS_FORK);
|
log_syscall_entry!(SYS_FORK);
|
||||||
let child_process = fork(parent_context);
|
let child_process = fork(parent_context);
|
||||||
Ok(SyscallReturn::Return(child_process.pid() as _))
|
Ok(SyscallReturn::Return(child_process.pid() as _))
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
use jinux_frame::vm::VmIo;
|
use jinux_frame::vm::VmIo;
|
||||||
|
|
||||||
use crate::fs::stat::Stat;
|
use crate::fs::stat::Stat;
|
||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::syscall::{SyscallReturn, SYS_FSTAT};
|
use crate::syscall::{SyscallReturn, SYS_FSTAT};
|
||||||
|
|
||||||
pub fn sys_fstat(fd: u64, stat_buf_ptr: Vaddr) -> Result<SyscallReturn> {
|
pub fn sys_fstat(fd: u64, stat_buf_ptr: Vaddr) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_FSTAT]", SYS_FSTAT);
|
log_syscall_entry!(SYS_FSTAT);
|
||||||
debug!("fd = {}, stat_buf_addr = 0x{:x}", fd, stat_buf_ptr);
|
debug!("fd = {}, stat_buf_addr = 0x{:x}", fd, stat_buf_ptr);
|
||||||
|
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let vm_space = current.vm_space().unwrap();
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
if fd == 1 {
|
if fd == 1 {
|
||||||
let stat = Stat::stdout_stat();
|
let stat = Stat::stdout_stat();
|
||||||
vm_space.write_val(stat_buf_ptr, &stat)?;
|
root_vmar.write_val(stat_buf_ptr, &stat)?;
|
||||||
return Ok(SyscallReturn::Return(0));
|
return Ok(SyscallReturn::Return(0));
|
||||||
}
|
}
|
||||||
// TODO: fstat only returns fake result now
|
// TODO: fstat only returns fake result now
|
||||||
|
@ -2,9 +2,10 @@ use core::sync::atomic::{AtomicBool, Ordering};
|
|||||||
|
|
||||||
use crate::process::{Pid, Process};
|
use crate::process::{Pid, Process};
|
||||||
use crate::syscall::SyscallReturn;
|
use crate::syscall::SyscallReturn;
|
||||||
use crate::{memory::read_val_from_user, syscall::SYS_FUTEX};
|
use crate::syscall::SYS_FUTEX;
|
||||||
|
use crate::util::read_val_from_user;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
use jinux_frame::cpu::num_cpus;
|
use jinux_frame::cpu::num_cpus;
|
||||||
|
|
||||||
type FutexBitSet = u32;
|
type FutexBitSet = u32;
|
||||||
@ -22,7 +23,7 @@ pub fn sys_futex(
|
|||||||
futex_new_addr: u64,
|
futex_new_addr: u64,
|
||||||
bitset: u64,
|
bitset: u64,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_FUTEX]", SYS_FUTEX);
|
log_syscall_entry!(SYS_FUTEX);
|
||||||
// FIXME: we current ignore futex flags
|
// FIXME: we current ignore futex flags
|
||||||
let (futex_op, futex_flags) = futex_op_and_flags_from_u32(futex_op as _).unwrap();
|
let (futex_op, futex_flags) = futex_op_and_flags_from_u32(futex_op as _).unwrap();
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use crate::memory::write_bytes_to_user;
|
use crate::log_syscall_entry;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::util::write_bytes_to_user;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_GETCWD;
|
use super::SYS_GETCWD;
|
||||||
|
|
||||||
pub fn sys_getcwd(buf: Vaddr, len: usize) -> Result<SyscallReturn> {
|
pub fn sys_getcwd(buf: Vaddr, len: usize) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETCWD]", SYS_GETCWD);
|
log_syscall_entry!(SYS_GETCWD);
|
||||||
// TODO: getcwd only return a fake result now
|
// TODO: getcwd only return a fake result now
|
||||||
let fake_cwd = CString::new("/")?;
|
let fake_cwd = CString::new("/")?;
|
||||||
let bytes = fake_cwd.as_bytes_with_nul();
|
let bytes = fake_cwd.as_bytes_with_nul();
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::{prelude::*, syscall::SYS_GETEGID};
|
use crate::{log_syscall_entry, prelude::*, syscall::SYS_GETEGID};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_getegid() -> Result<SyscallReturn> {
|
pub fn sys_getegid() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETEGID]", SYS_GETEGID);
|
log_syscall_entry!(SYS_GETEGID);
|
||||||
// TODO: getegid only return a fake egid now
|
// TODO: getegid only return a fake egid now
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::{prelude::*, syscall::SYS_GETEUID};
|
use crate::{log_syscall_entry, prelude::*, syscall::SYS_GETEUID};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_geteuid() -> Result<SyscallReturn> {
|
pub fn sys_geteuid() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETEUID]", SYS_GETEUID);
|
log_syscall_entry!(SYS_GETEUID);
|
||||||
// TODO: geteuid only return a fake euid now"
|
// TODO: geteuid only return a fake euid now"
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::{prelude::*, syscall::SYS_GETGID};
|
use crate::{log_syscall_entry, prelude::*, syscall::SYS_GETGID};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_getgid() -> Result<SyscallReturn> {
|
pub fn sys_getgid() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETGID]", SYS_GETGID);
|
log_syscall_entry!(SYS_GETGID);
|
||||||
// TODO: getgid only return a fake gid now"
|
// TODO: getgid only return a fake gid now"
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use super::{SyscallReturn, SYS_GETPGRP};
|
use super::{SyscallReturn, SYS_GETPGRP};
|
||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
pub fn sys_getpgrp() -> Result<SyscallReturn> {
|
pub fn sys_getpgrp() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETPGRP]", SYS_GETPGRP);
|
log_syscall_entry!(SYS_GETPGRP);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
Ok(SyscallReturn::Return(current.pgid() as _))
|
Ok(SyscallReturn::Return(current.pgid() as _))
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::syscall::SYS_GETPID;
|
use crate::syscall::SYS_GETPID;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_getpid() -> Result<SyscallReturn> {
|
pub fn sys_getpid() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETPID]", SYS_GETPID);
|
log_syscall_entry!(SYS_GETPID);
|
||||||
let pid = current!().pid();
|
let pid = current!().pid();
|
||||||
debug!("[sys_getpid]: pid = {}", pid);
|
debug!("[sys_getpid]: pid = {}", pid);
|
||||||
Ok(SyscallReturn::Return(pid as _))
|
Ok(SyscallReturn::Return(pid as _))
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
use crate::log_syscall_entry;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_GETPPID;
|
use super::SYS_GETPPID;
|
||||||
|
|
||||||
pub fn sys_getppid() -> Result<SyscallReturn> {
|
pub fn sys_getppid() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETPPID]", SYS_GETPPID);
|
log_syscall_entry!(SYS_GETPPID);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let parent = current.parent();
|
let parent = current.parent();
|
||||||
match parent {
|
match parent {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::syscall::SYS_GETTID;
|
use crate::syscall::SYS_GETTID;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_gettid() -> Result<SyscallReturn> {
|
pub fn sys_gettid() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETTID]", SYS_GETTID);
|
log_syscall_entry!(SYS_GETTID);
|
||||||
// For single-thread process, tid is equal to pid
|
// For single-thread process, tid is equal to pid
|
||||||
let tid = current!().pid();
|
let tid = current!().pid();
|
||||||
Ok(SyscallReturn::Return(tid as _))
|
Ok(SyscallReturn::Return(tid as _))
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::{prelude::*, syscall::SYS_GETUID};
|
use crate::{log_syscall_entry, prelude::*, syscall::SYS_GETUID};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_getuid() -> Result<SyscallReturn> {
|
pub fn sys_getuid() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_GETUID]", SYS_GETUID);
|
log_syscall_entry!(SYS_GETUID);
|
||||||
// TODO: getuid only return a fake uid now;
|
// TODO: getuid only return a fake uid now;
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use crate::fs::file::FileDescripter;
|
use crate::fs::file::FileDescripter;
|
||||||
use crate::fs::ioctl::IoctlCmd;
|
use crate::fs::ioctl::IoctlCmd;
|
||||||
|
use crate::log_syscall_entry;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_IOCTL;
|
use super::SYS_IOCTL;
|
||||||
|
|
||||||
pub fn sys_ioctl(fd: FileDescripter, cmd: u32, arg: Vaddr) -> Result<SyscallReturn> {
|
pub fn sys_ioctl(fd: FileDescripter, cmd: u32, arg: Vaddr) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_IOCTL]", SYS_IOCTL);
|
log_syscall_entry!(SYS_IOCTL);
|
||||||
let ioctl_cmd = IoctlCmd::try_from(cmd)?;
|
let ioctl_cmd = IoctlCmd::try_from(cmd)?;
|
||||||
debug!(
|
debug!(
|
||||||
"fd = {}, ioctl_cmd = {:?}, arg = 0x{:x}",
|
"fd = {}, ioctl_cmd = {:?}, arg = 0x{:x}",
|
||||||
@ -14,11 +15,7 @@ pub fn sys_ioctl(fd: FileDescripter, cmd: u32, arg: Vaddr) -> Result<SyscallRetu
|
|||||||
);
|
);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let file_table = current.file_table().lock();
|
let file_table = current.file_table().lock();
|
||||||
match file_table.get_file(fd) {
|
let file = file_table.get_file(fd)?;
|
||||||
None => return_errno_with_message!(Errno::EBADF, "Fd does not exist"),
|
|
||||||
Some(file) => {
|
|
||||||
let res = file.ioctl(ioctl_cmd, arg)?;
|
let res = file.ioctl(ioctl_cmd, arg)?;
|
||||||
return Ok(SyscallReturn::Return(res as _));
|
return Ok(SyscallReturn::Return(res as _));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::process::signal::signals::user::{UserSignal, UserSignalKind};
|
use crate::process::signal::signals::user::{UserSignal, UserSignalKind};
|
||||||
use crate::process::{table, Process};
|
use crate::process::{table, Process};
|
||||||
@ -10,7 +10,7 @@ use crate::{
|
|||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_kill(process_filter: u64, sig_num: u64) -> Result<SyscallReturn> {
|
pub fn sys_kill(process_filter: u64, sig_num: u64) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_KILL]", SYS_KILL);
|
log_syscall_entry!(SYS_KILL);
|
||||||
let process_filter = ProcessFilter::from_id(process_filter as _);
|
let process_filter = ProcessFilter::from_id(process_filter as _);
|
||||||
let sig_num = SigNum::try_from(sig_num as u8).unwrap();
|
let sig_num = SigNum::try_from(sig_num as u8).unwrap();
|
||||||
debug!(
|
debug!(
|
||||||
@ -49,7 +49,9 @@ fn get_processes(filter: &ProcessFilter) -> Result<Vec<Arc<Process>>> {
|
|||||||
ProcessFilter::WithPid(pid) => {
|
ProcessFilter::WithPid(pid) => {
|
||||||
let process = table::pid_to_process(*pid);
|
let process = table::pid_to_process(*pid);
|
||||||
match process {
|
match process {
|
||||||
None => return_errno!(Errno::ESRCH),
|
None => {
|
||||||
|
return_errno_with_message!(Errno::ESRCH, "No such process in process table")
|
||||||
|
}
|
||||||
Some(process) => vec![process],
|
Some(process) => vec![process],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
use crate::log_syscall_entry;
|
||||||
use crate::{fs::file::FileDescripter, prelude::*};
|
use crate::{fs::file::FileDescripter, prelude::*};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_LSEEK;
|
use super::SYS_LSEEK;
|
||||||
|
|
||||||
pub fn sys_lseek(fd: FileDescripter, offset: usize, whence: u32) -> Result<SyscallReturn> {
|
pub fn sys_lseek(fd: FileDescripter, offset: usize, whence: u32) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_LSEEK]", SYS_LSEEK);
|
log_syscall_entry!(SYS_LSEEK);
|
||||||
debug!("fd = {}, offset = {}, whence = {}", fd, offset, whence);
|
debug!("fd = {}, offset = {}, whence = {}", fd, offset, whence);
|
||||||
// TODO: do lseek
|
// TODO: do lseek
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
use crate::fs::stat::Stat;
|
use crate::fs::stat::Stat;
|
||||||
use crate::memory::read_cstring_from_user;
|
use crate::log_syscall_entry;
|
||||||
use crate::memory::write_val_to_user;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::syscall::constants::MAX_FILENAME_LEN;
|
use crate::syscall::constants::MAX_FILENAME_LEN;
|
||||||
|
use crate::util::read_cstring_from_user;
|
||||||
|
use crate::util::write_val_to_user;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_LSTAT;
|
use super::SYS_LSTAT;
|
||||||
|
|
||||||
pub fn sys_lstat(filename_ptr: Vaddr, stat_buf_ptr: Vaddr) -> Result<SyscallReturn> {
|
pub fn sys_lstat(filename_ptr: Vaddr, stat_buf_ptr: Vaddr) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_LSTAT]", SYS_LSTAT);
|
log_syscall_entry!(SYS_LSTAT);
|
||||||
let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
|
let filename = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
|
||||||
debug!(
|
debug!(
|
||||||
"filename = {:?}, stat_buf_ptr = 0x{:x}",
|
"filename = {:?}, stat_buf_ptr = 0x{:x}",
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
//! This mod defines mmap flags and the handler to syscall mmap
|
//! This mod defines mmap flags and the handler to syscall mmap
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::process::process_vm::mmap_flags::MMapFlags;
|
||||||
use crate::process::process_vm::mmap_area::MMapFlags;
|
use crate::rights::Rights;
|
||||||
|
use crate::vm::perms::VmPerms;
|
||||||
|
use crate::vm::vmo::VmoOptions;
|
||||||
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
use jinux_frame::vm::VmPerm;
|
use jinux_frame::vm::VmPerm;
|
||||||
|
|
||||||
use crate::syscall::SYS_MMAP;
|
use crate::syscall::SYS_MMAP;
|
||||||
@ -16,7 +19,7 @@ pub fn sys_mmap(
|
|||||||
fd: u64,
|
fd: u64,
|
||||||
offset: u64,
|
offset: u64,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_MMAP]", SYS_MMAP);
|
log_syscall_entry!(SYS_MMAP);
|
||||||
let perms = VmPerm::try_from(perms).unwrap();
|
let perms = VmPerm::try_from(perms).unwrap();
|
||||||
let flags = MMapFlags::try_from(flags).unwrap();
|
let flags = MMapFlags::try_from(flags).unwrap();
|
||||||
let res = do_sys_mmap(
|
let res = do_sys_mmap(
|
||||||
@ -26,7 +29,7 @@ pub fn sys_mmap(
|
|||||||
flags,
|
flags,
|
||||||
fd as usize,
|
fd as usize,
|
||||||
offset as usize,
|
offset as usize,
|
||||||
);
|
)?;
|
||||||
Ok(SyscallReturn::Return(res as _))
|
Ok(SyscallReturn::Return(res as _))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,25 +40,47 @@ pub fn do_sys_mmap(
|
|||||||
flags: MMapFlags,
|
flags: MMapFlags,
|
||||||
fd: usize,
|
fd: usize,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
) -> Vaddr {
|
) -> Result<Vaddr> {
|
||||||
debug!(
|
info!(
|
||||||
"addr = 0x{:x}, len = 0x{:x}, perms = {:?}, flags = {:?}, fd = {}, offset = 0x{:x}",
|
"addr = 0x{:x}, len = 0x{:x}, perms = {:?}, flags = {:?}, fd = {}, offset = 0x{:x}",
|
||||||
addr, len, vm_perm, flags, fd, offset
|
addr, len, vm_perm, flags, fd, offset
|
||||||
);
|
);
|
||||||
|
|
||||||
if flags.contains(MMapFlags::MAP_ANONYMOUS) & !flags.contains(MMapFlags::MAP_FIXED) {
|
if flags.contains(MMapFlags::MAP_ANONYMOUS) {
|
||||||
// only support map anonymous areas on **NOT** fixed addr now
|
// only support map anonymous areas.
|
||||||
|
mmap_anonymous_vmo(len, offset, vm_perm, flags)
|
||||||
} else {
|
} else {
|
||||||
panic!("Unsupported mmap flags: {:?}", flags);
|
panic!("Unsupported mmap flags: {:?}", flags);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let current = current!();
|
|
||||||
let mmap_area = current
|
pub fn mmap_anonymous_vmo(
|
||||||
.mmap_area()
|
len: usize,
|
||||||
.expect("mmap should work on process with mmap area");
|
offset: usize,
|
||||||
let vm_space = current
|
vm_perm: VmPerm,
|
||||||
.vm_space()
|
flags: MMapFlags,
|
||||||
.expect("mmap should work on process with user space");
|
) -> Result<Vaddr> {
|
||||||
// current.mmap(len, vm_perm, flags, offset)
|
// TODO: how to respect flags?
|
||||||
mmap_area.mmap(len, offset, vm_perm, flags, vm_space)
|
if flags.complement().contains(MMapFlags::MAP_ANONYMOUS)
|
||||||
|
| flags.complement().contains(MMapFlags::MAP_PRIVATE)
|
||||||
|
{
|
||||||
|
panic!("Unsupported mmap flags {:?} now", flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if len % PAGE_SIZE != 0 {
|
||||||
|
panic!("Mmap only support page-aligned len");
|
||||||
|
}
|
||||||
|
if offset % PAGE_SIZE != 0 {
|
||||||
|
panic!("Mmap only support page-aligned offset");
|
||||||
|
}
|
||||||
|
let vmo_options: VmoOptions<Rights> = VmoOptions::new(len);
|
||||||
|
let vmo = vmo_options.alloc()?;
|
||||||
|
let current = current!();
|
||||||
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
|
let perms = VmPerms::from(vm_perm);
|
||||||
|
let mut vmar_map_options = root_vmar.new_map(vmo, perms)?;
|
||||||
|
if flags.contains(MMapFlags::MAP_FIXED) {
|
||||||
|
vmar_map_options = vmar_map_options.offset(offset);
|
||||||
|
}
|
||||||
|
Ok(vmar_map_options.build()?)
|
||||||
}
|
}
|
||||||
|
@ -274,3 +274,11 @@ pub fn syscall_dispatch(
|
|||||||
_ => panic!("Unsupported syscall number: {}", syscall_number),
|
_ => panic!("Unsupported syscall number: {}", syscall_number),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_syscall_entry {
|
||||||
|
($syscall_name: tt) => {
|
||||||
|
let syscall_name_str = stringify!($syscall_name);
|
||||||
|
info!("[SYSCALL][id={}][{}]", $syscall_name, syscall_name_str);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use jinux_frame::vm::VmPerm;
|
use jinux_frame::vm::VmPerm;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::syscall::SYS_MPROTECT;
|
use crate::syscall::SYS_MPROTECT;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_mprotect(vaddr: u64, len: u64, perms: u64) -> Result<SyscallReturn> {
|
pub fn sys_mprotect(vaddr: u64, len: u64, perms: u64) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_MPROTECT]", SYS_MPROTECT);
|
log_syscall_entry!(SYS_MPROTECT);
|
||||||
let perms = VmPerm::try_from(perms).unwrap();
|
let perms = VmPerm::try_from(perms).unwrap();
|
||||||
do_sys_mprotect(vaddr as Vaddr, len as usize, perms);
|
do_sys_mprotect(vaddr as Vaddr, len as usize, perms);
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
use crate::log_syscall_entry;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_MUNMAP;
|
use super::SYS_MUNMAP;
|
||||||
|
|
||||||
pub fn sys_munmap(addr: Vaddr, len: usize) -> Result<SyscallReturn> {
|
pub fn sys_munmap(addr: Vaddr, len: usize) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_MUNMAP]", SYS_MUNMAP);
|
log_syscall_entry!(SYS_MUNMAP);
|
||||||
debug!("addr = 0x{:x}, len = {}", addr, len);
|
debug!("addr = 0x{:x}, len = {}", addr, len);
|
||||||
//TODO: do munmap
|
//TODO: do munmap
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use crate::fs::file::File;
|
use crate::fs::file::File;
|
||||||
use crate::fs::file::FileDescripter;
|
use crate::fs::file::FileDescripter;
|
||||||
use crate::memory::read_cstring_from_user;
|
use crate::log_syscall_entry;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::syscall::constants::MAX_FILENAME_LEN;
|
use crate::syscall::constants::MAX_FILENAME_LEN;
|
||||||
use crate::tty::get_console;
|
use crate::tty::get_console;
|
||||||
|
use crate::util::read_cstring_from_user;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_OPENAT;
|
use super::SYS_OPENAT;
|
||||||
@ -16,7 +17,7 @@ pub fn sys_openat(
|
|||||||
flags: i32,
|
flags: i32,
|
||||||
mode: u16,
|
mode: u16,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_OPENAT]", SYS_OPENAT);
|
log_syscall_entry!(SYS_OPENAT);
|
||||||
let pathname = read_cstring_from_user(pathname_addr, MAX_FILENAME_LEN)?;
|
let pathname = read_cstring_from_user(pathname_addr, MAX_FILENAME_LEN)?;
|
||||||
debug!(
|
debug!(
|
||||||
"dirfd = {}, pathname = {:?}, flags = {}, mode = {}",
|
"dirfd = {}, pathname = {:?}, flags = {}, mode = {}",
|
||||||
@ -28,15 +29,15 @@ pub fn sys_openat(
|
|||||||
// Below are three special files we encountered when running busybox ash.
|
// Below are three special files we encountered when running busybox ash.
|
||||||
// We currently only return ENOENT, which means the file does not exist.
|
// We currently only return ENOENT, which means the file does not exist.
|
||||||
if dirfd == AT_FDCWD && pathname == CString::new("/etc/passwd")? {
|
if dirfd == AT_FDCWD && pathname == CString::new("/etc/passwd")? {
|
||||||
return_errno!(Errno::ENOENT);
|
return_errno_with_message!(Errno::ENOENT, "No such file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if dirfd == AT_FDCWD && pathname == CString::new("/etc/profile")? {
|
if dirfd == AT_FDCWD && pathname == CString::new("/etc/profile")? {
|
||||||
return_errno!(Errno::ENOENT);
|
return_errno_with_message!(Errno::ENOENT, "No such file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if dirfd == AT_FDCWD && pathname == CString::new("./trace")? {
|
if dirfd == AT_FDCWD && pathname == CString::new("./trace")? {
|
||||||
return_errno!(Errno::ENOENT);
|
return_errno_with_message!(Errno::ENOENT, "No such file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if dirfd == AT_FDCWD && pathname == CString::new("/dev/tty")? {
|
if dirfd == AT_FDCWD && pathname == CString::new("/dev/tty")? {
|
||||||
@ -44,7 +45,6 @@ pub fn sys_openat(
|
|||||||
let current = current!();
|
let current = current!();
|
||||||
let mut file_table = current.file_table().lock();
|
let mut file_table = current.file_table().lock();
|
||||||
let fd = file_table.insert(tty_file);
|
let fd = file_table.insert(tty_file);
|
||||||
debug!("openat fd = {}", fd);
|
|
||||||
return Ok(SyscallReturn::Return(fd as _));
|
return Ok(SyscallReturn::Return(fd as _));
|
||||||
}
|
}
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
|
||||||
use crate::fs::poll::{c_pollfd, PollFd};
|
use crate::fs::poll::{c_pollfd, PollFd};
|
||||||
use crate::memory::{read_val_from_user, write_val_to_user};
|
use crate::log_syscall_entry;
|
||||||
|
use crate::util::{read_val_from_user, write_val_to_user};
|
||||||
use crate::{fs::poll::c_nfds, prelude::*};
|
use crate::{fs::poll::c_nfds, prelude::*};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_POLL;
|
use super::SYS_POLL;
|
||||||
|
|
||||||
pub fn sys_poll(fds: Vaddr, nfds: c_nfds, timeout: i32) -> Result<SyscallReturn> {
|
pub fn sys_poll(fds: Vaddr, nfds: c_nfds, timeout: i32) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_POLL]", SYS_POLL);
|
log_syscall_entry!(SYS_POLL);
|
||||||
|
|
||||||
let mut read_addr = fds;
|
let mut read_addr = fds;
|
||||||
let mut pollfds = Vec::with_capacity(nfds as _);
|
let mut pollfds = Vec::with_capacity(nfds as _);
|
||||||
@ -36,8 +37,8 @@ pub fn sys_poll(fds: Vaddr, nfds: c_nfds, timeout: i32) -> Result<SyscallReturn>
|
|||||||
let file_table = current.file_table().lock();
|
let file_table = current.file_table().lock();
|
||||||
let file = file_table.get_file(pollfd.fd);
|
let file = file_table.get_file(pollfd.fd);
|
||||||
match file {
|
match file {
|
||||||
None => return Some(Err(Error::new(Errno::EBADF))),
|
Err(_) => return Some(Err(Error::new(Errno::EBADF))),
|
||||||
Some(file) => {
|
Ok(file) => {
|
||||||
let file_events = file.poll();
|
let file_events = file.poll();
|
||||||
let polled_events = pollfd.events.intersection(file_events);
|
let polled_events = pollfd.events.intersection(file_events);
|
||||||
if !polled_events.is_empty() {
|
if !polled_events.is_empty() {
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use crate::memory::read_cstring_from_user;
|
use crate::log_syscall_entry;
|
||||||
use crate::memory::write_bytes_to_user;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::process::name::MAX_PROCESS_NAME_LEN;
|
use crate::process::name::MAX_PROCESS_NAME_LEN;
|
||||||
|
use crate::util::read_cstring_from_user;
|
||||||
|
use crate::util::write_bytes_to_user;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_PRCTL;
|
use super::SYS_PRCTL;
|
||||||
pub fn sys_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Result<SyscallReturn> {
|
pub fn sys_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_PRCTL]", SYS_PRCTL);
|
log_syscall_entry!(SYS_PRCTL);
|
||||||
let prctl_cmd = PrctlCmd::from_args(option, arg2, arg3, arg4, arg5)?;
|
let prctl_cmd = PrctlCmd::from_args(option, arg2, arg3, arg4, arg5)?;
|
||||||
debug!("prctl cmd = {:?}", prctl_cmd);
|
debug!("prctl cmd = {:?}", prctl_cmd);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
|
@ -1,33 +1,21 @@
|
|||||||
use crate::memory::write_bytes_to_user;
|
use crate::log_syscall_entry;
|
||||||
|
use crate::util::write_bytes_to_user;
|
||||||
use crate::{fs::file::FileDescripter, prelude::*};
|
use crate::{fs::file::FileDescripter, prelude::*};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
use super::SYS_READ;
|
use super::SYS_READ;
|
||||||
|
|
||||||
pub fn sys_read(fd: FileDescripter, user_buf_addr: Vaddr, buf_len: usize) -> Result<SyscallReturn> {
|
pub fn sys_read(fd: FileDescripter, user_buf_addr: Vaddr, buf_len: usize) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_READ]", SYS_READ);
|
log_syscall_entry!(SYS_READ);
|
||||||
debug!(
|
debug!(
|
||||||
"fd = {}, user_buf_ptr = 0x{:x}, buf_len = 0x{:x}",
|
"fd = {}, user_buf_ptr = 0x{:x}, buf_len = 0x{:x}",
|
||||||
fd, user_buf_addr, buf_len
|
fd, user_buf_addr, buf_len
|
||||||
);
|
);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let file_table = current.file_table().lock();
|
let file_table = current.file_table().lock();
|
||||||
let file = file_table.get_file(fd);
|
let file = file_table.get_file(fd)?;
|
||||||
match file {
|
|
||||||
None => return_errno!(Errno::EBADF),
|
|
||||||
Some(file) => {
|
|
||||||
let mut read_buf = vec![0u8; buf_len];
|
let mut read_buf = vec![0u8; buf_len];
|
||||||
let read_len = file.read(&mut read_buf)?;
|
let read_len = file.read(&mut read_buf)?;
|
||||||
write_bytes_to_user(user_buf_addr, &read_buf)?;
|
write_bytes_to_user(user_buf_addr, &read_buf)?;
|
||||||
debug!(
|
|
||||||
"read_len = {}, read_buf = {:?}",
|
|
||||||
read_len,
|
|
||||||
&read_buf[..read_len]
|
|
||||||
);
|
|
||||||
// let read_str = core::str::from_utf8(&read_buf[..read_len - 1]).unwrap();
|
|
||||||
// println!("str = {}" ,read_str);
|
|
||||||
// todo!();
|
|
||||||
return Ok(SyscallReturn::Return(read_len as _));
|
return Ok(SyscallReturn::Return(read_len as _));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::{
|
use crate::syscall::SYS_READLINK;
|
||||||
memory::{read_bytes_from_user, write_bytes_to_user},
|
use crate::util::{read_bytes_from_user, write_bytes_to_user};
|
||||||
syscall::SYS_READLINK,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
@ -14,7 +12,7 @@ pub fn sys_readlink(
|
|||||||
user_buf_ptr: u64,
|
user_buf_ptr: u64,
|
||||||
user_buf_len: u64,
|
user_buf_len: u64,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_READLINK]", SYS_READLINK);
|
log_syscall_entry!(SYS_READLINK);
|
||||||
let res = do_sys_readlink(
|
let res = do_sys_readlink(
|
||||||
filename_ptr as Vaddr,
|
filename_ptr as Vaddr,
|
||||||
user_buf_ptr as Vaddr,
|
user_buf_ptr as Vaddr,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
memory::{read_val_from_user, write_val_to_user},
|
log_syscall_entry,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::signal::{c_types::sigaction_t, sig_action::SigAction, sig_num::SigNum},
|
process::signal::{c_types::sigaction_t, sig_action::SigAction, sig_num::SigNum},
|
||||||
syscall::SYS_RT_SIGACTION,
|
syscall::SYS_RT_SIGACTION,
|
||||||
|
util::{read_val_from_user, write_val_to_user},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
@ -13,7 +14,7 @@ pub fn sys_rt_sigaction(
|
|||||||
old_sig_action_ptr: Vaddr,
|
old_sig_action_ptr: Vaddr,
|
||||||
sigset_size: u64,
|
sigset_size: u64,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_RT_SIGACTION]", SYS_RT_SIGACTION);
|
log_syscall_entry!(SYS_RT_SIGACTION);
|
||||||
let sig_num = SigNum::try_from(sig_num)?;
|
let sig_num = SigNum::try_from(sig_num)?;
|
||||||
let sig_action_c = read_val_from_user::<sigaction_t>(sig_action_ptr)?;
|
let sig_action_c = read_val_from_user::<sigaction_t>(sig_action_ptr)?;
|
||||||
let sig_action = SigAction::try_from(sig_action_c).unwrap();
|
let sig_action = SigAction::try_from(sig_action_c).unwrap();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use jinux_frame::vm::VmIo;
|
use jinux_frame::vm::VmIo;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
log_syscall_entry,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
syscall::{SyscallReturn, SYS_RT_SIGPROCMASK},
|
syscall::{SyscallReturn, SYS_RT_SIGPROCMASK},
|
||||||
};
|
};
|
||||||
@ -11,7 +12,7 @@ pub fn sys_rt_sigprocmask(
|
|||||||
oldset_ptr: Vaddr,
|
oldset_ptr: Vaddr,
|
||||||
sigset_size: usize,
|
sigset_size: usize,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_RT_SIGPROCMASK]", SYS_RT_SIGPROCMASK);
|
log_syscall_entry!(SYS_RT_SIGPROCMASK);
|
||||||
let mask_op = MaskOp::try_from(how).unwrap();
|
let mask_op = MaskOp::try_from(how).unwrap();
|
||||||
debug!(
|
debug!(
|
||||||
"mask op = {:?}, set_ptr = 0x{:x}, oldset_ptr = 0x{:x}, sigset_size = {}",
|
"mask op = {:?}, set_ptr = 0x{:x}, oldset_ptr = 0x{:x}, sigset_size = {}",
|
||||||
@ -31,15 +32,15 @@ fn do_rt_sigprocmask(
|
|||||||
sigset_size: usize,
|
sigset_size: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let vm_space = current.vm_space().unwrap();
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
let mut sig_mask = current.sig_mask().lock();
|
let mut sig_mask = current.sig_mask().lock();
|
||||||
let old_sig_mask_value = sig_mask.as_u64();
|
let old_sig_mask_value = sig_mask.as_u64();
|
||||||
debug!("old sig mask value: 0x{:x}", old_sig_mask_value);
|
debug!("old sig mask value: 0x{:x}", old_sig_mask_value);
|
||||||
if oldset_ptr != 0 {
|
if oldset_ptr != 0 {
|
||||||
vm_space.write_val(oldset_ptr, &old_sig_mask_value)?;
|
root_vmar.write_val(oldset_ptr, &old_sig_mask_value)?;
|
||||||
}
|
}
|
||||||
if set_ptr != 0 {
|
if set_ptr != 0 {
|
||||||
let new_set = vm_space.read_val::<u64>(set_ptr)?;
|
let new_set = root_vmar.read_val::<u64>(set_ptr)?;
|
||||||
debug!("new set = 0x{:x}", new_set);
|
debug!("new set = 0x{:x}", new_set);
|
||||||
match mask_op {
|
match mask_op {
|
||||||
MaskOp::Block => sig_mask.block(new_set),
|
MaskOp::Block => sig_mask.block(new_set),
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use crate::{memory::read_val_from_user, prelude::*, process::signal::c_types::ucontext_t};
|
use crate::{
|
||||||
|
log_syscall_entry, prelude::*, process::signal::c_types::ucontext_t, util::read_val_from_user,
|
||||||
|
};
|
||||||
use jinux_frame::cpu::CpuContext;
|
use jinux_frame::cpu::CpuContext;
|
||||||
|
|
||||||
use super::{SyscallReturn, SYS_RT_SIGRETRUN};
|
use super::{SyscallReturn, SYS_RT_SIGRETRUN};
|
||||||
|
|
||||||
pub fn sys_rt_sigreturn(context: &mut CpuContext) -> Result<SyscallReturn> {
|
pub fn sys_rt_sigreturn(context: &mut CpuContext) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_RT_SIGRETURN]", SYS_RT_SIGRETRUN);
|
log_syscall_entry!(SYS_RT_SIGRETRUN);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let sig_context = current.sig_context().lock().pop_back().unwrap();
|
let sig_context = current.sig_context().lock().pop_back().unwrap();
|
||||||
let ucontext = read_val_from_user::<ucontext_t>(sig_context)?;
|
let ucontext = read_val_from_user::<ucontext_t>(sig_context)?;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::{process::Process, syscall::SYS_SCHED_YIELD};
|
use crate::{process::Process, syscall::SYS_SCHED_YIELD};
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_sched_yield() -> Result<SyscallReturn> {
|
pub fn sys_sched_yield() -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_SCHED_YIELD]", SYS_SCHED_YIELD);
|
log_syscall_entry!(SYS_SCHED_YIELD);
|
||||||
Process::yield_now();
|
Process::yield_now();
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
log_syscall_entry,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::{process_group::ProcessGroup, table, Pgid, Pid},
|
process::{process_group::ProcessGroup, table, Pgid, Pid},
|
||||||
};
|
};
|
||||||
@ -6,7 +7,7 @@ use crate::{
|
|||||||
use super::{SyscallReturn, SYS_SETPGID};
|
use super::{SyscallReturn, SYS_SETPGID};
|
||||||
|
|
||||||
pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result<SyscallReturn> {
|
pub fn sys_setpgid(pid: Pid, pgid: Pgid) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_SETPGID]", SYS_SETPGID);
|
log_syscall_entry!(SYS_SETPGID);
|
||||||
let current = current!();
|
let current = current!();
|
||||||
// if pid is 0, pid should be the pid of current process
|
// if pid is 0, pid should be the pid of current process
|
||||||
let pid = if pid == 0 { current.pid() } else { pid };
|
let pid = if pid == 0 { current.pid() } else { pid };
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::process::signal::sig_num::SigNum;
|
use crate::process::signal::sig_num::SigNum;
|
||||||
use crate::process::signal::signals::user::{UserSignal, UserSignalKind};
|
use crate::process::signal::signals::user::{UserSignal, UserSignalKind};
|
||||||
@ -11,7 +11,7 @@ use super::SyscallReturn;
|
|||||||
/// Since jinuxx only supports one-thread process now, tgkill will send signal to process with pid as its process id,
|
/// Since jinuxx only supports one-thread process now, tgkill will send signal to process with pid as its process id,
|
||||||
/// and tgid as its process group id.
|
/// and tgid as its process group id.
|
||||||
pub fn sys_tgkill(tgid: Pgid, pid: Pid, sig_num: u8) -> Result<SyscallReturn> {
|
pub fn sys_tgkill(tgid: Pgid, pid: Pid, sig_num: u8) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_TGKILL]", SYS_TGKILL);
|
log_syscall_entry!(SYS_TGKILL);
|
||||||
let sig_num = SigNum::from_u8(sig_num);
|
let sig_num = SigNum::from_u8(sig_num);
|
||||||
debug!("tgid = {}, pid = {}, sig_num = {:?}", tgid, pid, sig_num);
|
debug!("tgid = {}, pid = {}, sig_num = {:?}", tgid, pid, sig_num);
|
||||||
let target_process =
|
let target_process =
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::{memory::write_val_to_user, syscall::SYS_UNAME};
|
use crate::syscall::SYS_UNAME;
|
||||||
|
use crate::util::write_val_to_user;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ fn copy_cstring_to_u8_slice(src: &CStr, dst: &mut [u8]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_uname(old_uname_addr: Vaddr) -> Result<SyscallReturn> {
|
pub fn sys_uname(old_uname_addr: Vaddr) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_UNAME]", SYS_UNAME);
|
log_syscall_entry!(SYS_UNAME);
|
||||||
debug!("old uname addr = 0x{:x}", old_uname_addr);
|
debug!("old uname addr = 0x{:x}", old_uname_addr);
|
||||||
write_val_to_user(old_uname_addr, &*UTS_NAME)?;
|
write_val_to_user(old_uname_addr, &*UTS_NAME)?;
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
memory::write_val_to_user,
|
log_syscall_entry,
|
||||||
process::{process_filter::ProcessFilter, wait::wait_child_exit},
|
process::{process_filter::ProcessFilter, wait::wait_child_exit},
|
||||||
syscall::SYS_WAIT4,
|
syscall::SYS_WAIT4,
|
||||||
|
util::write_val_to_user,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -10,7 +11,7 @@ use crate::process::wait::WaitOptions;
|
|||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
pub fn sys_wait4(wait_pid: u64, exit_status_ptr: u64, wait_options: u32) -> Result<SyscallReturn> {
|
pub fn sys_wait4(wait_pid: u64, exit_status_ptr: u64, wait_options: u32) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_WAIT4]", SYS_WAIT4);
|
log_syscall_entry!(SYS_WAIT4);
|
||||||
let wait_options = WaitOptions::from_bits(wait_options).expect("Unknown wait options");
|
let wait_options = WaitOptions::from_bits(wait_options).expect("Unknown wait options");
|
||||||
debug!(
|
debug!(
|
||||||
"pid = {}, exit_status_ptr = {}, wait_options: {:?}",
|
"pid = {}, exit_status_ptr = {}, wait_options: {:?}",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::prelude::*;
|
|
||||||
use crate::process::{process_filter::ProcessFilter, wait::wait_child_exit};
|
use crate::process::{process_filter::ProcessFilter, wait::wait_child_exit};
|
||||||
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::process::wait::WaitOptions;
|
use crate::process::wait::WaitOptions;
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ pub fn sys_waitid(
|
|||||||
rusage_addr: u64,
|
rusage_addr: u64,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
// FIXME: what does infoq and rusage use for?
|
// FIXME: what does infoq and rusage use for?
|
||||||
debug!("[syscall][id={}][SYS_WAITID]", SYS_WAITID);
|
log_syscall_entry!(SYS_WAITID);
|
||||||
let process_filter = ProcessFilter::from_which_and_id(which, upid);
|
let process_filter = ProcessFilter::from_which_and_id(which, upid);
|
||||||
let wait_options = WaitOptions::from_bits(options as u32).expect("Unknown wait options");
|
let wait_options = WaitOptions::from_bits(options as u32).expect("Unknown wait options");
|
||||||
let (exit_code, pid) = wait_child_exit(process_filter, wait_options)?;
|
let (exit_code, pid) = wait_child_exit(process_filter, wait_options)?;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::fs::file::FileDescripter;
|
use crate::fs::file::FileDescripter;
|
||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::{memory::read_bytes_from_user, syscall::SYS_WRITE};
|
use crate::syscall::SYS_WRITE;
|
||||||
|
use crate::util::read_bytes_from_user;
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
@ -13,8 +14,7 @@ pub fn sys_write(
|
|||||||
user_buf_ptr: Vaddr,
|
user_buf_ptr: Vaddr,
|
||||||
user_buf_len: u64,
|
user_buf_len: u64,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
// only suppprt STDOUT now.
|
log_syscall_entry!(SYS_WRITE);
|
||||||
debug!("[syscall][id={}][SYS_WRITE]", SYS_WRITE);
|
|
||||||
debug!(
|
debug!(
|
||||||
"fd = {}, user_buf_ptr = 0x{:x}, user_buf_len = 0x{:x}",
|
"fd = {}, user_buf_ptr = 0x{:x}, user_buf_len = 0x{:x}",
|
||||||
fd, user_buf_ptr, user_buf_len
|
fd, user_buf_ptr, user_buf_len
|
||||||
@ -22,14 +22,9 @@ pub fn sys_write(
|
|||||||
|
|
||||||
let current = current!();
|
let current = current!();
|
||||||
let file_table = current.file_table().lock();
|
let file_table = current.file_table().lock();
|
||||||
match file_table.get_file(fd) {
|
let file = file_table.get_file(fd)?;
|
||||||
None => return_errno!(Errno::EBADF),
|
|
||||||
Some(file) => {
|
|
||||||
let mut buffer = vec![0u8; user_buf_len as usize];
|
let mut buffer = vec![0u8; user_buf_len as usize];
|
||||||
read_bytes_from_user(user_buf_ptr as usize, &mut buffer)?;
|
read_bytes_from_user(user_buf_ptr as usize, &mut buffer)?;
|
||||||
debug!("write buf = {:?}", buffer);
|
|
||||||
let write_len = file.write(&buffer)?;
|
let write_len = file.write(&buffer)?;
|
||||||
Ok(SyscallReturn::Return(write_len as _))
|
Ok(SyscallReturn::Return(write_len as _))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use crate::prelude::*;
|
use crate::{log_syscall_entry, prelude::*};
|
||||||
|
|
||||||
use crate::{
|
use crate::syscall::SYS_WRITEV;
|
||||||
memory::{read_bytes_from_user, read_val_from_user},
|
use crate::util::{read_bytes_from_user, read_val_from_user};
|
||||||
syscall::SYS_WRITEV,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::SyscallReturn;
|
use super::SyscallReturn;
|
||||||
|
|
||||||
@ -17,7 +15,7 @@ pub struct IoVec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_writev(fd: u64, io_vec_ptr: u64, io_vec_count: u64) -> Result<SyscallReturn> {
|
pub fn sys_writev(fd: u64, io_vec_ptr: u64, io_vec_count: u64) -> Result<SyscallReturn> {
|
||||||
debug!("[syscall][id={}][SYS_WRITEV]", SYS_WRITEV);
|
log_syscall_entry!(SYS_WRITEV);
|
||||||
let res = do_sys_writev(fd, io_vec_ptr as Vaddr, io_vec_count as usize)?;
|
let res = do_sys_writev(fd, io_vec_ptr as Vaddr, io_vec_count as usize)?;
|
||||||
Ok(SyscallReturn::Return(res as _))
|
Ok(SyscallReturn::Return(res as _))
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ use jinux_frame::receive_char;
|
|||||||
use self::line_discipline::LineDiscipline;
|
use self::line_discipline::LineDiscipline;
|
||||||
use crate::fs::events::IoEvents;
|
use crate::fs::events::IoEvents;
|
||||||
use crate::fs::ioctl::IoctlCmd;
|
use crate::fs::ioctl::IoctlCmd;
|
||||||
use crate::memory::{read_val_from_user, write_val_to_user};
|
|
||||||
use crate::process::Pgid;
|
use crate::process::Pgid;
|
||||||
|
use crate::util::{read_val_from_user, write_val_to_user};
|
||||||
use crate::{fs::file::File, prelude::*};
|
use crate::{fs::file::File, prelude::*};
|
||||||
|
|
||||||
pub mod line_discipline;
|
pub mod line_discipline;
|
||||||
|
@ -1 +1,38 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use jinux_frame::vm::VmIo;
|
||||||
|
use pod::Pod;
|
||||||
|
|
||||||
|
/// copy bytes from user space of current process. The bytes len is the len of dest.
|
||||||
|
pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) -> Result<()> {
|
||||||
|
let current = current!();
|
||||||
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
|
Ok(root_vmar.read_bytes(src, dest)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// copy val (Plain of Data type) from user space of current process.
|
||||||
|
pub fn read_val_from_user<T: Pod>(src: Vaddr) -> Result<T> {
|
||||||
|
let current = current!();
|
||||||
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
|
Ok(root_vmar.read_val(src)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// write bytes from user space of current process. The bytes len is the len of src.
|
||||||
|
pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) -> Result<()> {
|
||||||
|
let current = current!();
|
||||||
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
|
Ok(root_vmar.write_bytes(dest, src)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// write val (Plain of Data type) to user space of current process.
|
||||||
|
pub fn write_val_to_user<T: Pod>(dest: Vaddr, val: &T) -> Result<()> {
|
||||||
|
let current = current!();
|
||||||
|
let root_vmar = current.root_vmar().unwrap();
|
||||||
|
Ok(root_vmar.write_val(dest, val)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read a cstring from user, the length of cstring should not exceed max_len(include null byte)
|
||||||
|
pub fn read_cstring_from_user(addr: Vaddr, max_len: usize) -> Result<CString> {
|
||||||
|
let mut buffer = vec![0u8; max_len];
|
||||||
|
read_bytes_from_user(addr, &mut buffer)?;
|
||||||
|
Ok(CString::from(CStr::from_bytes_until_nul(&buffer)?))
|
||||||
|
}
|
||||||
|
@ -14,5 +14,7 @@
|
|||||||
//! In Jinux, VMARs and VMOs, as well as other capabilities, are implemented
|
//! In Jinux, VMARs and VMOs, as well as other capabilities, are implemented
|
||||||
//! as zero-cost capabilities.
|
//! as zero-cost capabilities.
|
||||||
|
|
||||||
mod vmar;
|
pub mod page_fault_handler;
|
||||||
mod vmo;
|
pub mod perms;
|
||||||
|
pub mod vmar;
|
||||||
|
pub mod vmo;
|
||||||
|
13
src/services/libs/jinux-std/src/vm/page_fault_handler.rs
Normal file
13
src/services/libs/jinux-std/src/vm/page_fault_handler.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
/// This trait is implemented by structs which can handle a user space page fault.
|
||||||
|
/// In current implementation, they are vmars and vmos.
|
||||||
|
pub trait PageFaultHandler {
|
||||||
|
/// Handle a page fault at a specific addr. if not_present is true, the page fault is caused by page not present.
|
||||||
|
/// Otherwise, it's caused by page protection error.
|
||||||
|
/// if write is true, means the page fault is caused by a write access,
|
||||||
|
/// otherwise, the page fault is caused by a read access.
|
||||||
|
/// If the page fault can be handled successfully, this function will return Ok(()).
|
||||||
|
/// Otherwise, this function will return Err.
|
||||||
|
fn handle_page_fault(&self, offset: Vaddr, not_present: bool, write: bool) -> Result<()>;
|
||||||
|
}
|
79
src/services/libs/jinux-std/src/vm/perms.rs
Normal file
79
src/services/libs/jinux-std/src/vm/perms.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use crate::rights::Rights;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use jinux_frame::vm::VmPerm;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// The memory access permissions of memory mappings.
|
||||||
|
pub struct VmPerms: u32 {
|
||||||
|
/// Readable.
|
||||||
|
const READ = 1 << 0;
|
||||||
|
/// Writable.
|
||||||
|
const WRITE = 1 << 1;
|
||||||
|
/// Executable.
|
||||||
|
const EXEC = 1 << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rights> for VmPerms {
|
||||||
|
fn from(rights: Rights) -> VmPerms {
|
||||||
|
let mut vm_perm = VmPerms::empty();
|
||||||
|
if rights.contains(Rights::READ) {
|
||||||
|
vm_perm |= VmPerms::READ;
|
||||||
|
}
|
||||||
|
if rights.contains(Rights::WRITE) {
|
||||||
|
vm_perm |= VmPerms::WRITE;
|
||||||
|
}
|
||||||
|
if rights.contains(Rights::EXEC) {
|
||||||
|
vm_perm |= VmPerms::EXEC;
|
||||||
|
}
|
||||||
|
vm_perm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VmPerms> for Rights {
|
||||||
|
fn from(vm_perms: VmPerms) -> Rights {
|
||||||
|
let mut rights = Rights::empty();
|
||||||
|
if vm_perms.contains(VmPerms::READ) {
|
||||||
|
rights |= Rights::READ;
|
||||||
|
}
|
||||||
|
if vm_perms.contains(VmPerms::WRITE) {
|
||||||
|
rights |= Rights::WRITE;
|
||||||
|
}
|
||||||
|
if vm_perms.contains(VmPerms::EXEC) {
|
||||||
|
rights |= Rights::EXEC;
|
||||||
|
}
|
||||||
|
rights
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VmPerm> for VmPerms {
|
||||||
|
fn from(perm: VmPerm) -> Self {
|
||||||
|
let mut perms = VmPerms::empty();
|
||||||
|
if perm.contains(VmPerm::R) {
|
||||||
|
perms |= VmPerms::READ;
|
||||||
|
}
|
||||||
|
if perm.contains(VmPerm::W) {
|
||||||
|
perms |= VmPerms::WRITE;
|
||||||
|
}
|
||||||
|
if perm.contains(VmPerm::X) {
|
||||||
|
perms |= VmPerms::EXEC;
|
||||||
|
}
|
||||||
|
perms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VmPerms> for VmPerm {
|
||||||
|
fn from(perms: VmPerms) -> Self {
|
||||||
|
let mut perm = VmPerm::empty();
|
||||||
|
if perms.contains(VmPerms::READ) {
|
||||||
|
perm |= VmPerm::R;
|
||||||
|
}
|
||||||
|
if perms.contains(VmPerms::WRITE) {
|
||||||
|
perm |= VmPerm::W;
|
||||||
|
}
|
||||||
|
if perms.contains(VmPerms::EXEC) {
|
||||||
|
perm |= VmPerm::X;
|
||||||
|
}
|
||||||
|
perm
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,21 @@
|
|||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
|
use jinux_frame::vm::{Vaddr, VmIo};
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use crate::prelude::*;
|
||||||
use jinux_frame::prelude::Result;
|
|
||||||
use jinux_frame::{vm::VmIo, Error};
|
|
||||||
|
|
||||||
use crate::{rights::Rights, vm::vmo::Vmo};
|
use crate::{
|
||||||
|
rights::Rights,
|
||||||
|
vm::{page_fault_handler::PageFaultHandler, vmo::Vmo},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
options::{VmarChildOptions, VmarMapOptions},
|
options::VmarChildOptions, vm_mapping::VmarMapOptions, VmPerms, Vmar, VmarRightsOp, Vmar_,
|
||||||
VmPerms, Vmar, Vmar_,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Vmar<Rights> {
|
impl Vmar<Rights> {
|
||||||
/// Creates a root VMAR.
|
/// Creates a root VMAR.
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new_root() -> Result<Self> {
|
||||||
let inner = Arc::new(Vmar_::new()?);
|
let inner = Arc::new(Vmar_::new_root()?);
|
||||||
let rights = Rights::all();
|
let rights = Rights::all();
|
||||||
let new_self = Self(inner, rights);
|
let new_self = Self(inner, rights);
|
||||||
Ok(new_self)
|
Ok(new_self)
|
||||||
@ -106,6 +107,12 @@ impl Vmar<Rights> {
|
|||||||
self.0.protect(perms, range)
|
self.0.protect(perms, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// clear all mappings and children vmars.
|
||||||
|
/// After being cleared, this vmar will become an empty vmar
|
||||||
|
pub fn clear(&self) -> Result<()> {
|
||||||
|
self.0.clear_root_vmar()
|
||||||
|
}
|
||||||
|
|
||||||
/// Destroy a VMAR, including all its mappings and children VMARs.
|
/// Destroy a VMAR, including all its mappings and children VMARs.
|
||||||
///
|
///
|
||||||
/// After being destroyed, the VMAR becomes useless and returns errors
|
/// After being destroyed, the VMAR becomes useless and returns errors
|
||||||
@ -135,31 +142,43 @@ impl Vmar<Rights> {
|
|||||||
/// The method requires the Dup right.
|
/// The method requires the Dup right.
|
||||||
pub fn dup(&self) -> Result<Self> {
|
pub fn dup(&self) -> Result<Self> {
|
||||||
self.check_rights(Rights::DUP)?;
|
self.check_rights(Rights::DUP)?;
|
||||||
todo!()
|
Ok(Vmar(self.0.clone(), self.1.clone()))
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the access rights.
|
|
||||||
pub fn rights(&self) -> Rights {
|
|
||||||
self.1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_rights(&self, rights: Rights) -> Result<()> {
|
|
||||||
if self.1.contains(rights) {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::AccessDenied)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmIo for Vmar<Rights> {
|
impl VmIo for Vmar<Rights> {
|
||||||
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> jinux_frame::Result<()> {
|
||||||
self.check_rights(Rights::READ)?;
|
self.check_rights(Rights::READ)?;
|
||||||
self.0.read(offset, buf)
|
self.0.read(offset, buf)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> jinux_frame::Result<()> {
|
||||||
self.check_rights(Rights::WRITE)?;
|
self.check_rights(Rights::WRITE)?;
|
||||||
self.0.write(offset, buf)
|
self.0.write(offset, buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageFaultHandler for Vmar<Rights> {
|
||||||
|
fn handle_page_fault(
|
||||||
|
&self,
|
||||||
|
page_fault_addr: Vaddr,
|
||||||
|
not_present: bool,
|
||||||
|
write: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
if write {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
} else {
|
||||||
|
self.check_rights(Rights::READ)?;
|
||||||
|
}
|
||||||
|
self.0
|
||||||
|
.handle_page_fault(page_fault_addr, not_present, write)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmarRightsOp for Vmar<Rights> {
|
||||||
|
fn rights(&self) -> Rights {
|
||||||
|
self.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,16 +3,24 @@
|
|||||||
mod dyn_cap;
|
mod dyn_cap;
|
||||||
mod options;
|
mod options;
|
||||||
mod static_cap;
|
mod static_cap;
|
||||||
|
pub mod vm_mapping;
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::rights::Full;
|
||||||
use crate::rights::Rights;
|
use crate::rights::Rights;
|
||||||
|
use crate::vm::perms::VmPerms;
|
||||||
|
use alloc::collections::BTreeMap;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use bitflags::bitflags;
|
use alloc::sync::Weak;
|
||||||
|
use alloc::vec::Vec;
|
||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
use jinux_frame::prelude::Result;
|
|
||||||
use jinux_frame::vm::Vaddr;
|
|
||||||
use jinux_frame::vm::VmSpace;
|
use jinux_frame::vm::VmSpace;
|
||||||
use jinux_frame::Error;
|
use jinux_frame::AlignExt;
|
||||||
use spin::Mutex;
|
|
||||||
|
use self::vm_mapping::VmMapping;
|
||||||
|
|
||||||
|
use super::page_fault_handler::PageFaultHandler;
|
||||||
|
use super::vmo::Vmo;
|
||||||
|
|
||||||
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages
|
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages
|
||||||
/// user address spaces.
|
/// user address spaces.
|
||||||
@ -43,44 +51,601 @@ use spin::Mutex;
|
|||||||
///
|
///
|
||||||
pub struct Vmar<R = Rights>(Arc<Vmar_>, R);
|
pub struct Vmar<R = Rights>(Arc<Vmar_>, R);
|
||||||
|
|
||||||
|
pub trait VmarRightsOp {
|
||||||
|
/// Returns the access rights.
|
||||||
|
fn rights(&self) -> Rights;
|
||||||
|
fn check_rights(&self, rights: Rights) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> VmarRightsOp for Vmar<R> {
|
||||||
|
default fn rights(&self) -> Rights {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
default fn check_rights(&self, rights: Rights) -> Result<()> {
|
||||||
|
if self.rights().contains(rights) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
return_errno_with_message!(Errno::EACCES, "Rights check failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: how page faults can be delivered to and handled by the current VMAR.
|
// TODO: how page faults can be delivered to and handled by the current VMAR.
|
||||||
|
impl<R> PageFaultHandler for Vmar<R> {
|
||||||
|
default fn handle_page_fault(
|
||||||
|
&self,
|
||||||
|
page_fault_addr: Vaddr,
|
||||||
|
not_present: bool,
|
||||||
|
write: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Vmar_ {
|
impl<R> Vmar<R> {
|
||||||
inner: Mutex<Inner>,
|
/// FIXME: This function should require access control
|
||||||
// The offset relative to the root VMAR
|
pub fn vm_space(&self) -> &VmSpace {
|
||||||
|
self.0.vm_space()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct Vmar_ {
|
||||||
|
/// vmar inner
|
||||||
|
inner: Mutex<VmarInner>,
|
||||||
|
/// The offset relative to the root VMAR
|
||||||
base: Vaddr,
|
base: Vaddr,
|
||||||
parent: Option<Arc<Vmar_>>,
|
/// The total size of the VMAR in bytes
|
||||||
|
size: usize,
|
||||||
|
/// The attached vmspace
|
||||||
|
vm_space: VmSpace,
|
||||||
|
/// The parent vmar. If points to none, this is a root vmar
|
||||||
|
parent: Weak<Vmar_>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Inner {
|
struct VmarInner {
|
||||||
|
/// Whether the vmar is destroyed
|
||||||
is_destroyed: bool,
|
is_destroyed: bool,
|
||||||
vm_space: VmSpace,
|
/// The child vmars. The key is offset relative to root VMAR
|
||||||
//...
|
child_vmar_s: BTreeMap<Vaddr, Arc<Vmar_>>,
|
||||||
|
/// The mapped vmos. The key is offset relative to root VMAR
|
||||||
|
vm_mappings: BTreeMap<Vaddr, Arc<VmMapping>>,
|
||||||
|
/// Free regions that can be used for creating child vmar or mapping vmos
|
||||||
|
free_regions: BTreeMap<Vaddr, FreeRegion>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: How to set the correct root vmar range?
|
||||||
|
// We should not include addr 0 here(is this right?), since the 0 addr means the null pointer.
|
||||||
|
// We should include addr 0x0040_0000, since non-pie executables typically are put on 0x0040_0000.
|
||||||
|
pub const ROOT_VMAR_LOWEST_ADDR: Vaddr = 0x0010_0000;
|
||||||
|
pub const ROOT_VMAR_HIGHEST_ADDR: Vaddr = 0x1000_0000_0000;
|
||||||
|
|
||||||
impl Vmar_ {
|
impl Vmar_ {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new_root() -> Result<Self> {
|
||||||
todo!()
|
let mut free_regions = BTreeMap::new();
|
||||||
|
let root_region = FreeRegion::new(ROOT_VMAR_LOWEST_ADDR..ROOT_VMAR_HIGHEST_ADDR);
|
||||||
|
free_regions.insert(root_region.start(), root_region);
|
||||||
|
let vmar_inner = VmarInner {
|
||||||
|
is_destroyed: false,
|
||||||
|
child_vmar_s: BTreeMap::new(),
|
||||||
|
vm_mappings: BTreeMap::new(),
|
||||||
|
free_regions,
|
||||||
|
};
|
||||||
|
let vmar_ = Vmar_ {
|
||||||
|
inner: Mutex::new(vmar_inner),
|
||||||
|
vm_space: VmSpace::new(),
|
||||||
|
base: 0,
|
||||||
|
size: ROOT_VMAR_HIGHEST_ADDR,
|
||||||
|
parent: Weak::new(),
|
||||||
|
};
|
||||||
|
Ok(vmar_)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_root_vmar(&self) -> bool {
|
||||||
|
if let Some(_) = self.parent.upgrade() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn protect(&self, perms: VmPerms, range: Range<usize>) -> Result<()> {
|
pub fn protect(&self, perms: VmPerms, range: Range<usize>) -> Result<()> {
|
||||||
todo!()
|
assert!(range.start % PAGE_SIZE == 0);
|
||||||
|
assert!(range.end % PAGE_SIZE == 0);
|
||||||
|
self.check_protected_range(&range)?;
|
||||||
|
self.do_protect_inner(perms, range)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// do real protect. The protected range is ensured to be mapped.
|
||||||
|
fn do_protect_inner(&self, perms: VmPerms, range: Range<usize>) -> Result<()> {
|
||||||
|
for (vm_mapping_base, vm_mapping) in &self.inner.lock().vm_mappings {
|
||||||
|
let vm_mapping_range = *vm_mapping_base..(*vm_mapping_base + vm_mapping.size());
|
||||||
|
if is_intersected(&range, &vm_mapping_range) {
|
||||||
|
let intersected_range = get_intersected_range(&range, &vm_mapping_range);
|
||||||
|
vm_mapping.protect(perms, intersected_range)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, child_vmar_) in &self.inner.lock().child_vmar_s {
|
||||||
|
let child_vmar_range = child_vmar_.range();
|
||||||
|
if is_intersected(&range, &child_vmar_range) {
|
||||||
|
let intersected_range = get_intersected_range(&range, &child_vmar_range);
|
||||||
|
child_vmar_.do_protect_inner(perms, intersected_range)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ensure the whole protected range is mapped, that is to say, backed up by a VMO.
|
||||||
|
/// Internally, we check whether the range intersects any free region recursively.
|
||||||
|
/// If so, the range is not fully mapped.
|
||||||
|
fn check_protected_range(&self, protected_range: &Range<usize>) -> Result<()> {
|
||||||
|
// The protected range should be in self's range
|
||||||
|
assert!(self.base <= protected_range.start);
|
||||||
|
assert!(protected_range.end <= self.base + self.size);
|
||||||
|
|
||||||
|
// The protected range should not interstect with any free region
|
||||||
|
for (_, free_region) in &self.inner.lock().free_regions {
|
||||||
|
if is_intersected(&free_region.range, &protected_range) {
|
||||||
|
return_errno_with_message!(Errno::EACCES, "protected range is not fully mapped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the protected range intersects with child vmar_, child vmar_ is responsible to do the check.
|
||||||
|
for (_, child_vmar_) in &self.inner.lock().child_vmar_s {
|
||||||
|
let child_range = child_vmar_.range();
|
||||||
|
if is_intersected(&child_range, &protected_range) {
|
||||||
|
let intersected_range = get_intersected_range(&child_range, &protected_range);
|
||||||
|
child_vmar_.check_protected_range(&intersected_range)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle user space page fault, if the page fault is successfully handled ,return Ok(()).
|
||||||
|
pub fn handle_page_fault(
|
||||||
|
&self,
|
||||||
|
page_fault_addr: Vaddr,
|
||||||
|
not_present: bool,
|
||||||
|
write: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
if page_fault_addr < self.base || page_fault_addr >= self.base + self.size {
|
||||||
|
return_errno_with_message!(Errno::EACCES, "page fault addr is not in current vmar");
|
||||||
|
}
|
||||||
|
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
for (child_vmar_base, child_vmar) in &inner.child_vmar_s {
|
||||||
|
if *child_vmar_base <= page_fault_addr
|
||||||
|
&& page_fault_addr < *child_vmar_base + child_vmar.size
|
||||||
|
{
|
||||||
|
return child_vmar.handle_page_fault(page_fault_addr, not_present, write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: If multiple vmos are mapped to the addr, should we allow all vmos to handle page fault?
|
||||||
|
for (vm_mapping_base, vm_mapping) in &inner.vm_mappings {
|
||||||
|
if *vm_mapping_base <= page_fault_addr
|
||||||
|
&& page_fault_addr <= *vm_mapping_base + vm_mapping.size()
|
||||||
|
{
|
||||||
|
return vm_mapping.handle_page_fault(page_fault_addr, not_present, write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return_errno_with_message!(Errno::EACCES, "page fault addr is not in current vmar");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// clear all content of the root vmar
|
||||||
|
pub fn clear_root_vmar(&self) -> Result<()> {
|
||||||
|
debug_assert!(self.is_root_vmar());
|
||||||
|
if !self.is_root_vmar() {
|
||||||
|
return_errno_with_message!(Errno::EACCES, "The vmar is not root vmar");
|
||||||
|
}
|
||||||
|
self.vm_space.clear();
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
inner.child_vmar_s.clear();
|
||||||
|
inner.vm_mappings.clear();
|
||||||
|
inner.free_regions.clear();
|
||||||
|
let root_region = FreeRegion::new(ROOT_VMAR_LOWEST_ADDR..ROOT_VMAR_HIGHEST_ADDR);
|
||||||
|
inner.free_regions.insert(root_region.start(), root_region);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_all(&self) -> Result<()> {
|
pub fn destroy_all(&self) -> Result<()> {
|
||||||
todo!()
|
let mut inner = self.inner.lock();
|
||||||
|
inner.is_destroyed = true;
|
||||||
|
let mut free_regions = BTreeMap::new();
|
||||||
|
for (child_vmar_base, child_vmar) in &inner.child_vmar_s {
|
||||||
|
child_vmar.destroy_all()?;
|
||||||
|
let free_region = FreeRegion::new(child_vmar.range());
|
||||||
|
free_regions.insert(free_region.start(), free_region);
|
||||||
|
}
|
||||||
|
inner.child_vmar_s.clear();
|
||||||
|
inner.free_regions.append(&mut free_regions);
|
||||||
|
|
||||||
|
for (_, vm_mapping) in &inner.vm_mappings {
|
||||||
|
vm_mapping.unmap(vm_mapping.range(), true)?;
|
||||||
|
let free_region = FreeRegion::new(vm_mapping.range());
|
||||||
|
free_regions.insert(free_region.start(), free_region);
|
||||||
|
}
|
||||||
|
inner.vm_mappings.clear();
|
||||||
|
inner.free_regions.append(&mut free_regions);
|
||||||
|
|
||||||
|
drop(inner);
|
||||||
|
self.merge_continuous_regions();
|
||||||
|
self.vm_space.clear();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(&self, range: Range<usize>) -> Result<()> {
|
pub fn destroy(&self, range: Range<usize>) -> Result<()> {
|
||||||
todo!()
|
self.check_destroy_range(&range)?;
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
let mut free_regions = BTreeMap::new();
|
||||||
|
for (child_vmar_base, child_vmar) in &inner.child_vmar_s {
|
||||||
|
let child_vmar_range = child_vmar.range();
|
||||||
|
if is_intersected(&range, &child_vmar_range) {
|
||||||
|
child_vmar.destroy_all()?;
|
||||||
|
}
|
||||||
|
let free_region = FreeRegion::new(child_vmar_range);
|
||||||
|
free_regions.insert(free_region.start(), free_region);
|
||||||
|
}
|
||||||
|
|
||||||
|
inner
|
||||||
|
.child_vmar_s
|
||||||
|
.retain(|_, child_vmar_| !child_vmar_.is_destroyed());
|
||||||
|
|
||||||
|
for (_, vm_mapping) in &inner.vm_mappings {
|
||||||
|
let vm_mapping_range = vm_mapping.range();
|
||||||
|
if is_intersected(&vm_mapping_range, &range) {
|
||||||
|
let intersected_range = get_intersected_range(&vm_mapping_range, &range);
|
||||||
|
vm_mapping.unmap(intersected_range.clone(), true)?;
|
||||||
|
let free_region = FreeRegion::new(intersected_range);
|
||||||
|
free_regions.insert(free_region.start(), free_region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner
|
||||||
|
.vm_mappings
|
||||||
|
.retain(|_, vm_mapping| !vm_mapping.is_destroyed());
|
||||||
|
inner.free_regions.append(&mut free_regions);
|
||||||
|
self.merge_continuous_regions();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_destroy_range(&self, range: &Range<usize>) -> Result<()> {
|
||||||
|
debug_assert!(range.start % PAGE_SIZE == 0);
|
||||||
|
debug_assert!(range.end % PAGE_SIZE == 0);
|
||||||
|
|
||||||
|
for (child_vmar_base, child_vmar) in &self.inner.lock().child_vmar_s {
|
||||||
|
let child_vmar_start = *child_vmar_base;
|
||||||
|
let child_vmar_end = child_vmar_start + child_vmar.size;
|
||||||
|
if child_vmar_end <= range.start || child_vmar_start >= range.end {
|
||||||
|
// child vmar does not intersect with range
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if range.start <= child_vmar_start && child_vmar_end <= range.end {
|
||||||
|
// child vmar is totolly in the range
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert!(is_intersected(range, &(child_vmar_start..child_vmar_end)));
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EACCES,
|
||||||
|
"Child vmar is partly intersected with destryed range"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_destroyed(&self) -> bool {
|
||||||
|
self.inner.lock().is_destroyed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_continuous_regions(&self) {
|
||||||
|
let mut new_free_regions = BTreeMap::new();
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
let keys = inner.free_regions.keys().cloned().collect::<Vec<_>>();
|
||||||
|
for key in keys {
|
||||||
|
if let Some(mut free_region) = inner.free_regions.remove(&key) {
|
||||||
|
let mut region_end = free_region.end();
|
||||||
|
while let Some(another_region) = inner.free_regions.remove(®ion_end) {
|
||||||
|
free_region.merge_other_region(&another_region);
|
||||||
|
region_end = another_region.end();
|
||||||
|
}
|
||||||
|
new_free_regions.insert(free_region.start(), free_region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inner.free_regions.clear();
|
||||||
|
inner.free_regions.append(&mut new_free_regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
pub fn read(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
todo!()
|
let read_start = self.base + offset;
|
||||||
|
let read_end = buf.len() + read_start;
|
||||||
|
// if the read range is in child vmar
|
||||||
|
for (child_vmar_base, child_vmar) in &self.inner.lock().child_vmar_s {
|
||||||
|
let child_vmar_end = *child_vmar_base + child_vmar.size;
|
||||||
|
if *child_vmar_base <= read_start && read_end <= child_vmar_end {
|
||||||
|
let child_offset = read_start - *child_vmar_base;
|
||||||
|
return child_vmar.read(child_offset, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the read range is in mapped vmo
|
||||||
|
for (vm_mapping_base, vm_mapping) in &self.inner.lock().vm_mappings {
|
||||||
|
let vm_mapping_end = *vm_mapping_base + vm_mapping.size();
|
||||||
|
if *vm_mapping_base <= read_start && read_end <= vm_mapping_end {
|
||||||
|
let vm_mapping_offset = read_start - *vm_mapping_base;
|
||||||
|
return vm_mapping.read_bytes(vm_mapping_offset, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: If the read range is across different vmos or child vmars, should we directly return error?
|
||||||
|
return_errno_with_message!(Errno::EACCES, "read range is not backed up by a vmo");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
pub fn write(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
todo!()
|
let write_start = self.base + offset;
|
||||||
|
let write_end = buf.len() + write_start;
|
||||||
|
// if the write range is in child vmar
|
||||||
|
for (child_vmar_base, child_vmar) in &self.inner.lock().child_vmar_s {
|
||||||
|
let child_vmar_end = *child_vmar_base + child_vmar.size;
|
||||||
|
if *child_vmar_base <= write_start && write_end <= child_vmar_end {
|
||||||
|
let child_offset = write_start - *child_vmar_base;
|
||||||
|
return child_vmar.write(child_offset, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the write range is in mapped vmo
|
||||||
|
for (vm_mapping_base, vm_mapping) in &self.inner.lock().vm_mappings {
|
||||||
|
let vm_mapping_end = *vm_mapping_base + vm_mapping.size();
|
||||||
|
if *vm_mapping_base <= write_start && write_end <= vm_mapping_end {
|
||||||
|
let vm_mapping_offset = write_start - *vm_mapping_base;
|
||||||
|
return vm_mapping.write_bytes(vm_mapping_offset, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: If the write range is across different vmos or child vmars, should we directly return error?
|
||||||
|
return_errno_with_message!(Errno::EACCES, "write range is not backed up by a vmo");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// allocate a child vmar_.
|
||||||
|
pub fn alloc_child_vmar(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
child_vmar_offset: Option<usize>,
|
||||||
|
child_vmar_size: usize,
|
||||||
|
align: usize,
|
||||||
|
) -> Result<Arc<Vmar_>> {
|
||||||
|
let (region_base, child_vmar_offset) =
|
||||||
|
self.find_free_region_for_child(child_vmar_offset, child_vmar_size, align)?;
|
||||||
|
// This unwrap should never fails
|
||||||
|
let free_region = self.inner.lock().free_regions.remove(®ion_base).unwrap();
|
||||||
|
let child_range = child_vmar_offset..(child_vmar_offset + child_vmar_size);
|
||||||
|
let regions_after_allocation = free_region.allocate_range(child_range.clone());
|
||||||
|
regions_after_allocation.into_iter().for_each(|region| {
|
||||||
|
self.inner
|
||||||
|
.lock()
|
||||||
|
.free_regions
|
||||||
|
.insert(region.start(), region);
|
||||||
|
});
|
||||||
|
let child_region = FreeRegion::new(child_range);
|
||||||
|
let mut child_regions = BTreeMap::new();
|
||||||
|
child_regions.insert(child_region.start(), child_region);
|
||||||
|
let child_vmar_inner = VmarInner {
|
||||||
|
is_destroyed: false,
|
||||||
|
child_vmar_s: BTreeMap::new(),
|
||||||
|
vm_mappings: BTreeMap::new(),
|
||||||
|
free_regions: child_regions,
|
||||||
|
};
|
||||||
|
let child_vmar_ = Arc::new(Vmar_ {
|
||||||
|
inner: Mutex::new(child_vmar_inner),
|
||||||
|
base: child_vmar_offset,
|
||||||
|
size: child_vmar_size,
|
||||||
|
vm_space: self.vm_space.clone(),
|
||||||
|
parent: Arc::downgrade(self),
|
||||||
|
});
|
||||||
|
self.inner
|
||||||
|
.lock()
|
||||||
|
.child_vmar_s
|
||||||
|
.insert(child_vmar_.base, child_vmar_.clone());
|
||||||
|
Ok(child_vmar_)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// find a free region for child vmar or vmo.
|
||||||
|
/// returns (region base addr, child real offset)
|
||||||
|
fn find_free_region_for_child(
|
||||||
|
&self,
|
||||||
|
child_offset: Option<Vaddr>,
|
||||||
|
child_size: usize,
|
||||||
|
align: usize,
|
||||||
|
) -> Result<(Vaddr, Vaddr)> {
|
||||||
|
for (region_base, free_region) in &self.inner.lock().free_regions {
|
||||||
|
if let Some(child_vmar_offset) = child_offset {
|
||||||
|
// if the offset is set, we should find a free region can satisfy both the offset and size
|
||||||
|
if *region_base <= child_vmar_offset
|
||||||
|
&& (child_vmar_offset + child_size) <= (free_region.end())
|
||||||
|
{
|
||||||
|
return Ok((*region_base, child_vmar_offset));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// else, we find a free region that can satisfy the length and align requirement.
|
||||||
|
// Here, we use a simple brute-force algorithm to find the first free range that can satisfy.
|
||||||
|
// FIXME: A randomized algorithm may be more efficient.
|
||||||
|
let region_start = free_region.start();
|
||||||
|
let region_end = free_region.end();
|
||||||
|
let child_vmar_real_start = region_start.align_up(align);
|
||||||
|
let child_vmar_real_end = child_vmar_real_start + child_size;
|
||||||
|
if region_start <= child_vmar_real_start && child_vmar_real_end <= region_end {
|
||||||
|
return Ok((*region_base, child_vmar_real_start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return_errno_with_message!(Errno::EACCES, "Cannot find free region for child")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Range<usize> {
|
||||||
|
self.base..(self.base + self.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_vmo_overwrite(&self, vmo_range: Range<usize>, can_overwrite: bool) -> Result<()> {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
for (_, child_vmar) in &inner.child_vmar_s {
|
||||||
|
let child_vmar_range = child_vmar.range();
|
||||||
|
if is_intersected(&vmo_range, &child_vmar_range) {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EACCES,
|
||||||
|
"vmo range overlapped with child vmar range"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !can_overwrite {
|
||||||
|
for (child_vmo_base, child_vmo) in &inner.vm_mappings {
|
||||||
|
let child_vmo_range = *child_vmo_base..*child_vmo_base + child_vmo.size();
|
||||||
|
if is_intersected(&vmo_range, &child_vmo_range) {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EACCES,
|
||||||
|
"vmo range overlapped with another vmo"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the attached vm_space
|
||||||
|
pub(super) fn vm_space(&self) -> &VmSpace {
|
||||||
|
&self.vm_space
|
||||||
|
}
|
||||||
|
|
||||||
|
/// map a vmo to this vmar
|
||||||
|
pub fn add_mapping(&self, mapping: Arc<VmMapping>) {
|
||||||
|
self.inner
|
||||||
|
.lock()
|
||||||
|
.vm_mappings
|
||||||
|
.insert(mapping.map_to_addr(), mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocate_free_region_for_vmo(
|
||||||
|
&self,
|
||||||
|
vmo_size: usize,
|
||||||
|
size: usize,
|
||||||
|
offset: Option<usize>,
|
||||||
|
align: usize,
|
||||||
|
can_overwrite: bool,
|
||||||
|
) -> Result<Vaddr> {
|
||||||
|
let allocate_size = size.max(vmo_size);
|
||||||
|
|
||||||
|
if can_overwrite {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
// if can_overwrite, the offset is ensured not to be None
|
||||||
|
let offset = offset.unwrap();
|
||||||
|
let vmo_range = offset..(offset + allocate_size);
|
||||||
|
// If can overwrite, the vmo can cross multiple free regions. We will split each free regions that intersect with the vmo
|
||||||
|
let mut split_regions = Vec::new();
|
||||||
|
for (free_region_base, free_region) in &inner.free_regions {
|
||||||
|
let free_region_range = free_region.range();
|
||||||
|
if is_intersected(free_region_range, &vmo_range) {
|
||||||
|
split_regions.push(*free_region_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for region_base in split_regions {
|
||||||
|
let free_region = inner.free_regions.remove(®ion_base).unwrap();
|
||||||
|
let intersected_range = get_intersected_range(free_region.range(), &vmo_range);
|
||||||
|
let regions_after_split = free_region.allocate_range(intersected_range);
|
||||||
|
regions_after_split.into_iter().for_each(|region| {
|
||||||
|
inner.free_regions.insert(region.start(), region);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Ok(offset);
|
||||||
|
} else {
|
||||||
|
// Otherwise, the vmo in a single region
|
||||||
|
let (free_region_base, offset) =
|
||||||
|
self.find_free_region_for_child(offset, allocate_size, align)?;
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
let free_region = inner.free_regions.remove(&free_region_base).unwrap();
|
||||||
|
let vmo_range = offset..(offset + allocate_size);
|
||||||
|
let intersected_range = get_intersected_range(free_region.range(), &vmo_range);
|
||||||
|
let regions_after_split = free_region.allocate_range(intersected_range);
|
||||||
|
regions_after_split.into_iter().for_each(|region| {
|
||||||
|
inner.free_regions.insert(region.start(), region);
|
||||||
|
});
|
||||||
|
return Ok(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// fork vmar for child process
|
||||||
|
pub fn fork_vmar_(&self, parent: Weak<Vmar_>) -> Result<Arc<Self>> {
|
||||||
|
// create an empty vmar at first
|
||||||
|
let is_destroyed = false;
|
||||||
|
let child_vmar_s = BTreeMap::new();
|
||||||
|
let mapped_vmos = BTreeMap::new();
|
||||||
|
let free_regions = BTreeMap::new();
|
||||||
|
let vmar_inner = VmarInner {
|
||||||
|
is_destroyed,
|
||||||
|
child_vmar_s,
|
||||||
|
vm_mappings: mapped_vmos,
|
||||||
|
free_regions,
|
||||||
|
};
|
||||||
|
// If this is a root vmar, we create a new vmspace
|
||||||
|
// Otherwise, we clone the vm space from parent.
|
||||||
|
let vm_space = if let Some(parent) = parent.upgrade() {
|
||||||
|
parent.vm_space().clone()
|
||||||
|
} else {
|
||||||
|
VmSpace::new()
|
||||||
|
};
|
||||||
|
let vmar_ = Vmar_ {
|
||||||
|
inner: Mutex::new(vmar_inner),
|
||||||
|
base: self.base,
|
||||||
|
size: self.size,
|
||||||
|
vm_space,
|
||||||
|
parent,
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_vmar_ = Arc::new(vmar_);
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
// clone free regions
|
||||||
|
for (free_region_base, free_region) in &inner.free_regions {
|
||||||
|
new_vmar_
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.free_regions
|
||||||
|
.insert(*free_region_base, free_region.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone child vmars
|
||||||
|
for (child_vmar_base, child_vmar_) in &inner.child_vmar_s {
|
||||||
|
let parent_of_forked_child = Arc::downgrade(&new_vmar_);
|
||||||
|
let forked_child_vmar = child_vmar_.fork_vmar_(parent_of_forked_child)?;
|
||||||
|
new_vmar_
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.child_vmar_s
|
||||||
|
.insert(*child_vmar_base, forked_child_vmar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone vm mappings
|
||||||
|
for (vm_mapping_base, vm_mapping) in &inner.vm_mappings {
|
||||||
|
let parent_of_forked_mapping = Arc::downgrade(&new_vmar_);
|
||||||
|
let forked_mapping = Arc::new(vm_mapping.fork_mapping(parent_of_forked_mapping)?);
|
||||||
|
new_vmar_
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.vm_mappings
|
||||||
|
.insert(*vm_mapping_base, forked_mapping);
|
||||||
|
}
|
||||||
|
Ok(new_vmar_)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get mapped vmo at given offset
|
||||||
|
pub fn get_mapped_vmo(&self, offset: Vaddr) -> Result<Vmo<Rights>> {
|
||||||
|
for (vm_mapping_base, vm_mapping) in &self.inner.lock().vm_mappings {
|
||||||
|
if *vm_mapping_base <= offset && offset < *vm_mapping_base + vm_mapping.size() {
|
||||||
|
return Ok(vm_mapping.vmo().dup()?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return_errno_with_message!(Errno::EACCES, "No mapped vmo at this offset");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,28 +656,86 @@ impl<R> Vmar<R> {
|
|||||||
pub fn base(&self) -> Vaddr {
|
pub fn base(&self) -> Vaddr {
|
||||||
self.0.base
|
self.0.base
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The size of the vmar in bytes.
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.0.size
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
/// Fork a vmar for child process
|
||||||
/// The memory access permissions of memory mappings.
|
pub fn fork_vmar(&self) -> Result<Vmar<Full>> {
|
||||||
pub struct VmPerms: u32 {
|
let rights = Rights::all();
|
||||||
/// Readable.
|
self.check_rights(rights)?;
|
||||||
const READ = 1 << 0;
|
let vmar_ = self.0.fork_vmar_(Weak::new())?;
|
||||||
/// Writable.
|
Ok(Vmar(vmar_, Full::new()))
|
||||||
const WRITE = 1 << 1;
|
}
|
||||||
/// Executable.
|
|
||||||
const EXEC = 1 << 2;
|
/// get a mapped vmo
|
||||||
|
pub fn get_mapped_vmo(&self, offset: Vaddr) -> Result<Vmo<Rights>> {
|
||||||
|
let rights = Rights::all();
|
||||||
|
self.check_rights(rights)?;
|
||||||
|
self.0.get_mapped_vmo(offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Rights> for VmPerms {
|
#[derive(Debug, Clone)]
|
||||||
fn from(rights: Rights) -> VmPerms {
|
pub struct FreeRegion {
|
||||||
todo!()
|
range: Range<Vaddr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FreeRegion {
|
||||||
|
pub fn new(range: Range<Vaddr>) -> Self {
|
||||||
|
Self { range }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&self) -> Vaddr {
|
||||||
|
self.range.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(&self) -> Vaddr {
|
||||||
|
self.range.end
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
self.range.end - self.range.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> &Range<usize> {
|
||||||
|
&self.range
|
||||||
|
}
|
||||||
|
|
||||||
|
/// allocate a range in this free region.
|
||||||
|
/// The range is ensured to be contained in current region before call this function.
|
||||||
|
/// The return vector contains regions that are not allocated. Since the allocate_range can be
|
||||||
|
/// in the middle of a free region, the original region may be split as at most two regions.
|
||||||
|
pub fn allocate_range(&self, allocate_range: Range<Vaddr>) -> Vec<FreeRegion> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
if self.range.start < allocate_range.start {
|
||||||
|
let free_region = FreeRegion::new(self.range.start..allocate_range.start);
|
||||||
|
res.push(free_region);
|
||||||
|
}
|
||||||
|
if allocate_range.end < self.range.end {
|
||||||
|
let free_region = FreeRegion::new(allocate_range.end..self.range.end);
|
||||||
|
res.push(free_region);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_other_region(&mut self, other_region: &FreeRegion) {
|
||||||
|
assert!(self.range.end == other_region.range.start);
|
||||||
|
assert!(self.range.start < other_region.range.end);
|
||||||
|
self.range = self.range.start..other_region.range.end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<VmPerms> for Rights {
|
/// determine whether two ranges are intersected.
|
||||||
fn from(vm_perms: VmPerms) -> Rights {
|
pub fn is_intersected(range1: &Range<usize>, range2: &Range<usize>) -> bool {
|
||||||
todo!()
|
range1.start.max(range2.start) < range1.end.min(range2.end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get the intersection range of two ranges.
|
||||||
|
/// The two ranges should be ensured to be intersected.
|
||||||
|
pub fn get_intersected_range(range1: &Range<usize>, range2: &Range<usize>) -> Range<usize> {
|
||||||
|
debug_assert!(is_intersected(range1, range2));
|
||||||
|
range1.start.max(range2.start)..range1.end.min(range2.end)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
//! Options for allocating child VMARs and creating mappings.
|
//! Options for allocating child VMARs.
|
||||||
|
|
||||||
use jinux_frame::prelude::Result;
|
use jinux_frame::config::PAGE_SIZE;
|
||||||
use jinux_frame::{config::PAGE_SIZE, vm::Vaddr};
|
use jinux_frame::{Error, Result};
|
||||||
|
|
||||||
use crate::vm::vmo::Vmo;
|
use super::Vmar;
|
||||||
|
|
||||||
use super::{VmPerms, Vmar};
|
|
||||||
|
|
||||||
/// Options for allocating a child VMAR, which must not overlap with any
|
/// Options for allocating a child VMAR, which must not overlap with any
|
||||||
/// existing mappings or child VMARs.
|
/// existing mappings or child VMARs.
|
||||||
@ -27,7 +25,7 @@ use super::{VmPerms, Vmar};
|
|||||||
/// assert!(child_vmar.size() == child_size);
|
/// assert!(child_vmar.size() == child_size);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// A child VMO created from a parent VMO of _static_ capability is also a
|
/// A child VMAR created from a parent VMAR of _static_ capability is also a
|
||||||
/// _static_ capability.
|
/// _static_ capability.
|
||||||
/// ```
|
/// ```
|
||||||
/// use jinux_std::prelude::*;
|
/// use jinux_std::prelude::*;
|
||||||
@ -45,8 +43,8 @@ use super::{VmPerms, Vmar};
|
|||||||
pub struct VmarChildOptions<R> {
|
pub struct VmarChildOptions<R> {
|
||||||
parent: Vmar<R>,
|
parent: Vmar<R>,
|
||||||
size: usize,
|
size: usize,
|
||||||
offset: usize,
|
offset: Option<usize>,
|
||||||
align: usize,
|
align: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> VmarChildOptions<R> {
|
impl<R> VmarChildOptions<R> {
|
||||||
@ -58,8 +56,8 @@ impl<R> VmarChildOptions<R> {
|
|||||||
Self {
|
Self {
|
||||||
parent,
|
parent,
|
||||||
size,
|
size,
|
||||||
offset: 0,
|
offset: None,
|
||||||
align: PAGE_SIZE,
|
align: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +67,8 @@ impl<R> VmarChildOptions<R> {
|
|||||||
///
|
///
|
||||||
/// The alignment must be a power of two and a multiple of the page size.
|
/// The alignment must be a power of two and a multiple of the page size.
|
||||||
pub fn align(mut self, align: usize) -> Self {
|
pub fn align(mut self, align: usize) -> Self {
|
||||||
todo!()
|
self.align = Some(align);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the offset of the child VMAR.
|
/// Sets the offset of the child VMAR.
|
||||||
@ -84,7 +83,8 @@ impl<R> VmarChildOptions<R> {
|
|||||||
///
|
///
|
||||||
/// The offset must be page-aligned.
|
/// The offset must be page-aligned.
|
||||||
pub fn offset(mut self, offset: usize) -> Self {
|
pub fn offset(mut self, offset: usize) -> Self {
|
||||||
todo!()
|
self.offset = Some(offset);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates the child VMAR according to the specified options.
|
/// Allocates the child VMAR according to the specified options.
|
||||||
@ -94,118 +94,40 @@ impl<R> VmarChildOptions<R> {
|
|||||||
/// # Access rights
|
/// # Access rights
|
||||||
///
|
///
|
||||||
/// The child VMAR is initially assigned all the parent's access rights.
|
/// The child VMAR is initially assigned all the parent's access rights.
|
||||||
pub fn alloc(mut self) -> Result<Vmar<R>> {
|
pub fn alloc(self) -> Result<Vmar<R>> {
|
||||||
todo!()
|
// check align
|
||||||
}
|
let align = if let Some(align) = self.align {
|
||||||
}
|
debug_assert!(align % PAGE_SIZE == 0);
|
||||||
|
debug_assert!(align.is_power_of_two());
|
||||||
/// Options for creating a new mapping. The mapping is not allowed to overlap
|
if align % PAGE_SIZE != 0 || !align.is_power_of_two() {
|
||||||
/// with any child VMARs. And unless specified otherwise, it is not allowed
|
return Err(Error::InvalidArgs);
|
||||||
/// to overlap with any existing mapping, either.
|
}
|
||||||
pub struct VmarMapOptions<R1, R2> {
|
align
|
||||||
parent: Vmar<R1>,
|
} else {
|
||||||
vmo: Vmo<R2>,
|
PAGE_SIZE
|
||||||
perms: VmPerms,
|
};
|
||||||
vmo_offset: usize,
|
// check size
|
||||||
size: usize,
|
if self.size % align != 0 {
|
||||||
offset: Option<usize>,
|
return Err(Error::InvalidArgs);
|
||||||
align: usize,
|
}
|
||||||
can_overwrite: bool,
|
// check offset
|
||||||
}
|
let root_vmar_offset = if let Some(offset) = self.offset {
|
||||||
|
if offset % PAGE_SIZE != 0 {
|
||||||
impl<R1, R2> VmarMapOptions<R1, R2> {
|
return Err(Error::InvalidArgs);
|
||||||
/// Creates a default set of options with the VMO and the memory access
|
}
|
||||||
/// permissions.
|
let root_vmar_offset = offset + self.parent.base();
|
||||||
///
|
if root_vmar_offset % align != 0 {
|
||||||
/// The VMO must have access rights that correspond to the memory
|
return Err(Error::InvalidArgs);
|
||||||
/// access permissions. For example, if `perms` contains `VmPerm::Write`,
|
}
|
||||||
/// then `vmo.rights()` should contain `Rights::WRITE`.
|
Some(root_vmar_offset)
|
||||||
pub fn new(parent: Vmar<R1>, vmo: Vmo<R2>, perms: VmPerms) -> Self {
|
} else {
|
||||||
let size = vmo.size();
|
None
|
||||||
Self {
|
};
|
||||||
parent,
|
let child_vmar_ = self
|
||||||
vmo,
|
.parent
|
||||||
perms,
|
.0
|
||||||
vmo_offset: 0,
|
.alloc_child_vmar(root_vmar_offset, self.size, align)?;
|
||||||
size,
|
let child_vmar = Vmar(child_vmar_, self.parent.1);
|
||||||
offset: None,
|
Ok(child_vmar)
|
||||||
align: PAGE_SIZE,
|
|
||||||
can_overwrite: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the offset of the first memory page in the VMO that is to be
|
|
||||||
/// mapped into the VMAR.
|
|
||||||
///
|
|
||||||
/// The offset must be page-aligned and within the VMO.
|
|
||||||
///
|
|
||||||
/// The default value is zero.
|
|
||||||
pub fn vmo_offset(mut self, offset: usize) -> Self {
|
|
||||||
self.vmo_offset = offset;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the size of the mapping.
|
|
||||||
///
|
|
||||||
/// The size of a mapping may not be equal to that of the VMO.
|
|
||||||
/// For example, it is ok to create a mapping whose size is larger than
|
|
||||||
/// that of the VMO, although one cannot read from or write to the
|
|
||||||
/// part of the mapping that is not backed by the VMO.
|
|
||||||
/// So you may wonder: what is the point of supporting such _oversized_
|
|
||||||
/// mappings? The reason is two-fold.
|
|
||||||
/// 1. VMOs are resizable. So even if a mapping is backed by a VMO whose
|
|
||||||
/// size is equal to that of the mapping initially, we cannot prevent
|
|
||||||
/// the VMO from shrinking.
|
|
||||||
/// 2. Mappings are not allowed to overlap by default. As a result,
|
|
||||||
/// oversized mappings can serve as a placeholder to prevent future
|
|
||||||
/// mappings from occupying some particular address ranges accidentally.
|
|
||||||
///
|
|
||||||
/// The default value is the size of the VMO.
|
|
||||||
pub fn size(mut self, size: usize) -> Self {
|
|
||||||
self.size = size;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the mapping's alignment.
|
|
||||||
///
|
|
||||||
/// The default value is the page size.
|
|
||||||
///
|
|
||||||
/// The provided alignment must be a power of two and a multiple of the
|
|
||||||
/// page size.
|
|
||||||
pub fn align(mut self, align: usize) -> Self {
|
|
||||||
self.align = align;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the mapping's offset inside the VMAR.
|
|
||||||
///
|
|
||||||
/// The offset must satisfy the alignment requirement.
|
|
||||||
/// Also, the mapping's range `[offset, offset + size)` must be within
|
|
||||||
/// the VMAR.
|
|
||||||
///
|
|
||||||
/// If not set, the system will choose an offset automatically.
|
|
||||||
pub fn offset(mut self, offset: usize) -> Self {
|
|
||||||
self.offset = Some(offset);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether the mapping can overwrite existing mappings.
|
|
||||||
///
|
|
||||||
/// The default value is false.
|
|
||||||
///
|
|
||||||
/// If this option is set to true, then the `offset` option must be
|
|
||||||
/// set.
|
|
||||||
pub fn can_overwrite(mut self, can_overwrite: bool) -> Self {
|
|
||||||
self.can_overwrite = can_overwrite;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the mapping.
|
|
||||||
///
|
|
||||||
/// All options will be checked at this point.
|
|
||||||
///
|
|
||||||
/// On success, the virtual address of the new mapping is returned.
|
|
||||||
pub fn build(mut self) -> Result<Vaddr> {
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use crate::prelude::*;
|
||||||
use jinux_frame::prelude::Result;
|
use jinux_frame::vm::VmIo;
|
||||||
use jinux_frame::{vm::VmIo, Error};
|
|
||||||
use jinux_rights_proc::require;
|
use jinux_rights_proc::require;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
rights::{Dup, Read, Rights, TRights},
|
rights::{Dup, Rights, TRights},
|
||||||
vm::vmo::Vmo,
|
vm::{page_fault_handler::PageFaultHandler, vmo::Vmo},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
options::{VmarChildOptions, VmarMapOptions},
|
options::VmarChildOptions, vm_mapping::VmarMapOptions, VmPerms, Vmar, VmarRightsOp, Vmar_,
|
||||||
VmPerms, Vmar, Vmar_,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<R: TRights> Vmar<R> {
|
impl<R: TRights> Vmar<R> {
|
||||||
@ -21,8 +19,8 @@ impl<R: TRights> Vmar<R> {
|
|||||||
/// # Access rights
|
/// # Access rights
|
||||||
///
|
///
|
||||||
/// A root VMAR is initially given full access rights.
|
/// A root VMAR is initially given full access rights.
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new_root() -> Result<Self> {
|
||||||
let inner = Arc::new(Vmar_::new()?);
|
let inner = Arc::new(Vmar_::new_root()?);
|
||||||
let rights = R::new();
|
let rights = R::new();
|
||||||
let new_self = Self(inner, rights);
|
let new_self = Self(inner, rights);
|
||||||
Ok(new_self)
|
Ok(new_self)
|
||||||
@ -112,6 +110,12 @@ impl<R: TRights> Vmar<R> {
|
|||||||
self.0.protect(perms, range)
|
self.0.protect(perms, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// clear all mappings and children vmars.
|
||||||
|
/// After being cleared, this vmar will become an empty vmar
|
||||||
|
pub fn clear(&self) -> Result<()> {
|
||||||
|
self.0.clear_root_vmar()
|
||||||
|
}
|
||||||
|
|
||||||
/// Destroy a VMAR, including all its mappings and children VMARs.
|
/// Destroy a VMAR, including all its mappings and children VMARs.
|
||||||
///
|
///
|
||||||
/// After being destroyed, the VMAR becomes useless and returns errors
|
/// After being destroyed, the VMAR becomes useless and returns errors
|
||||||
@ -141,37 +145,57 @@ impl<R: TRights> Vmar<R> {
|
|||||||
/// The method requires the Dup right.
|
/// The method requires the Dup right.
|
||||||
#[require(R > Dup)]
|
#[require(R > Dup)]
|
||||||
pub fn dup(&self) -> Result<Self> {
|
pub fn dup(&self) -> Result<Self> {
|
||||||
todo!()
|
Ok(Vmar(self.0.clone(), self.1))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Strict the access rights.
|
/// Strict the access rights.
|
||||||
#[require(R > R1)]
|
#[require(R > R1)]
|
||||||
pub fn restrict<R1>(mut self) -> Vmo<R1> {
|
pub fn restrict<R1: TRights>(self) -> Vmar<R1> {
|
||||||
todo!()
|
Vmar(self.0, R1::new())
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the access rights.
|
|
||||||
pub const fn rights(&self) -> Rights {
|
|
||||||
Rights::from_bits(R::BITS).unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_rights(&self, rights: Rights) -> Result<()> {
|
fn check_rights(&self, rights: Rights) -> Result<()> {
|
||||||
if self.rights().contains(rights) {
|
if self.rights().contains(rights) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::AccessDenied)
|
return_errno_with_message!(Errno::EACCES, "check rights failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: TRights> VmIo for Vmar<R> {
|
impl<R: TRights> VmIo for Vmar<R> {
|
||||||
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> jinux_frame::Result<()> {
|
||||||
self.check_rights(Rights::READ)?;
|
self.check_rights(Rights::READ)?;
|
||||||
self.0.read(offset, buf)
|
self.0.read(offset, buf)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> jinux_frame::Result<()> {
|
||||||
self.check_rights(Rights::WRITE)?;
|
self.check_rights(Rights::WRITE)?;
|
||||||
self.0.write(offset, buf)
|
self.0.write(offset, buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TRights> PageFaultHandler for Vmar<R> {
|
||||||
|
fn handle_page_fault(
|
||||||
|
&self,
|
||||||
|
page_fault_addr: Vaddr,
|
||||||
|
not_present: bool,
|
||||||
|
write: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
if write {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
} else {
|
||||||
|
self.check_rights(Rights::READ)?;
|
||||||
|
}
|
||||||
|
self.0
|
||||||
|
.handle_page_fault(page_fault_addr, not_present, write)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TRights> VmarRightsOp for Vmar<R> {
|
||||||
|
fn rights(&self) -> Rights {
|
||||||
|
Rights::from_bits(R::BITS).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
444
src/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs
Normal file
444
src/services/libs/jinux-std/src/vm/vmar/vm_mapping.rs
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use core::ops::Range;
|
||||||
|
use jinux_frame::vm::VmMapOptions;
|
||||||
|
use jinux_frame::vm::{VmFrameVec, VmIo, VmPerm};
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
use crate::vm::{
|
||||||
|
vmo::get_page_idx_range,
|
||||||
|
vmo::{Vmo, VmoChildOptions},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Vmar, Vmar_};
|
||||||
|
use crate::vm::perms::VmPerms;
|
||||||
|
use crate::vm::vmar::Rights;
|
||||||
|
use crate::vm::vmo::VmoRightsOp;
|
||||||
|
|
||||||
|
/// A VmMapping represents mapping a vmo into a vmar.
|
||||||
|
/// A vmar can has multiple VmMappings, which means multiple vmos are mapped to a vmar.
|
||||||
|
/// A vmo can also contain multiple VmMappings, which means a vmo can be mapped to multiple vmars.
|
||||||
|
/// The reltionship between Vmar and Vmo is M:N.
|
||||||
|
pub struct VmMapping {
|
||||||
|
inner: Mutex<VmMappingInner>,
|
||||||
|
/// The parent vmar. The parent should always point to a valid vmar.
|
||||||
|
parent: Weak<Vmar_>,
|
||||||
|
/// The mapped vmo. The mapped vmo is with dynamic capability.
|
||||||
|
vmo: Vmo<Rights>,
|
||||||
|
/// The map offset of the vmo, in bytes.
|
||||||
|
vmo_offset: usize,
|
||||||
|
/// The size of mapping, in bytes. The map size can even be larger than the size of backup vmo.
|
||||||
|
/// Those pages outside vmo range cannot be read or write.
|
||||||
|
map_size: usize,
|
||||||
|
/// The base address relative to the root vmar where the vmo is mapped.
|
||||||
|
map_to_addr: Vaddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VmMappingInner {
|
||||||
|
/// is destroyed
|
||||||
|
is_destroyed: bool,
|
||||||
|
/// The pages already mapped. The key is the page index in vmo.
|
||||||
|
mapped_pages: BTreeSet<usize>,
|
||||||
|
/// The permission of each page. The key is the page index in vmo.
|
||||||
|
/// This map can be filled when mapping a vmo to vmar and can be modified when call mprotect.
|
||||||
|
/// We keep the options in case the page is not committed(or create copy on write mappings) and will further need these options.
|
||||||
|
page_perms: BTreeMap<usize, VmPerm>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmMapping {
|
||||||
|
pub fn build_mapping<R1, R2>(option: VmarMapOptions<R1, R2>) -> Result<Self> {
|
||||||
|
let VmarMapOptions {
|
||||||
|
parent,
|
||||||
|
vmo,
|
||||||
|
perms,
|
||||||
|
vmo_offset,
|
||||||
|
size,
|
||||||
|
offset,
|
||||||
|
align,
|
||||||
|
can_overwrite,
|
||||||
|
} = option;
|
||||||
|
let Vmar(parent_vmar, _) = parent;
|
||||||
|
let vmo = vmo.to_dyn();
|
||||||
|
let vmo_size = vmo.size();
|
||||||
|
let map_to_addr = parent_vmar.allocate_free_region_for_vmo(
|
||||||
|
vmo_size,
|
||||||
|
size,
|
||||||
|
offset,
|
||||||
|
align,
|
||||||
|
can_overwrite,
|
||||||
|
)?;
|
||||||
|
let mut page_perms = BTreeMap::new();
|
||||||
|
let real_map_size = size.min(vmo_size);
|
||||||
|
|
||||||
|
let perm = VmPerm::from(perms);
|
||||||
|
let page_idx_range = get_page_idx_range(&(vmo_offset..vmo_offset + size));
|
||||||
|
for page_idx in page_idx_range {
|
||||||
|
page_perms.insert(page_idx, perm);
|
||||||
|
}
|
||||||
|
|
||||||
|
let vm_space = parent_vmar.vm_space();
|
||||||
|
let mut mapped_pages = BTreeSet::new();
|
||||||
|
let mapped_page_idx_range = get_page_idx_range(&(vmo_offset..vmo_offset + real_map_size));
|
||||||
|
let start_page_idx = mapped_page_idx_range.start;
|
||||||
|
for page_idx in mapped_page_idx_range {
|
||||||
|
let mut vm_map_options = VmMapOptions::new();
|
||||||
|
let page_map_addr = map_to_addr + (page_idx - start_page_idx) * PAGE_SIZE;
|
||||||
|
vm_map_options.addr(Some(page_map_addr));
|
||||||
|
vm_map_options.perm(perm.clone());
|
||||||
|
vm_map_options.can_overwrite(can_overwrite);
|
||||||
|
vm_map_options.align(align);
|
||||||
|
if let Ok(frames) = vmo.get_backup_frame(page_idx, false, false) {
|
||||||
|
vm_space.map(frames, &vm_map_options)?;
|
||||||
|
mapped_pages.insert(page_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let vm_mapping_inner = VmMappingInner {
|
||||||
|
is_destroyed: false,
|
||||||
|
mapped_pages,
|
||||||
|
page_perms,
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
inner: Mutex::new(vm_mapping_inner),
|
||||||
|
parent: Arc::downgrade(&parent_vmar),
|
||||||
|
vmo,
|
||||||
|
vmo_offset,
|
||||||
|
map_size: size,
|
||||||
|
map_to_addr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn vmo(&self) -> &Vmo<Rights> {
|
||||||
|
&self.vmo
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new committed page and map it to vmspace. If copy on write is set, it's allowed to unmap the page at the same address.
|
||||||
|
/// FIXME: This implementation based on the truth that we map one page at a time. If multiple pages are mapped together, this implementation may have problems
|
||||||
|
pub(super) fn map_one_page(&self, page_idx: usize, frames: VmFrameVec) -> Result<()> {
|
||||||
|
let parent = self.parent.upgrade().unwrap();
|
||||||
|
let vm_space = parent.vm_space();
|
||||||
|
let map_addr = page_idx * PAGE_SIZE + self.map_to_addr;
|
||||||
|
let vm_perm = self.inner.lock().page_perms.get(&page_idx).unwrap().clone();
|
||||||
|
let mut vm_map_options = VmMapOptions::new();
|
||||||
|
vm_map_options.addr(Some(map_addr));
|
||||||
|
vm_map_options.perm(vm_perm.clone());
|
||||||
|
// copy on write allows unmap the mapped page
|
||||||
|
if self.vmo.is_cow_child() && vm_space.is_mapped(map_addr) {
|
||||||
|
vm_space.unmap(&(map_addr..(map_addr + PAGE_SIZE))).unwrap();
|
||||||
|
}
|
||||||
|
vm_space.map(frames, &vm_map_options)?;
|
||||||
|
self.inner.lock().mapped_pages.insert(page_idx);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// unmap a page
|
||||||
|
pub(super) fn unmap_one_page(&self, page_idx: usize) -> Result<()> {
|
||||||
|
let parent = self.parent.upgrade().unwrap();
|
||||||
|
let vm_space = parent.vm_space();
|
||||||
|
let map_addr = page_idx * PAGE_SIZE + self.map_to_addr;
|
||||||
|
let range = map_addr..(map_addr + PAGE_SIZE);
|
||||||
|
if vm_space.is_mapped(map_addr) {
|
||||||
|
vm_space.unmap(&range)?;
|
||||||
|
}
|
||||||
|
self.inner.lock().mapped_pages.remove(&page_idx);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the mapping's start address
|
||||||
|
pub(super) fn map_to_addr(&self) -> Vaddr {
|
||||||
|
self.map_to_addr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn size(&self) -> usize {
|
||||||
|
self.map_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
|
let vmo_read_offset = self.vmo_offset + offset;
|
||||||
|
self.vmo.read_bytes(vmo_read_offset, buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
|
let vmo_write_offset = self.vmo_offset + offset;
|
||||||
|
self.vmo.write_bytes(vmo_write_offset, buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmap pages in the range
|
||||||
|
pub fn unmap(&self, range: Range<usize>, destroy: bool) -> Result<()> {
|
||||||
|
let vmo_map_range = (range.start - self.map_to_addr)..(range.end - self.map_to_addr);
|
||||||
|
let page_idx_range = get_page_idx_range(&vmo_map_range);
|
||||||
|
for page_idx in page_idx_range {
|
||||||
|
self.unmap_one_page(page_idx)?;
|
||||||
|
}
|
||||||
|
if destroy && range == self.range() {
|
||||||
|
self.inner.lock().is_destroyed = false;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_destroyed(&self) -> bool {
|
||||||
|
self.inner.lock().is_destroyed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_page_fault(
|
||||||
|
&self,
|
||||||
|
page_fault_addr: Vaddr,
|
||||||
|
not_present: bool,
|
||||||
|
write: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
let vmo_offset = self.vmo_offset + page_fault_addr - self.map_to_addr;
|
||||||
|
if vmo_offset >= self.vmo.size() {
|
||||||
|
return_errno_with_message!(Errno::EACCES, "page fault addr is not backed up by a vmo");
|
||||||
|
}
|
||||||
|
if write {
|
||||||
|
self.vmo.check_rights(Rights::WRITE)?;
|
||||||
|
} else {
|
||||||
|
self.vmo.check_rights(Rights::READ)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the backup frame for page
|
||||||
|
let page_idx = vmo_offset / PAGE_SIZE;
|
||||||
|
let frames = self.vmo.get_backup_frame(page_idx, write, true)?;
|
||||||
|
// map the page
|
||||||
|
self.map_one_page(page_idx, frames)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn protect(&self, perms: VmPerms, range: Range<usize>) -> Result<()> {
|
||||||
|
let rights = Rights::from(perms);
|
||||||
|
self.vmo().check_rights(rights)?;
|
||||||
|
// FIXME: should we commit and map these pages before protect vmspace?
|
||||||
|
let vmar = self.parent.upgrade().unwrap();
|
||||||
|
let vm_space = vmar.vm_space();
|
||||||
|
let perm = VmPerm::from(perms);
|
||||||
|
vm_space.protect(&range, perm)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn fork_mapping(&self, new_parent: Weak<Vmar_>) -> Result<VmMapping> {
|
||||||
|
let VmMapping {
|
||||||
|
inner,
|
||||||
|
parent,
|
||||||
|
vmo,
|
||||||
|
vmo_offset,
|
||||||
|
map_size,
|
||||||
|
map_to_addr,
|
||||||
|
} = self;
|
||||||
|
let parent_vmo = vmo.clone();
|
||||||
|
let vmo_size = parent_vmo.size();
|
||||||
|
let child_vmo = VmoChildOptions::new_cow(parent_vmo, 0..vmo_size).alloc()?;
|
||||||
|
let parent_vmar = new_parent.upgrade().unwrap();
|
||||||
|
let vm_space = parent_vmar.vm_space();
|
||||||
|
|
||||||
|
let real_map_size = self.size().min(child_vmo.size());
|
||||||
|
let vmo_offset = *vmo_offset;
|
||||||
|
let page_idx_range = get_page_idx_range(&(vmo_offset..vmo_offset + real_map_size));
|
||||||
|
let start_page_idx = page_idx_range.start;
|
||||||
|
let mut mapped_pages = BTreeSet::new();
|
||||||
|
|
||||||
|
for page_idx in page_idx_range {
|
||||||
|
// When map pages from parent, we should forbid write access to these pages.
|
||||||
|
// So any write access to these pages will trigger a page fault. Then, we can allocate new pages for the page.
|
||||||
|
let mut vm_perm = inner.lock().page_perms.get(&page_idx).unwrap().clone();
|
||||||
|
vm_perm -= VmPerm::W;
|
||||||
|
let mut vm_map_options = VmMapOptions::new();
|
||||||
|
let map_addr = (page_idx - start_page_idx) * PAGE_SIZE + self.map_to_addr;
|
||||||
|
vm_map_options.addr(Some(map_addr));
|
||||||
|
vm_map_options.perm(vm_perm);
|
||||||
|
if let Ok(frames) = child_vmo.get_backup_frame(page_idx, false, false) {
|
||||||
|
vm_space.map(frames, &vm_map_options)?;
|
||||||
|
mapped_pages.insert(page_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let is_destroyed = inner.lock().is_destroyed;
|
||||||
|
let page_perms = inner.lock().page_perms.clone();
|
||||||
|
let inner = VmMappingInner {
|
||||||
|
is_destroyed,
|
||||||
|
mapped_pages,
|
||||||
|
page_perms,
|
||||||
|
};
|
||||||
|
Ok(VmMapping {
|
||||||
|
inner: Mutex::new(inner),
|
||||||
|
parent: new_parent,
|
||||||
|
vmo: child_vmo,
|
||||||
|
vmo_offset,
|
||||||
|
map_size: *map_size,
|
||||||
|
map_to_addr: *map_to_addr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> Range<usize> {
|
||||||
|
self.map_to_addr..self.map_to_addr + self.map_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Options for creating a new mapping. The mapping is not allowed to overlap
|
||||||
|
/// with any child VMARs. And unless specified otherwise, it is not allowed
|
||||||
|
/// to overlap with any existing mapping, either.
|
||||||
|
pub struct VmarMapOptions<R1, R2> {
|
||||||
|
parent: Vmar<R1>,
|
||||||
|
vmo: Vmo<R2>,
|
||||||
|
perms: VmPerms,
|
||||||
|
vmo_offset: usize,
|
||||||
|
size: usize,
|
||||||
|
offset: Option<usize>,
|
||||||
|
align: usize,
|
||||||
|
can_overwrite: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R1, R2> VmarMapOptions<R1, R2> {
|
||||||
|
/// Creates a default set of options with the VMO and the memory access
|
||||||
|
/// permissions.
|
||||||
|
///
|
||||||
|
/// The VMO must have access rights that correspond to the memory
|
||||||
|
/// access permissions. For example, if `perms` contains `VmPerm::Write`,
|
||||||
|
/// then `vmo.rights()` should contain `Rights::WRITE`.
|
||||||
|
pub fn new(parent: Vmar<R1>, vmo: Vmo<R2>, perms: VmPerms) -> Self {
|
||||||
|
let size = vmo.size();
|
||||||
|
Self {
|
||||||
|
parent,
|
||||||
|
vmo,
|
||||||
|
perms,
|
||||||
|
vmo_offset: 0,
|
||||||
|
size,
|
||||||
|
offset: None,
|
||||||
|
align: PAGE_SIZE,
|
||||||
|
can_overwrite: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the offset of the first memory page in the VMO that is to be
|
||||||
|
/// mapped into the VMAR.
|
||||||
|
///
|
||||||
|
/// The offset must be page-aligned and within the VMO.
|
||||||
|
///
|
||||||
|
/// The default value is zero.
|
||||||
|
pub fn vmo_offset(mut self, offset: usize) -> Self {
|
||||||
|
self.vmo_offset = offset;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the size of the mapping.
|
||||||
|
///
|
||||||
|
/// The size of a mapping may not be equal to that of the VMO.
|
||||||
|
/// For example, it is ok to create a mapping whose size is larger than
|
||||||
|
/// that of the VMO, although one cannot read from or write to the
|
||||||
|
/// part of the mapping that is not backed by the VMO.
|
||||||
|
/// So you may wonder: what is the point of supporting such _oversized_
|
||||||
|
/// mappings? The reason is two-fold.
|
||||||
|
/// 1. VMOs are resizable. So even if a mapping is backed by a VMO whose
|
||||||
|
/// size is equal to that of the mapping initially, we cannot prevent
|
||||||
|
/// the VMO from shrinking.
|
||||||
|
/// 2. Mappings are not allowed to overlap by default. As a result,
|
||||||
|
/// oversized mappings can serve as a placeholder to prevent future
|
||||||
|
/// mappings from occupying some particular address ranges accidentally.
|
||||||
|
///
|
||||||
|
/// The default value is the size of the VMO.
|
||||||
|
pub fn size(mut self, size: usize) -> Self {
|
||||||
|
self.size = size;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the mapping's alignment.
|
||||||
|
///
|
||||||
|
/// The default value is the page size.
|
||||||
|
///
|
||||||
|
/// The provided alignment must be a power of two and a multiple of the
|
||||||
|
/// page size.
|
||||||
|
pub fn align(mut self, align: usize) -> Self {
|
||||||
|
self.align = align;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the mapping's offset inside the VMAR.
|
||||||
|
///
|
||||||
|
/// The offset must satisfy the alignment requirement.
|
||||||
|
/// Also, the mapping's range `[offset, offset + size)` must be within
|
||||||
|
/// the VMAR.
|
||||||
|
///
|
||||||
|
/// If not set, the system will choose an offset automatically.
|
||||||
|
pub fn offset(mut self, offset: usize) -> Self {
|
||||||
|
self.offset = Some(offset);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether the mapping can overwrite existing mappings.
|
||||||
|
///
|
||||||
|
/// The default value is false.
|
||||||
|
///
|
||||||
|
/// If this option is set to true, then the `offset` option must be
|
||||||
|
/// set.
|
||||||
|
pub fn can_overwrite(mut self, can_overwrite: bool) -> Self {
|
||||||
|
self.can_overwrite = can_overwrite;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the mapping.
|
||||||
|
///
|
||||||
|
/// All options will be checked at this point.
|
||||||
|
///
|
||||||
|
/// On success, the virtual address of the new mapping is returned.
|
||||||
|
pub fn build(self) -> Result<Vaddr> {
|
||||||
|
self.check_options()?;
|
||||||
|
let parent_vmar = self.parent.0.clone();
|
||||||
|
let vmo_ = self.vmo.0.clone();
|
||||||
|
let vm_mapping = Arc::new(VmMapping::build_mapping(self)?);
|
||||||
|
let map_to_addr = vm_mapping.map_to_addr();
|
||||||
|
parent_vmar.add_mapping(vm_mapping);
|
||||||
|
Ok(map_to_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// check whether all options are valid
|
||||||
|
fn check_options(&self) -> Result<()> {
|
||||||
|
// check align
|
||||||
|
debug_assert!(self.align % PAGE_SIZE == 0);
|
||||||
|
debug_assert!(self.align.is_power_of_two());
|
||||||
|
if self.align % PAGE_SIZE != 0 || !self.align.is_power_of_two() {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "invalid align");
|
||||||
|
}
|
||||||
|
debug_assert!(self.vmo_offset % self.align == 0);
|
||||||
|
if self.vmo_offset % self.align != 0 {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "invalid vmo offset");
|
||||||
|
}
|
||||||
|
if let Some(offset) = self.offset {
|
||||||
|
debug_assert!(offset % self.align == 0);
|
||||||
|
if offset % self.align != 0 {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "invalid offset");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.check_perms()?;
|
||||||
|
self.check_overwrite()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// check whether the vmperm is subset of vmo rights
|
||||||
|
fn check_perms(&self) -> Result<()> {
|
||||||
|
let perm_rights = Rights::from(self.perms);
|
||||||
|
self.vmo.check_rights(perm_rights)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// check whether the vmo will overwrite with any existing vmo or vmar
|
||||||
|
fn check_overwrite(&self) -> Result<()> {
|
||||||
|
if self.can_overwrite {
|
||||||
|
// if can_overwrite is set, the offset cannot be None
|
||||||
|
debug_assert!(self.offset != None);
|
||||||
|
if self.offset == None {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EINVAL,
|
||||||
|
"offset can not be none when can overwrite is true"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.offset == None {
|
||||||
|
// if does not specify the offset, we assume the map can always find suitable free region.
|
||||||
|
// FIXME: is this always true?
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let offset = self.offset.unwrap();
|
||||||
|
// we should spare enough space at least for the whole vmo
|
||||||
|
let size = self.size.max(self.vmo.size());
|
||||||
|
let vmo_range = offset..(offset + size);
|
||||||
|
self.parent
|
||||||
|
.0
|
||||||
|
.check_vmo_overwrite(vmo_range, self.can_overwrite)
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
|
|
||||||
use jinux_frame::prelude::Result;
|
use crate::prelude::*;
|
||||||
use jinux_frame::{vm::VmIo, Error};
|
|
||||||
|
use jinux_frame::vm::VmIo;
|
||||||
|
|
||||||
use crate::rights::{Rights, TRights};
|
use crate::rights::{Rights, TRights};
|
||||||
|
|
||||||
|
use super::VmoRightsOp;
|
||||||
use super::{
|
use super::{
|
||||||
options::{VmoCowChild, VmoSliceChild},
|
options::{VmoCowChild, VmoSliceChild},
|
||||||
Vmo, VmoChildOptions,
|
Vmo, VmoChildOptions,
|
||||||
@ -66,6 +68,12 @@ impl Vmo<Rights> {
|
|||||||
Ok(VmoChildOptions::new_cow(dup_self, range))
|
Ok(VmoChildOptions::new_cow(dup_self, range))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// commit a page at specific offset
|
||||||
|
pub fn commit_page(&self, offset: usize) -> Result<()> {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
self.0.commit_page(offset)
|
||||||
|
}
|
||||||
|
|
||||||
/// Commits the pages specified in the range (in bytes).
|
/// Commits the pages specified in the range (in bytes).
|
||||||
///
|
///
|
||||||
/// The range must be within the size of the VMO.
|
/// The range must be within the size of the VMO.
|
||||||
@ -125,42 +133,50 @@ impl Vmo<Rights> {
|
|||||||
/// The method requires the Dup right.
|
/// The method requires the Dup right.
|
||||||
pub fn dup(&self) -> Result<Self> {
|
pub fn dup(&self) -> Result<Self> {
|
||||||
self.check_rights(Rights::DUP)?;
|
self.check_rights(Rights::DUP)?;
|
||||||
todo!()
|
Ok(Self(self.0.clone(), self.1.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restricts the access rights given the mask.
|
/// Restricts the access rights given the mask.
|
||||||
pub fn restrict(mut self, mask: Rights) -> Self {
|
pub fn restrict(mut self, mask: Rights) -> Self {
|
||||||
todo!()
|
self.1 |= mask;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts to a static capability.
|
/// Converts to a static capability.
|
||||||
pub fn to_static<R1: TRights>(self) -> Result<Vmo<R1>> {
|
pub fn to_static<R1: TRights>(self) -> Result<Vmo<R1>> {
|
||||||
self.check_rights(Rights::from_bits(R1::BITS).ok_or(Error::InvalidArgs)?)?;
|
self.check_rights(Rights::from_bits(R1::BITS).ok_or(Error::new(Errno::EINVAL))?)?;
|
||||||
todo!()
|
Ok(Vmo(self.0, R1::new()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the access rights.
|
impl Clone for Vmo<Rights> {
|
||||||
pub fn rights(&self) -> Rights {
|
fn clone(&self) -> Self {
|
||||||
self.1
|
Self(self.0.clone(), self.1.clone())
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_rights(&self, rights: Rights) -> Result<()> {
|
|
||||||
if self.rights().contains(rights) {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::AccessDenied)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmIo for Vmo<Rights> {
|
impl VmIo for Vmo<Rights> {
|
||||||
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> jinux_frame::Result<()> {
|
||||||
self.check_rights(Rights::READ)?;
|
self.check_rights(Rights::READ)?;
|
||||||
self.0.read_bytes(offset, buf)
|
self.0.read_bytes(offset, buf)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> jinux_frame::Result<()> {
|
||||||
self.check_rights(Rights::WRITE)?;
|
self.check_rights(Rights::WRITE)?;
|
||||||
self.0.write_bytes(offset, buf)
|
self.0.write_bytes(offset, buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmoRightsOp for Vmo<Rights> {
|
||||||
|
fn rights(&self) -> Rights {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to a dynamic capability.
|
||||||
|
fn to_dyn(self) -> Vmo<Rights> {
|
||||||
|
let rights = self.rights();
|
||||||
|
Vmo(self.0, rights)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
|
|
||||||
use crate::rights::Rights;
|
use crate::rights::Rights;
|
||||||
use alloc::sync::Arc;
|
use jinux_frame::vm::{Paddr, VmAllocOptions, VmFrameVec, VmIo};
|
||||||
use bitflags::bitflags;
|
use jinux_frame::AlignExt;
|
||||||
use jinux_frame::{prelude::Result, vm::Paddr, Error};
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
mod dyn_cap;
|
mod dyn_cap;
|
||||||
mod options;
|
mod options;
|
||||||
@ -71,7 +72,42 @@ use spin::Mutex;
|
|||||||
/// `Vmo` is easier to use (by offering more powerful APIs) and
|
/// `Vmo` is easier to use (by offering more powerful APIs) and
|
||||||
/// harder to misuse (thanks to its nature of being capability).
|
/// harder to misuse (thanks to its nature of being capability).
|
||||||
///
|
///
|
||||||
pub struct Vmo<R>(Arc<Vmo_>, R);
|
pub struct Vmo<R = Rights>(pub(super) Arc<Vmo_>, R);
|
||||||
|
|
||||||
|
/// Functions exist both for static capbility and dynamic capibility
|
||||||
|
pub trait VmoRightsOp {
|
||||||
|
/// Returns the access rights.
|
||||||
|
fn rights(&self) -> Rights;
|
||||||
|
|
||||||
|
/// Check whether rights is included in self
|
||||||
|
fn check_rights(&self, rights: Rights) -> Result<()> {
|
||||||
|
if self.rights().contains(rights) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "vmo rights check failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to a dynamic capability.
|
||||||
|
fn to_dyn(self) -> Vmo<Rights>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We implement this trait for Vmo, so we can use functions on type like Vmo<R> without trait bounds.
|
||||||
|
// FIXME: This requires the imcomplete feature specialization, which should be fixed further.
|
||||||
|
impl<R> VmoRightsOp for Vmo<R> {
|
||||||
|
default fn rights(&self) -> Rights {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
default fn to_dyn(self) -> Vmo<Rights>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// VMO flags.
|
/// VMO flags.
|
||||||
@ -90,59 +126,302 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Vmo_ {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum VmoType {
|
||||||
|
/// This vmo_ is created as a copy on write child
|
||||||
|
CopyOnWriteChild,
|
||||||
|
/// This vmo_ is created as a slice child
|
||||||
|
SliceChild,
|
||||||
|
/// This vmo_ is not created as a child of a parent vmo
|
||||||
|
NotChild,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct Vmo_ {
|
||||||
|
/// Flags
|
||||||
flags: VmoFlags,
|
flags: VmoFlags,
|
||||||
|
/// VmoInner
|
||||||
inner: Mutex<VmoInner>,
|
inner: Mutex<VmoInner>,
|
||||||
parent: Option<Arc<Vmo_>>,
|
/// Parent Vmo
|
||||||
|
parent: Weak<Vmo_>,
|
||||||
|
/// paddr
|
||||||
|
paddr: Option<Paddr>,
|
||||||
|
/// vmo type
|
||||||
|
vmo_type: VmoType,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VmoInner {
|
struct VmoInner {
|
||||||
//...
|
/// The backup pager
|
||||||
|
pager: Option<Arc<dyn Pager>>,
|
||||||
|
/// size, in bytes
|
||||||
|
size: usize,
|
||||||
|
/// The pages committed. The key is the page index, the value is the backup frame.
|
||||||
|
committed_pages: BTreeMap<usize, VmFrameVec>,
|
||||||
|
/// The pages from the parent that current vmo can access. The pages can only be inherited when create childs vmo.
|
||||||
|
/// We store the page index range
|
||||||
|
inherited_pages: InheritedPages,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pages inherited from parent
|
||||||
|
struct InheritedPages {
|
||||||
|
/// The page index range in child vmo. The pages inside these range are initially inherited from parent vmo.
|
||||||
|
/// The range includes the start page, but not including the end page
|
||||||
|
page_range: Range<usize>,
|
||||||
|
/// The page index offset in parent vmo. That is to say, the page with index `idx` in child vmo corrsponds to
|
||||||
|
/// page with index `idx + parent_page_idx_offset` in parent vmo
|
||||||
|
parent_page_idx_offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InheritedPages {
|
||||||
|
pub fn new_empty() -> Self {
|
||||||
|
Self {
|
||||||
|
page_range: 0..0,
|
||||||
|
parent_page_idx_offset: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(page_range: Range<usize>, parent_page_idx_offset: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
page_range,
|
||||||
|
parent_page_idx_offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_page(&self, page_idx: usize) -> bool {
|
||||||
|
self.page_range.start <= page_idx && page_idx < self.page_range.end
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent_page_idx(&self, child_page_idx: usize) -> Option<usize> {
|
||||||
|
if self.contains_page(child_page_idx) {
|
||||||
|
Some(child_page_idx + self.parent_page_idx_offset)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vmo_ {
|
impl Vmo_ {
|
||||||
pub fn commit_page(&self, offset: usize) -> Result<()> {
|
pub fn commit_page(&self, offset: usize) -> Result<()> {
|
||||||
todo!()
|
let page_idx = offset / PAGE_SIZE;
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
|
||||||
|
if !inner.committed_pages.contains_key(&page_idx) {
|
||||||
|
let frames = match &inner.pager {
|
||||||
|
None => {
|
||||||
|
let vm_alloc_option = VmAllocOptions::new(1);
|
||||||
|
let frames = VmFrameVec::allocate(&vm_alloc_option)?;
|
||||||
|
frames.iter().for_each(|frame| frame.zero());
|
||||||
|
frames
|
||||||
|
}
|
||||||
|
Some(pager) => {
|
||||||
|
let frame = pager.commit_page(offset)?;
|
||||||
|
VmFrameVec::from_one_frame(frame)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
inner.committed_pages.insert(page_idx, frames);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decommit_page(&self, offset: usize) -> Result<()> {
|
pub fn decommit_page(&self, offset: usize) -> Result<()> {
|
||||||
todo!()
|
let page_idx = offset / PAGE_SIZE;
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
if inner.committed_pages.contains_key(&page_idx) {
|
||||||
|
inner.committed_pages.remove(&page_idx);
|
||||||
|
if let Some(pager) = &inner.pager {
|
||||||
|
pager.decommit_page(offset)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit(&self, range: Range<usize>) -> Result<()> {
|
pub fn commit(&self, range: Range<usize>) -> Result<()> {
|
||||||
todo!()
|
let page_idx_range = get_page_idx_range(&range);
|
||||||
|
for page_idx in page_idx_range {
|
||||||
|
let offset = page_idx * PAGE_SIZE;
|
||||||
|
self.commit_page(offset)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decommit(&self, range: Range<usize>) -> Result<()> {
|
pub fn decommit(&self, range: Range<usize>) -> Result<()> {
|
||||||
todo!()
|
let page_idx_range = get_page_idx_range(&range);
|
||||||
|
for page_idx in page_idx_range {
|
||||||
|
let offset = page_idx * PAGE_SIZE;
|
||||||
|
self.decommit_page(offset)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// determine whether a page is commited
|
||||||
|
pub fn page_commited(&self, page_idx: usize) -> bool {
|
||||||
|
self.inner.lock().committed_pages.contains_key(&page_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
pub fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
||||||
todo!()
|
let read_len = buf.len();
|
||||||
|
debug_assert!(offset + read_len <= self.size());
|
||||||
|
if offset + read_len > self.size() {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "read range exceeds vmo size");
|
||||||
|
}
|
||||||
|
let read_range = offset..(offset + read_len);
|
||||||
|
let frames = self.ensure_all_pages_exist(read_range, false)?;
|
||||||
|
let read_offset = offset % PAGE_SIZE;
|
||||||
|
Ok(frames.read_bytes(read_offset, buf)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensure all pages inside range are backed up vm frames, returns the frames.
|
||||||
|
fn ensure_all_pages_exist(&self, range: Range<usize>, write_page: bool) -> Result<VmFrameVec> {
|
||||||
|
let page_idx_range = get_page_idx_range(&range);
|
||||||
|
let mut frames = VmFrameVec::empty();
|
||||||
|
for page_idx in page_idx_range {
|
||||||
|
let mut page_frame = self.get_backup_frame(page_idx, write_page, true)?;
|
||||||
|
frames.append(&mut page_frame)?;
|
||||||
|
}
|
||||||
|
Ok(frames)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the backup frame for a page. If commit_if_none is set, we will commit a new page for the page
|
||||||
|
/// if the page does not have a backup frame.
|
||||||
|
fn get_backup_frame(
|
||||||
|
&self,
|
||||||
|
page_idx: usize,
|
||||||
|
write_page: bool,
|
||||||
|
commit_if_none: bool,
|
||||||
|
) -> Result<VmFrameVec> {
|
||||||
|
// if the page is already commit, return the committed page.
|
||||||
|
if let Some(frames) = self.inner.lock().committed_pages.get(&page_idx) {
|
||||||
|
return Ok(frames.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.vmo_type {
|
||||||
|
// if the vmo is not child, then commit new page
|
||||||
|
VmoType::NotChild => {
|
||||||
|
if commit_if_none {
|
||||||
|
self.commit_page(page_idx * PAGE_SIZE)?;
|
||||||
|
let frames = self
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.committed_pages
|
||||||
|
.get(&page_idx)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
return Ok(frames);
|
||||||
|
} else {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "backup frame does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the vmo is slice child, we will request the frame from parent
|
||||||
|
VmoType::SliceChild => {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
debug_assert!(inner.inherited_pages.contains_page(page_idx));
|
||||||
|
if !inner.inherited_pages.contains_page(page_idx) {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EINVAL,
|
||||||
|
"page does not inherited from parent"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let parent = self.parent.upgrade().unwrap();
|
||||||
|
let parent_page_idx = inner.inherited_pages.parent_page_idx(page_idx).unwrap();
|
||||||
|
return parent.get_backup_frame(parent_page_idx, write_page, commit_if_none);
|
||||||
|
}
|
||||||
|
// If the vmo is copy on write
|
||||||
|
VmoType::CopyOnWriteChild => {
|
||||||
|
if write_page {
|
||||||
|
// write
|
||||||
|
// commit a new page
|
||||||
|
self.commit_page(page_idx * PAGE_SIZE)?;
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
let frames = inner.committed_pages.get(&page_idx).unwrap().clone();
|
||||||
|
if let Some(parent_page_idx) = inner.inherited_pages.parent_page_idx(page_idx) {
|
||||||
|
// copy contents of parent to the frame
|
||||||
|
let mut tmp_buffer = [0u8; PAGE_SIZE];
|
||||||
|
let parent = self.parent.upgrade().unwrap();
|
||||||
|
parent.read_bytes(parent_page_idx * PAGE_SIZE, &mut tmp_buffer)?;
|
||||||
|
frames.write_bytes(0, &tmp_buffer)?;
|
||||||
|
} else {
|
||||||
|
frames.zero();
|
||||||
|
}
|
||||||
|
return Ok(frames);
|
||||||
|
} else {
|
||||||
|
// read
|
||||||
|
if let Some(parent_page_idx) =
|
||||||
|
self.inner.lock().inherited_pages.parent_page_idx(page_idx)
|
||||||
|
{
|
||||||
|
// If it's inherited from parent, we request the page from parent
|
||||||
|
let parent = self.parent.upgrade().unwrap();
|
||||||
|
return parent.get_backup_frame(
|
||||||
|
parent_page_idx,
|
||||||
|
write_page,
|
||||||
|
commit_if_none,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Otherwise, we commit a new page
|
||||||
|
self.commit_page(page_idx * PAGE_SIZE)?;
|
||||||
|
let frames = self
|
||||||
|
.inner
|
||||||
|
.lock()
|
||||||
|
.committed_pages
|
||||||
|
.get(&page_idx)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
// FIXME: should we zero the frames here?
|
||||||
|
frames.zero();
|
||||||
|
return Ok(frames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
pub fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
||||||
todo!()
|
let write_len = buf.len();
|
||||||
|
debug_assert!(offset + write_len <= self.size());
|
||||||
|
if offset + write_len > self.size() {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "write range exceeds the vmo size");
|
||||||
|
}
|
||||||
|
|
||||||
|
let write_range = offset..(offset + write_len);
|
||||||
|
let frames = self.ensure_all_pages_exist(write_range, true)?;
|
||||||
|
let write_offset = offset % PAGE_SIZE;
|
||||||
|
frames.write_bytes(write_offset, buf)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self, range: Range<usize>) -> Result<()> {
|
pub fn clear(&self, range: Range<usize>) -> Result<()> {
|
||||||
todo!()
|
let buffer = vec![0u8; range.end - range.start];
|
||||||
|
self.write_bytes(range.start, &buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
pub fn size(&self) -> usize {
|
||||||
todo!()
|
self.inner.lock().size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&self, new_size: usize) -> Result<()> {
|
pub fn resize(&self, new_size: usize) -> Result<()> {
|
||||||
todo!()
|
assert!(self.flags.contains(VmoFlags::RESIZABLE));
|
||||||
|
let new_size = new_size.align_up(PAGE_SIZE);
|
||||||
|
let old_size = self.size();
|
||||||
|
if new_size == old_size {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_size < old_size {
|
||||||
|
self.decommit(new_size..old_size)?;
|
||||||
|
self.inner.lock().size = new_size;
|
||||||
|
} else {
|
||||||
|
self.commit(old_size..new_size)?;
|
||||||
|
self.inner.lock().size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paddr(&self) -> Option<Paddr> {
|
pub fn paddr(&self) -> Option<Paddr> {
|
||||||
todo!()
|
self.paddr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flags(&self) -> VmoFlags {
|
pub fn flags(&self) -> VmoFlags {
|
||||||
todo!()
|
self.flags.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,4 +441,34 @@ impl<R> Vmo<R> {
|
|||||||
pub fn flags(&self) -> VmoFlags {
|
pub fn flags(&self) -> VmoFlags {
|
||||||
self.0.flags()
|
self.0.flags()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// return whether a page is already committed
|
||||||
|
pub fn has_backup_frame(&self, page_idx: usize) -> bool {
|
||||||
|
if let Ok(_) = self.0.get_backup_frame(page_idx, false, false) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_backup_frame(
|
||||||
|
&self,
|
||||||
|
page_idx: usize,
|
||||||
|
write_page: bool,
|
||||||
|
commit_if_none: bool,
|
||||||
|
) -> Result<VmFrameVec> {
|
||||||
|
self.0
|
||||||
|
.get_backup_frame(page_idx, write_page, commit_if_none)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_cow_child(&self) -> bool {
|
||||||
|
self.0.vmo_type == VmoType::CopyOnWriteChild
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get the page index range that contains the offset range of vmo
|
||||||
|
pub fn get_page_idx_range(vmo_offset_range: &Range<usize>) -> Range<usize> {
|
||||||
|
let start = vmo_offset_range.start.align_down(PAGE_SIZE);
|
||||||
|
let end = vmo_offset_range.end.align_up(PAGE_SIZE);
|
||||||
|
(start / PAGE_SIZE)..(end / PAGE_SIZE)
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,19 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use jinux_frame::vm::{Paddr, VmAllocOptions, VmFrameVec};
|
||||||
use jinux_frame::prelude::Result;
|
use jinux_frame::AlignExt;
|
||||||
use jinux_frame::vm::Paddr;
|
|
||||||
use jinux_rights_proc::require;
|
use jinux_rights_proc::require;
|
||||||
|
use typeflags_util::{SetExtend, SetExtendOp};
|
||||||
|
|
||||||
use crate::rights::{Dup, Rights, TRights};
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use crate::rights::{Dup, Rights, TRights, Write};
|
||||||
|
use crate::vm::vmo::InheritedPages;
|
||||||
|
use crate::vm::vmo::VmoType;
|
||||||
|
use crate::vm::vmo::{VmoInner, Vmo_};
|
||||||
|
|
||||||
|
use super::VmoRightsOp;
|
||||||
use super::{Pager, Vmo, VmoFlags};
|
use super::{Pager, Vmo, VmoFlags};
|
||||||
|
|
||||||
/// Options for allocating a root VMO.
|
/// Options for allocating a root VMO.
|
||||||
@ -50,8 +56,8 @@ pub struct VmoOptions<R = Rights> {
|
|||||||
size: usize,
|
size: usize,
|
||||||
paddr: Option<Paddr>,
|
paddr: Option<Paddr>,
|
||||||
flags: VmoFlags,
|
flags: VmoFlags,
|
||||||
rights: R,
|
rights: Option<R>,
|
||||||
// supplier: Option<Arc<dyn FrameSupplier>>,
|
pager: Option<Arc<dyn Pager>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> VmoOptions<R> {
|
impl<R> VmoOptions<R> {
|
||||||
@ -60,7 +66,13 @@ impl<R> VmoOptions<R> {
|
|||||||
///
|
///
|
||||||
/// The size of the VMO will be rounded up to align with the page size.
|
/// The size of the VMO will be rounded up to align with the page size.
|
||||||
pub fn new(size: usize) -> Self {
|
pub fn new(size: usize) -> Self {
|
||||||
todo!()
|
Self {
|
||||||
|
size,
|
||||||
|
paddr: None,
|
||||||
|
flags: VmoFlags::empty(),
|
||||||
|
rights: None,
|
||||||
|
pager: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the starting physical address of the VMO.
|
/// Sets the starting physical address of the VMO.
|
||||||
@ -70,7 +82,9 @@ impl<R> VmoOptions<R> {
|
|||||||
/// If this option is set, then the underlying pages of VMO must be contiguous.
|
/// If this option is set, then the underlying pages of VMO must be contiguous.
|
||||||
/// So `VmoFlags::IS_CONTIGUOUS` will be set automatically.
|
/// So `VmoFlags::IS_CONTIGUOUS` will be set automatically.
|
||||||
pub fn paddr(mut self, paddr: Paddr) -> Self {
|
pub fn paddr(mut self, paddr: Paddr) -> Self {
|
||||||
todo!()
|
self.paddr = Some(paddr);
|
||||||
|
self.flags |= VmoFlags::CONTIGUOUS;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the VMO flags.
|
/// Sets the VMO flags.
|
||||||
@ -79,12 +93,14 @@ impl<R> VmoOptions<R> {
|
|||||||
///
|
///
|
||||||
/// For more information about the flags, see `VmoFlags`.
|
/// For more information about the flags, see `VmoFlags`.
|
||||||
pub fn flags(mut self, flags: VmoFlags) -> Self {
|
pub fn flags(mut self, flags: VmoFlags) -> Self {
|
||||||
todo!()
|
self.flags = flags;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the pager of the VMO.
|
/// Sets the pager of the VMO.
|
||||||
pub fn pager(mut self, pager: Arc<dyn Pager>) -> Self {
|
pub fn pager(mut self, pager: Arc<dyn Pager>) -> Self {
|
||||||
todo!()
|
self.pager = Some(pager);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +110,16 @@ impl VmoOptions<Rights> {
|
|||||||
/// # Access rights
|
/// # Access rights
|
||||||
///
|
///
|
||||||
/// The VMO is initially assigned full access rights.
|
/// The VMO is initially assigned full access rights.
|
||||||
pub fn alloc(mut self) -> Result<Vmo<Rights>> {
|
pub fn alloc(self) -> Result<Vmo<Rights>> {
|
||||||
todo!()
|
let VmoOptions {
|
||||||
|
size,
|
||||||
|
paddr,
|
||||||
|
flags,
|
||||||
|
pager,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
let vmo_ = alloc_vmo_(size, paddr, flags, pager)?;
|
||||||
|
Ok(Vmo(Arc::new(vmo_), Rights::all()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,8 +130,62 @@ impl<R: TRights> VmoOptions<R> {
|
|||||||
///
|
///
|
||||||
/// The VMO is initially assigned the access rights represented
|
/// The VMO is initially assigned the access rights represented
|
||||||
/// by `R: TRights`.
|
/// by `R: TRights`.
|
||||||
pub fn alloc(mut self) -> Result<Vmo<R>> {
|
pub fn alloc(self) -> Result<Vmo<R>> {
|
||||||
todo!()
|
let VmoOptions {
|
||||||
|
size,
|
||||||
|
paddr,
|
||||||
|
flags,
|
||||||
|
rights,
|
||||||
|
pager,
|
||||||
|
} = self;
|
||||||
|
let vmo_ = alloc_vmo_(size, paddr, flags, pager)?;
|
||||||
|
Ok(Vmo(Arc::new(vmo_), R::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_vmo_(
|
||||||
|
size: usize,
|
||||||
|
paddr: Option<Paddr>,
|
||||||
|
flags: VmoFlags,
|
||||||
|
pager: Option<Arc<dyn Pager>>,
|
||||||
|
) -> Result<Vmo_> {
|
||||||
|
let size = size.align_up(PAGE_SIZE);
|
||||||
|
let committed_pages = committed_pages_if_continuous(flags, size, paddr)?;
|
||||||
|
let vmo_inner = VmoInner {
|
||||||
|
pager,
|
||||||
|
size,
|
||||||
|
committed_pages,
|
||||||
|
inherited_pages: InheritedPages::new_empty(),
|
||||||
|
};
|
||||||
|
Ok(Vmo_ {
|
||||||
|
flags,
|
||||||
|
inner: Mutex::new(vmo_inner),
|
||||||
|
parent: Weak::new(),
|
||||||
|
paddr,
|
||||||
|
vmo_type: VmoType::NotChild,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn committed_pages_if_continuous(
|
||||||
|
flags: VmoFlags,
|
||||||
|
size: usize,
|
||||||
|
paddr: Option<Paddr>,
|
||||||
|
) -> Result<BTreeMap<usize, VmFrameVec>> {
|
||||||
|
if flags.contains(VmoFlags::CONTIGUOUS) {
|
||||||
|
// if the vmo is continuous, we need to allocate frames for the vmo
|
||||||
|
let frames_num = size / PAGE_SIZE;
|
||||||
|
let mut vm_alloc_option = VmAllocOptions::new(frames_num);
|
||||||
|
vm_alloc_option.is_contiguous(true);
|
||||||
|
vm_alloc_option.paddr(paddr);
|
||||||
|
let frames = VmFrameVec::allocate(&vm_alloc_option)?;
|
||||||
|
let mut committed_pages = BTreeMap::new();
|
||||||
|
for (idx, frame) in frames.into_iter().enumerate() {
|
||||||
|
committed_pages.insert(idx * PAGE_SIZE, VmFrameVec::from_one_frame(frame));
|
||||||
|
}
|
||||||
|
Ok(committed_pages)
|
||||||
|
} else {
|
||||||
|
// otherwise, we wait for the page is read or write
|
||||||
|
Ok(BTreeMap::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +259,7 @@ impl<R: TRights> VmoOptions<R> {
|
|||||||
/// Note that a slice VMO child and its parent cannot not be resizable.
|
/// Note that a slice VMO child and its parent cannot not be resizable.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use _std::vm::{PAGE_SIZE, VmoOptions};
|
/// use jinux_std::vm::{PAGE_SIZE, VmoOptions};
|
||||||
///
|
///
|
||||||
/// let parent_vmo = VmoOptions::new(PAGE_SIZE)
|
/// let parent_vmo = VmoOptions::new(PAGE_SIZE)
|
||||||
/// .alloc()
|
/// .alloc()
|
||||||
@ -284,14 +362,41 @@ impl<R, C> VmoChildOptions<R, C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> VmoChildOptions<Rights, C> {
|
impl VmoChildOptions<Rights, VmoSliceChild> {
|
||||||
/// Allocates the child VMO.
|
/// Allocates the child VMO.
|
||||||
///
|
///
|
||||||
/// # Access rights
|
/// # Access rights
|
||||||
///
|
///
|
||||||
/// The child VMO is initially assigned all the parent's access rights.
|
/// The child VMO is initially assigned all the parent's access rights.
|
||||||
pub fn alloc(mut self) -> Result<Vmo<Rights>> {
|
pub fn alloc(self) -> Result<Vmo<Rights>> {
|
||||||
todo!()
|
let VmoChildOptions {
|
||||||
|
parent,
|
||||||
|
range,
|
||||||
|
flags,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
let Vmo(parent_vmo_, parent_rights) = parent;
|
||||||
|
let child_vmo_ = alloc_child_vmo_(parent_vmo_, range, flags, ChildType::Slice)?;
|
||||||
|
Ok(Vmo(Arc::new(child_vmo_), parent_rights))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VmoChildOptions<Rights, VmoCowChild> {
|
||||||
|
/// Allocates the child VMO.
|
||||||
|
///
|
||||||
|
/// # Access rights
|
||||||
|
///
|
||||||
|
/// The child VMO is initially assigned all the parent's access rights.
|
||||||
|
pub fn alloc(self) -> Result<Vmo<Rights>> {
|
||||||
|
let VmoChildOptions {
|
||||||
|
parent,
|
||||||
|
range,
|
||||||
|
flags,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
let Vmo(parent_vmo_, parent_rights) = parent;
|
||||||
|
let child_vmo_ = alloc_child_vmo_(parent_vmo_, range, flags, ChildType::Cow)?;
|
||||||
|
Ok(Vmo(Arc::new(child_vmo_), parent_rights))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,8 +406,16 @@ impl<R: TRights> VmoChildOptions<R, VmoSliceChild> {
|
|||||||
/// # Access rights
|
/// # Access rights
|
||||||
///
|
///
|
||||||
/// The child VMO is initially assigned all the parent's access rights.
|
/// The child VMO is initially assigned all the parent's access rights.
|
||||||
pub fn alloc(mut self) -> Result<Vmo<R>> {
|
pub fn alloc(self) -> Result<Vmo<R>> {
|
||||||
todo!()
|
let VmoChildOptions {
|
||||||
|
parent,
|
||||||
|
range,
|
||||||
|
flags,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
let Vmo(parent_vmo_, parent_rights) = parent;
|
||||||
|
let child_vmo_ = alloc_child_vmo_(parent_vmo_, range, flags, ChildType::Slice)?;
|
||||||
|
Ok(Vmo(Arc::new(child_vmo_), parent_rights))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,28 +426,88 @@ impl<R: TRights> VmoChildOptions<R, VmoCowChild> {
|
|||||||
///
|
///
|
||||||
/// The child VMO is initially assigned all the parent's access rights
|
/// The child VMO is initially assigned all the parent's access rights
|
||||||
/// plus the Write right.
|
/// plus the Write right.
|
||||||
pub fn alloc<R1>(mut self) -> Result<Vmo<R1>>
|
pub fn alloc(self) -> Result<Vmo<SetExtendOp<R, Write>>>
|
||||||
where
|
where
|
||||||
R1: TRights, // TODO: R1 must contain the Write right. To do so at the type level,
|
R: SetExtend<Write>,
|
||||||
// we need to implement a type-level operator
|
SetExtendOp<R, Write>: TRights,
|
||||||
// (say, `TRightsExtend(L, F)`)
|
|
||||||
// that may extend a list (`L`) of type-level flags with an extra flag `F`.
|
|
||||||
// TRightsExtend<R, Write>
|
|
||||||
{
|
{
|
||||||
todo!()
|
let VmoChildOptions {
|
||||||
|
parent,
|
||||||
|
range,
|
||||||
|
flags,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
let Vmo(parent_vmo_, _) = parent;
|
||||||
|
let child_vmo_ = alloc_child_vmo_(parent_vmo_, range, flags, ChildType::Cow)?;
|
||||||
|
let right = SetExtendOp::<R, Write>::new();
|
||||||
|
Ok(Vmo(Arc::new(child_vmo_), right))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// original:
|
#[derive(Debug, Clone, Copy)]
|
||||||
// pub fn alloc<R1>(mut self) -> Result<Vmo<R1>>
|
enum ChildType {
|
||||||
// where
|
Cow,
|
||||||
// // TODO: R1 must contain the Write right. To do so at the type level,
|
Slice,
|
||||||
// // we need to implement a type-level operator
|
}
|
||||||
// // (say, `TRightsExtend(L, F)`)
|
|
||||||
// // that may extend a list (`L`) of type-level flags with an extra flag `F`.
|
fn alloc_child_vmo_(
|
||||||
// R1: R // TRightsExtend<R, Write>
|
parent_vmo_: Arc<Vmo_>,
|
||||||
// {
|
range: Range<usize>,
|
||||||
// todo!()
|
child_flags: VmoFlags,
|
||||||
// }
|
child_type: ChildType,
|
||||||
|
) -> Result<Vmo_> {
|
||||||
|
let child_vmo_start = range.start;
|
||||||
|
let child_vmo_end = range.end;
|
||||||
|
debug_assert!(child_vmo_start % PAGE_SIZE == 0);
|
||||||
|
debug_assert!(child_vmo_end % PAGE_SIZE == 0);
|
||||||
|
if child_vmo_start % PAGE_SIZE != 0 || child_vmo_end % PAGE_SIZE != 0 {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "vmo range does not aligned with PAGE_SIZE");
|
||||||
|
}
|
||||||
|
let parent_vmo_size = parent_vmo_.size();
|
||||||
|
let parent_vmo_inner = parent_vmo_.inner.lock();
|
||||||
|
match child_type {
|
||||||
|
ChildType::Slice => {
|
||||||
|
// A slice child should be inside parent vmo's range
|
||||||
|
debug_assert!(child_vmo_end <= parent_vmo_inner.size);
|
||||||
|
if child_vmo_end > parent_vmo_inner.size {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EINVAL,
|
||||||
|
"slice child vmo cannot exceed parent vmo's size"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ChildType::Cow => {
|
||||||
|
// A copy on Write child should intersect with parent vmo
|
||||||
|
debug_assert!(range.start < parent_vmo_inner.size);
|
||||||
|
if range.start >= parent_vmo_inner.size {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "COW vmo should overlap with its parent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let parent_page_idx_offset = range.start / PAGE_SIZE;
|
||||||
|
let inherited_end = range.end.min(parent_vmo_size);
|
||||||
|
let inherited_end_page_idx = inherited_end / PAGE_SIZE + 1;
|
||||||
|
let inherited_pages = InheritedPages::new(0..inherited_end_page_idx, parent_page_idx_offset);
|
||||||
|
let vmo_inner = VmoInner {
|
||||||
|
pager: None,
|
||||||
|
size: child_vmo_end - child_vmo_start,
|
||||||
|
committed_pages: BTreeMap::new(),
|
||||||
|
inherited_pages,
|
||||||
|
};
|
||||||
|
let child_paddr = parent_vmo_
|
||||||
|
.paddr()
|
||||||
|
.map(|parent_paddr| parent_paddr + child_vmo_start);
|
||||||
|
let vmo_type = match child_type {
|
||||||
|
ChildType::Cow => VmoType::CopyOnWriteChild,
|
||||||
|
ChildType::Slice => VmoType::SliceChild,
|
||||||
|
};
|
||||||
|
Ok(Vmo_ {
|
||||||
|
flags: child_flags,
|
||||||
|
inner: Mutex::new(vmo_inner),
|
||||||
|
parent: Arc::downgrade(&parent_vmo_),
|
||||||
|
paddr: child_paddr,
|
||||||
|
vmo_type,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type to specify the "type" of a child, which is either a slice or a COW.
|
/// A type to specify the "type" of a child, which is either a slice or a COW.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use jinux_frame::prelude::Result;
|
use crate::prelude::*;
|
||||||
use jinux_frame::vm::VmFrame;
|
use jinux_frame::vm::VmFrame;
|
||||||
|
|
||||||
/// Pagers provide frame to a VMO.
|
/// Pagers provide frame to a VMO.
|
||||||
@ -10,7 +10,7 @@ use jinux_frame::vm::VmFrame;
|
|||||||
/// notify the attached pager that the frame has been updated.
|
/// notify the attached pager that the frame has been updated.
|
||||||
/// Finally, when a frame is no longer needed (i.e., on decommits),
|
/// Finally, when a frame is no longer needed (i.e., on decommits),
|
||||||
/// the frame pager will also be notified.
|
/// the frame pager will also be notified.
|
||||||
pub trait Pager {
|
pub trait Pager: Send + Sync {
|
||||||
/// Ask the pager to provide a frame at a specified offset (in bytes).
|
/// Ask the pager to provide a frame at a specified offset (in bytes).
|
||||||
///
|
///
|
||||||
/// After a page of a VMO is committed, the VMO shall not call this method
|
/// After a page of a VMO is committed, the VMO shall not call this method
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
|
use jinux_frame::vm::VmIo;
|
||||||
use jinux_frame::prelude::Result;
|
|
||||||
use jinux_frame::{vm::VmIo, Error};
|
|
||||||
use jinux_rights_proc::require;
|
use jinux_rights_proc::require;
|
||||||
|
|
||||||
use crate::rights::*;
|
use crate::rights::*;
|
||||||
|
|
||||||
|
use super::VmoRightsOp;
|
||||||
use super::{
|
use super::{
|
||||||
options::{VmoCowChild, VmoSliceChild},
|
options::{VmoCowChild, VmoSliceChild},
|
||||||
Vmo, VmoChildOptions,
|
Vmo, VmoChildOptions,
|
||||||
@ -66,6 +66,12 @@ impl<R: TRights> Vmo<R> {
|
|||||||
Ok(VmoChildOptions::new_cow(dup_self, range))
|
Ok(VmoChildOptions::new_cow(dup_self, range))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// commit a page at specific offset
|
||||||
|
pub fn commit_page(&self, offset: usize) -> Result<()> {
|
||||||
|
self.check_rights(Rights::WRITE)?;
|
||||||
|
self.0.commit_page(offset)
|
||||||
|
}
|
||||||
|
|
||||||
/// Commit the pages specified in the range (in bytes).
|
/// Commit the pages specified in the range (in bytes).
|
||||||
///
|
///
|
||||||
/// The range must be within the size of the VMO.
|
/// The range must be within the size of the VMO.
|
||||||
@ -125,42 +131,38 @@ impl<R: TRights> Vmo<R> {
|
|||||||
/// The method requires the Dup right.
|
/// The method requires the Dup right.
|
||||||
#[require(R > Dup)]
|
#[require(R > Dup)]
|
||||||
pub fn dup(&self) -> Result<Self> {
|
pub fn dup(&self) -> Result<Self> {
|
||||||
todo!()
|
Ok(Vmo(self.0.clone(), self.1.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Strict the access rights.
|
/// Strict the access rights.
|
||||||
#[require(R > R1)]
|
#[require(R > R1)]
|
||||||
pub fn restrict<R1>(mut self) -> Vmo<R1> {
|
pub fn restrict<R1: TRights>(self) -> Vmo<R1> {
|
||||||
todo!()
|
Vmo(self.0, R1::new())
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts to a dynamic capability.
|
|
||||||
pub fn to_dyn(self) -> Vmo<Rights> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the access rights.
|
|
||||||
pub const fn rights(&self) -> Rights {
|
|
||||||
Rights::from_bits(R::BITS).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_rights(&self, rights: Rights) -> Result<()> {
|
|
||||||
if self.rights().contains(rights) {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Error::AccessDenied)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: TRights> VmIo for Vmo<R> {
|
impl<R: TRights> VmIo for Vmo<R> {
|
||||||
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
|
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> jinux_frame::Result<()> {
|
||||||
self.check_rights(Rights::READ)?;
|
self.check_rights(Rights::READ)?;
|
||||||
self.0.read_bytes(offset, buf)
|
self.0.read_bytes(offset, buf)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
|
fn write_bytes(&self, offset: usize, buf: &[u8]) -> jinux_frame::Result<()> {
|
||||||
self.check_rights(Rights::WRITE)?;
|
self.check_rights(Rights::WRITE)?;
|
||||||
self.0.write_bytes(offset, buf)
|
self.0.write_bytes(offset, buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TRights> VmoRightsOp for Vmo<R> {
|
||||||
|
fn rights(&self) -> Rights {
|
||||||
|
Rights::from_bits(R::BITS).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts to a dynamic capability.
|
||||||
|
fn to_dyn(self) -> Vmo<Rights> {
|
||||||
|
let rights = self.rights();
|
||||||
|
Vmo(self.0, rights)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
src/services/libs/typeflags-util/src/extend.rs
Normal file
15
src/services/libs/typeflags-util/src/extend.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use crate::{Cons, Nil};
|
||||||
|
|
||||||
|
/// This trait will extend a set with another item.
|
||||||
|
/// If the set already contains the item, it will return the original set.
|
||||||
|
/// Otherwise, it will return the new set with the new item.
|
||||||
|
/// The implementation should care about the item orders when extending set.
|
||||||
|
pub trait SetExtend<T> {
|
||||||
|
type Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SetExtendOp<Set, T> = <Set as SetExtend<T>>::Output;
|
||||||
|
|
||||||
|
impl<T> SetExtend<T> for Nil {
|
||||||
|
type Output = Cons<T, Nil>;
|
||||||
|
}
|
@ -5,11 +5,13 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
pub mod assert;
|
pub mod assert;
|
||||||
pub mod bool;
|
pub mod bool;
|
||||||
|
pub mod extend;
|
||||||
pub mod if_;
|
pub mod if_;
|
||||||
pub mod same;
|
pub mod same;
|
||||||
pub mod set;
|
pub mod set;
|
||||||
|
|
||||||
pub use crate::bool::{And, AndOp, False, IsFalse, IsTrue, Not, NotOp, Or, OrOp, True};
|
pub use crate::bool::{And, AndOp, False, IsFalse, IsTrue, Not, NotOp, Or, OrOp, True};
|
||||||
|
pub use crate::extend::{SetExtend, SetExtendOp};
|
||||||
pub use crate::same::{SameAs, SameAsOp};
|
pub use crate::same::{SameAs, SameAsOp};
|
||||||
pub use crate::set::{Cons, Nil, Set, SetContain, SetContainOp, SetInclude, SetIncludeOp};
|
pub use crate::set::{Cons, Nil, Set, SetContain, SetContainOp, SetInclude, SetIncludeOp};
|
||||||
pub use assert::AssertTypeSame;
|
pub use assert::AssertTypeSame;
|
||||||
|
@ -13,6 +13,7 @@ use core::ops::BitOr as Or;
|
|||||||
pub trait Set {}
|
pub trait Set {}
|
||||||
|
|
||||||
/// An non-empty type-level set.
|
/// An non-empty type-level set.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Cons<T, S: Set>(PhantomData<(T, S)>);
|
pub struct Cons<T, S: Set>(PhantomData<(T, S)>);
|
||||||
|
|
||||||
impl<T, S: Set> Cons<T, S> {
|
impl<T, S: Set> Cons<T, S> {
|
||||||
@ -22,6 +23,7 @@ impl<T, S: Set> Cons<T, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An empty type-level set.
|
/// An empty type-level set.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Nil;
|
pub struct Nil;
|
||||||
|
|
||||||
impl<T, S: Set> Set for Cons<T, S> {}
|
impl<T, S: Set> Set for Cons<T, S> {}
|
||||||
|
@ -11,6 +11,7 @@ const SET_NAME: &'static str = "::typeflags_util::Cons";
|
|||||||
/// A flagSet represent the combination of differnt flag item.
|
/// A flagSet represent the combination of differnt flag item.
|
||||||
/// e.g. [Read, Write], [Read], [] are all flag sets.
|
/// e.g. [Read, Write], [Read], [] are all flag sets.
|
||||||
/// The order of flagItem does not matters. So flag sets with same sets of items should be viewed as the same set.
|
/// The order of flagItem does not matters. So flag sets with same sets of items should be viewed as the same set.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FlagSet {
|
pub struct FlagSet {
|
||||||
items: Vec<FlagItem>,
|
items: Vec<FlagItem>,
|
||||||
}
|
}
|
||||||
@ -107,6 +108,23 @@ impl FlagSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains_type(&self, type_ident: &Ident) -> bool {
|
||||||
|
let type_name = type_ident.to_string();
|
||||||
|
self.items
|
||||||
|
.iter()
|
||||||
|
.position(|item| item.ident.to_string() == type_name)
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_set(&self, other_set: &FlagSet) -> bool {
|
||||||
|
for item in &other_set.items {
|
||||||
|
if !self.contains_type(&item.ident) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// The token stream inside macro definition. We will generate a token stream for each permutation of items
|
/// The token stream inside macro definition. We will generate a token stream for each permutation of items
|
||||||
/// since the user may use arbitrary order of items in macro.
|
/// since the user may use arbitrary order of items in macro.
|
||||||
pub fn macro_item_tokens(&self) -> Vec<TokenStream> {
|
pub fn macro_item_tokens(&self) -> Vec<TokenStream> {
|
||||||
@ -135,6 +153,14 @@ pub struct FlagItem {
|
|||||||
val: Expr,
|
val: Expr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for FlagItem {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("FlagItem")
|
||||||
|
.field("ident", &self.ident.to_string())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generate all possible flag sets
|
/// generate all possible flag sets
|
||||||
pub fn generate_flag_sets(type_flag_def: &TypeFlagDef) -> Vec<FlagSet> {
|
pub fn generate_flag_sets(type_flag_def: &TypeFlagDef) -> Vec<FlagSet> {
|
||||||
let flag_items = type_flag_def
|
let flag_items = type_flag_def
|
||||||
|
@ -43,6 +43,10 @@
|
|||||||
//! assert_type_same!(SetIncludeOp<R, W>, False);
|
//! assert_type_same!(SetIncludeOp<R, W>, False);
|
||||||
//! assert_type_same!(SetIncludeOp<W, O>, True);
|
//! assert_type_same!(SetIncludeOp<W, O>, True);
|
||||||
//! assert_type_same!(SetIncludeOp<O, R>, False);
|
//! assert_type_same!(SetIncludeOp<O, R>, False);
|
||||||
|
//! assert_type_same!(SetExtendOp<O, Read>, R);
|
||||||
|
//! assert_type_same!(SetExtendOp<R, Write>, RW);
|
||||||
|
//! assert_type_same!(SetExtendOp<R, Read>, R);
|
||||||
|
//! assert_type_same!(SetExtendOp<W, Read>, RW);
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![feature(proc_macro_diagnostic)]
|
#![feature(proc_macro_diagnostic)]
|
||||||
|
@ -77,7 +77,7 @@ impl TypeFlagDef {
|
|||||||
let type_ = self.type_.clone();
|
let type_ = self.type_.clone();
|
||||||
quote!(
|
quote!(
|
||||||
#(#attributes)*
|
#(#attributes)*
|
||||||
#vis trait #ident : Sync + Send{
|
#vis trait #ident : Sync + Send + Copy + Clone{
|
||||||
const BITS: #type_;
|
const BITS: #type_;
|
||||||
|
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
@ -128,6 +128,7 @@ impl TypeFlagItem {
|
|||||||
let ident = self.ident.clone();
|
let ident = self.ident.clone();
|
||||||
quote!(
|
quote!(
|
||||||
#(#attributes)*
|
#(#attributes)*
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
#vis struct #ident {}
|
#vis struct #ident {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use proc_macro2::TokenStream;
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::{quote, TokenStreamExt};
|
use quote::{quote, TokenStreamExt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -23,6 +23,9 @@ pub fn expand_type_flag(type_flags_def: &TypeFlagDef) -> TokenStream {
|
|||||||
all_tokens.append_all(impl_main_trait_tokens);
|
all_tokens.append_all(impl_main_trait_tokens);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let impl_set_entend_tokens = impl_set_extend(type_flags_def, &flag_sets);
|
||||||
|
all_tokens.append_all(impl_set_entend_tokens);
|
||||||
|
|
||||||
let export_declarive_macro_tokens = export_declarive_macro(type_flags_def, &flag_sets);
|
let export_declarive_macro_tokens = export_declarive_macro(type_flags_def, &flag_sets);
|
||||||
all_tokens.append_all(export_declarive_macro_tokens);
|
all_tokens.append_all(export_declarive_macro_tokens);
|
||||||
|
|
||||||
@ -73,6 +76,56 @@ pub fn impl_same_as(type_flags_def: &TypeFlagDef) -> TokenStream {
|
|||||||
all_tokens
|
all_tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn impl_set_extend(type_flags_def: &TypeFlagDef, flag_sets: &[FlagSet]) -> TokenStream {
|
||||||
|
let mut all_tokens = TokenStream::new();
|
||||||
|
let type_idents: Vec<_> = type_flags_def
|
||||||
|
.items_iter()
|
||||||
|
.map(|type_flag_item| type_flag_item.ident())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for flag_set in flag_sets {
|
||||||
|
// We don't need to impl set extend trait for Nil
|
||||||
|
if flag_set.len() == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for type_ident in &type_idents {
|
||||||
|
let type_ident = type_ident.clone();
|
||||||
|
let flag_set_tokens = flag_set.type_name_tokens();
|
||||||
|
if flag_set.contains_type(&type_ident) {
|
||||||
|
// the flagset contains the type
|
||||||
|
let impl_extend_tokens = quote!(
|
||||||
|
impl ::typeflags_util::SetExtend<#type_ident> for #flag_set_tokens {
|
||||||
|
type Output = #flag_set_tokens;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
all_tokens.append_all(impl_extend_tokens)
|
||||||
|
} else {
|
||||||
|
// the flagset does not contains the type
|
||||||
|
let output_set = extent_one_type(&type_ident, flag_set, flag_sets).unwrap();
|
||||||
|
let output_set_tokens = output_set.type_name_tokens();
|
||||||
|
let impl_extend_tokens = quote!(
|
||||||
|
impl ::typeflags_util::SetExtend<#type_ident> for #flag_set_tokens {
|
||||||
|
type Output = #output_set_tokens;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
all_tokens.append_all(impl_extend_tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
all_tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extent_one_type<'a>(
|
||||||
|
type_ident: &Ident,
|
||||||
|
flag_set: &'a FlagSet,
|
||||||
|
sets: &'a [FlagSet],
|
||||||
|
) -> Option<&'a FlagSet> {
|
||||||
|
sets.iter().find(|bigger_set| {
|
||||||
|
bigger_set.contains_type(type_ident) && bigger_set.contains_set(flag_set)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// export the declarive macro
|
/// export the declarive macro
|
||||||
pub fn export_declarive_macro(type_flags_def: &TypeFlagDef, flag_sets: &[FlagSet]) -> TokenStream {
|
pub fn export_declarive_macro(type_flags_def: &TypeFlagDef, flag_sets: &[FlagSet]) -> TokenStream {
|
||||||
let macro_ident = type_flags_def.trait_ident();
|
let macro_ident = type_flags_def.trait_ident();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user