WIP: Remove SYS_CLONE (to be done in userspace).
This commit is contained in:
+2
-1
@@ -5,6 +5,7 @@ use alloc::collections::BTreeMap;
|
||||
use core::{slice, str};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
use rmm::Flusher;
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::*;
|
||||
@@ -55,7 +56,7 @@ impl DiskScheme {
|
||||
let virt = phys + crate::PHYS_OFFSET;
|
||||
unsafe {
|
||||
let mut active_table = ActivePageTable::new(TableKind::Kernel);
|
||||
let flush_all = PageFlushAll::new();
|
||||
let mut flush_all = PageFlushAll::new();
|
||||
let start_page = Page::containing_address(VirtualAddress::new(virt));
|
||||
let end_page = Page::containing_address(VirtualAddress::new(virt + size - 1));
|
||||
for page in Page::range_inclusive(start_page, end_page) {
|
||||
|
||||
+4
-18
@@ -1,7 +1,7 @@
|
||||
use crate::context;
|
||||
use crate::context::memory::{page_flags, Grant};
|
||||
use crate::memory::{free_frames, used_frames, PAGE_SIZE};
|
||||
use crate::paging::{ActivePageTable, VirtualAddress};
|
||||
use crate::paging::{ActivePageTable, mapper::PageFlushAll, Page, VirtualAddress};
|
||||
use crate::syscall::data::{Map, OldMap, StatVfs};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::MapFlags;
|
||||
@@ -23,25 +23,11 @@ impl MemoryScheme {
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
|
||||
let mut grants = context.grants.write();
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
|
||||
let region = grants.find_free_at(VirtualAddress::new(map.address), map.size, map.flags)?.round();
|
||||
let region = addr_space.grants.find_free_at(VirtualAddress::new(map.address), map.size, map.flags)?.round();
|
||||
|
||||
{
|
||||
// Make sure it's *absolutely* not mapped already
|
||||
// TODO: Keep track of all allocated memory so this isn't necessary
|
||||
|
||||
let active_table = unsafe { ActivePageTable::new(rmm::TableKind::User) };
|
||||
|
||||
for page in region.pages() {
|
||||
if let Some(flags) = active_table.translate_page_flags(page).filter(|flags| flags.has_present()) {
|
||||
println!("page at {:#x} was already mapped, flags: {:?}", page.start_address().data(), flags);
|
||||
return Err(Error::new(EEXIST))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grants.insert(Grant::map(region.start_address(), region.size(), page_flags(map.flags)));
|
||||
addr_space.grants.insert(Grant::zeroed(Page::containing_address(region.start_address()), map.size / PAGE_SIZE, page_flags(map.flags), &mut *unsafe { ActivePageTable::new(rmm::TableKind::User) }, PageFlushAll::new())?);
|
||||
|
||||
Ok(region.start_address().data())
|
||||
}
|
||||
|
||||
+1
-1
@@ -301,7 +301,7 @@ pub fn schemes_mut() -> RwLockWriteGuard<'static, SchemeList> {
|
||||
}
|
||||
|
||||
pub trait KernelScheme: Scheme + Send + Sync + 'static {
|
||||
#[allow(unused_arguments)]
|
||||
#[allow(unused_variables)]
|
||||
fn kfmap(&self, number: usize, map: &syscall::data::Map, target_context: &Arc<RwLock<Context>>) -> Result<usize> {
|
||||
log::error!("Returning ENOSYS since kfmap can only be called on UserScheme schemes");
|
||||
Err(Error::new(ENOSYS))
|
||||
|
||||
+102
-60
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
arch::paging::{ActivePageTable, InactivePageTable, mapper::{Mapper, PageFlushAll}, Page, VirtualAddress},
|
||||
context::{self, Context, ContextId, Status, memory::{Grant, page_flags, Region}},
|
||||
arch::paging::{ActivePageTable, Flusher, InactivePageTable, mapper::{InactiveFlusher, Mapper, PageFlushAll}, Page, RmmA, VirtualAddress},
|
||||
context::{self, Context, ContextId, Status, memory::{addrspace, Grant, new_addrspace, PtId, page_flags, Region}},
|
||||
memory::PAGE_SIZE,
|
||||
ptrace,
|
||||
scheme::{AtomicSchemeId, SchemeId},
|
||||
@@ -32,7 +32,7 @@ use core::{
|
||||
str,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
use spin::RwLock;
|
||||
use spin::{Once, RwLock};
|
||||
|
||||
fn read_from(dst: &mut [u8], src: &[u8], offset: &mut usize) -> Result<usize> {
|
||||
let byte_count = cmp::min(dst.len(), src.len().saturating_sub(*offset));
|
||||
@@ -68,7 +68,7 @@ where
|
||||
}
|
||||
fn try_stop_context<F, T>(pid: ContextId, mut callback: F) -> Result<T>
|
||||
where
|
||||
F: FnMut(&mut Context) -> Result<T>,
|
||||
F: FnOnce(&mut Context) -> Result<T>,
|
||||
{
|
||||
if pid == context::context_id() {
|
||||
return Err(Error::new(EBADF));
|
||||
@@ -118,6 +118,8 @@ enum Operation {
|
||||
Sigstack,
|
||||
Attr(Attr),
|
||||
Files,
|
||||
AddrSpace { id: PtId },
|
||||
CurrentAddrSpace,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Attr {
|
||||
@@ -216,7 +218,7 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
|
||||
pub static PROC_SCHEME_ID: AtomicSchemeId = AtomicSchemeId::default();
|
||||
pub static PROC_SCHEME_ID: Once<SchemeId> = Once::new();
|
||||
|
||||
pub struct ProcScheme {
|
||||
next_id: AtomicUsize,
|
||||
@@ -231,7 +233,7 @@ pub enum Access {
|
||||
|
||||
impl ProcScheme {
|
||||
pub fn new(scheme_id: SchemeId) -> Self {
|
||||
PROC_SCHEME_ID.store(scheme_id, Ordering::SeqCst);
|
||||
PROC_SCHEME_ID.call_once(|| scheme_id);
|
||||
|
||||
Self {
|
||||
next_id: AtomicUsize::new(0),
|
||||
@@ -246,6 +248,11 @@ impl ProcScheme {
|
||||
access: Access::Restricted,
|
||||
}
|
||||
}
|
||||
fn new_handle(&self, handle: Handle) -> Result<usize> {
|
||||
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
|
||||
let _ = self.handles.write().insert(id, handle);
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for ProcScheme {
|
||||
@@ -264,7 +271,8 @@ impl Scheme for ProcScheme {
|
||||
|
||||
let operation = match parts.next() {
|
||||
Some("mem") => Operation::Memory,
|
||||
Some("grants") => Operation::Grants,
|
||||
Some("addrspace") => Operation::AddrSpace { id: context::contexts().current().ok_or(Error::new(ESRCH))?.read().addr_space()?.read().id },
|
||||
Some("current-addrspace") => Operation::CurrentAddrSpace,
|
||||
Some("regs/float") => Operation::Regs(RegsKind::Float),
|
||||
Some("regs/int") => Operation::Regs(RegsKind::Int),
|
||||
Some("regs/env") => Operation::Regs(RegsKind::Env),
|
||||
@@ -340,9 +348,16 @@ impl Scheme for ProcScheme {
|
||||
}
|
||||
};
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
let id = self.new_handle(Handle {
|
||||
info: Info {
|
||||
flags,
|
||||
pid,
|
||||
operation,
|
||||
},
|
||||
data,
|
||||
})?;
|
||||
|
||||
if let Operation::Trace { .. } = operation {
|
||||
if let Operation::Trace = operation {
|
||||
if !ptrace::try_new_session(pid, id) {
|
||||
// There is no good way to handle id being occupied for nothing
|
||||
// here, is there?
|
||||
@@ -355,44 +370,41 @@ impl Scheme for ProcScheme {
|
||||
}
|
||||
}
|
||||
|
||||
self.handles.write().insert(id, Handle {
|
||||
info: Info {
|
||||
flags,
|
||||
pid,
|
||||
operation,
|
||||
},
|
||||
data,
|
||||
});
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Using dup for `proc:` simply opens another operation on the same PID
|
||||
/// ```rust,ignore
|
||||
/// let trace = syscall::open("proc:1234/trace")?;
|
||||
///
|
||||
/// // let regs = syscall::open("proc:1234/regs/int")?;
|
||||
/// let regs = syscall::dup(trace, "regs/int")?;
|
||||
/// ```
|
||||
/// Dup is currently used to implement clone() and execve().
|
||||
fn dup(&self, old_id: usize, buf: &[u8]) -> Result<usize> {
|
||||
let info = {
|
||||
let handles = self.handles.read();
|
||||
let handle = handles.get(&old_id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
handle.info
|
||||
};
|
||||
|
||||
let buf_str = str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?;
|
||||
self.new_handle(match info.operation {
|
||||
Operation::AddrSpace { id } => {
|
||||
let new_ptid = match buf {
|
||||
// TODO: Better way to obtain new empty address spaces, perhaps using SYS_OPEN. But
|
||||
// in that case, what scheme?
|
||||
b"empty" => new_addrspace()?.0,
|
||||
// Reuse same ID.
|
||||
b"shared" => id,
|
||||
b"exclusive" => addrspace(id).ok_or(Error::new(EBADFD))?.read().try_clone()?.0,
|
||||
|
||||
let mut path = format!("{}/", info.pid.into());
|
||||
path.push_str(buf_str);
|
||||
|
||||
let (uid, gid) = {
|
||||
let contexts = context::contexts();
|
||||
let context = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context.read();
|
||||
(context.euid, context.egid)
|
||||
};
|
||||
|
||||
self.open(&path, info.flags, uid, gid)
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
Handle {
|
||||
info: Info {
|
||||
flags: 0,
|
||||
pid: info.pid,
|
||||
operation: Operation::AddrSpace { id: new_ptid },
|
||||
},
|
||||
data: OperationData::Other,
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
})
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: isize, whence: usize) -> Result<isize> {
|
||||
@@ -421,6 +433,7 @@ impl Scheme for ProcScheme {
|
||||
};
|
||||
|
||||
match info.operation {
|
||||
Operation::Grants => return Err(Error::new(ENOSYS)),
|
||||
Operation::Static(_) => {
|
||||
let mut handles = self.handles.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
@@ -455,8 +468,7 @@ impl Scheme for ProcScheme {
|
||||
data.offset = VirtualAddress::new(data.offset.data() + bytes_read);
|
||||
Ok(bytes_read)
|
||||
},
|
||||
// TODO: Allow reading process mappings?
|
||||
Operation::Grants => return Err(Error::new(EBADF)),
|
||||
Operation::AddrSpace { .. } => return Err(Error::new(EBADF)),
|
||||
|
||||
Operation::Regs(kind) => {
|
||||
union Output {
|
||||
@@ -586,6 +598,14 @@ impl Scheme for ProcScheme {
|
||||
|
||||
read_from(buf, &data.buf, &mut data.offset)
|
||||
}
|
||||
// TODO: Replace write() with SYS_DUP_FORWARD.
|
||||
// TODO: Find a better way to switch address spaces, since they also require switching
|
||||
// the instruction and stack pointer. Maybe remove `<pid>/regs` altogether and replace it
|
||||
// with `<pid>/ctx`
|
||||
Operation::CurrentAddrSpace => {
|
||||
//read_from(buf, &usize::to_ne_bytes(id.into()), &mut 0)
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -606,6 +626,7 @@ impl Scheme for ProcScheme {
|
||||
};
|
||||
|
||||
match info.operation {
|
||||
Operation::Grants => Err(Error::new(ENOSYS)),
|
||||
Operation::Static(_) => Err(Error::new(EBADF)),
|
||||
Operation::Memory => {
|
||||
// Won't context switch, don't worry about the locks
|
||||
@@ -631,7 +652,7 @@ impl Scheme for ProcScheme {
|
||||
data.offset = VirtualAddress::new(data.offset.data() + bytes_written);
|
||||
Ok(bytes_written)
|
||||
},
|
||||
Operation::Grants => {
|
||||
Operation::AddrSpace { .. } => {
|
||||
// FIXME: Forbid upgrading external mappings.
|
||||
|
||||
let pid = self.handles.read()
|
||||
@@ -649,51 +670,52 @@ impl Scheme for ProcScheme {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
|
||||
let is_inactive = pid != context::context_id();
|
||||
let is_active = pid == context::context_id();
|
||||
|
||||
let callback = |context: &mut Context| {
|
||||
let mut inactive = is_inactive.then(|| unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) });
|
||||
let (mut inactive, mut active);
|
||||
|
||||
let mut grants = context.grants.write();
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
|
||||
let conflicting = grants.conflicts(region).map(|g| *g.region()).collect::<Vec<_>>();
|
||||
let (mut mapper, mut flusher) = if is_active {
|
||||
active = (unsafe { ActivePageTable::new(rmm::TableKind::User) }, PageFlushAll::new());
|
||||
(active.0.mapper(), &mut active.1 as &mut dyn Flusher<RmmA>)
|
||||
} else {
|
||||
inactive = (unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) }, InactiveFlusher::new());
|
||||
(inactive.0.mapper(), &mut inactive.1 as &mut dyn Flusher<RmmA>)
|
||||
};
|
||||
|
||||
let conflicting = addr_space.grants.conflicts(region).map(|g| *g.region()).collect::<Vec<_>>();
|
||||
for conflicting_region in conflicting {
|
||||
let whole_grant = grants.take(&conflicting_region).ok_or(Error::new(EBADFD))?;
|
||||
let whole_grant = addr_space.grants.take(&conflicting_region).ok_or(Error::new(EBADFD))?;
|
||||
let (before_opt, current, after_opt) = whole_grant.extract(region.intersect(conflicting_region)).ok_or(Error::new(EBADFD))?;
|
||||
|
||||
if let Some(before) = before_opt {
|
||||
grants.insert(before);
|
||||
addr_space.grants.insert(before);
|
||||
}
|
||||
if let Some(after) = after_opt {
|
||||
grants.insert(after);
|
||||
addr_space.grants.insert(after);
|
||||
}
|
||||
|
||||
let res = if let Some(ref mut inactive) = inactive {
|
||||
current.unmap_inactive(inactive)
|
||||
} else {
|
||||
current.unmap()
|
||||
};
|
||||
let res = current.unmap(&mut mapper, &mut flusher);
|
||||
|
||||
if res.file_desc.is_some() {
|
||||
drop(grants);
|
||||
return Err(Error::new(EBUSY));
|
||||
}
|
||||
|
||||
// TODO: Partial free if grant is mapped externally.
|
||||
// TODO: Partial free if grant is mapped externally, or fail and force
|
||||
// userspace to do it.
|
||||
}
|
||||
|
||||
if flags.intersects(MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::PROT_WRITE) {
|
||||
let base = VirtualAddress::new(base);
|
||||
let base = Page::containing_address(VirtualAddress::new(base));
|
||||
|
||||
if let Some(ref mut inactive) = inactive {
|
||||
grants.insert(Grant::zeroed_inactive(Page::containing_address(base), size / PAGE_SIZE, page_flags(flags), inactive).unwrap());
|
||||
} else {
|
||||
grants.insert(Grant::map(base, size, page_flags(flags)));
|
||||
}
|
||||
addr_space.grants.insert(Grant::zeroed(base, size / PAGE_SIZE, page_flags(flags), &mut mapper, flusher)?);
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if is_inactive {
|
||||
if is_active {
|
||||
with_context_mut(pid, callback)?;
|
||||
} else {
|
||||
try_stop_context(pid, callback)?;
|
||||
@@ -868,6 +890,24 @@ impl Scheme for ProcScheme {
|
||||
Ok(buf.len())
|
||||
}
|
||||
Operation::Files => return Err(Error::new(EBADF)),
|
||||
Operation::CurrentAddrSpace { .. } => {
|
||||
let mut iter = buf.array_chunks::<{mem::size_of::<usize>()}>().copied().map(usize::from_ne_bytes);
|
||||
let id = iter.next().ok_or(Error::new(EINVAL))?;
|
||||
let sp = iter.next().ok_or(Error::new(EINVAL))?;
|
||||
let ip = iter.next().ok_or(Error::new(EINVAL))?;
|
||||
|
||||
let space = addrspace(PtId::from(id)).ok_or(Error::new(EINVAL))?;
|
||||
|
||||
try_stop_context(info.pid, |context| unsafe {
|
||||
let regs = &mut ptrace::regs_for_mut(context).ok_or(Error::new(ESRCH))?.iret;
|
||||
regs.rip = ip;
|
||||
regs.rsp = sp;
|
||||
|
||||
context.set_addr_space(space);
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(3 * mem::size_of::<usize>())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,6 +951,8 @@ impl Scheme for ProcScheme {
|
||||
Operation::Attr(Attr::Uid) => "uid",
|
||||
Operation::Attr(Attr::Gid) => "gid",
|
||||
Operation::Files => "files",
|
||||
Operation::AddrSpace { .. } => "addrspace",
|
||||
Operation::CurrentAddrSpace => "current-addrspace",
|
||||
});
|
||||
|
||||
read_from(buf, &path.as_bytes(), &mut 0)
|
||||
|
||||
@@ -28,11 +28,15 @@ pub fn resource() -> Result<Vec<u8>> {
|
||||
let mut stat_string = String::new();
|
||||
// TODO: All user programs must have some grant in order for executable memory to even
|
||||
// exist, but is this a good indicator of whether it is user or kernel?
|
||||
if context.grants.read().is_empty() {
|
||||
stat_string.push('K');
|
||||
stat_string.push(if let Ok(addr_space) = context.addr_space() {
|
||||
if addr_space.read().grants.is_empty() {
|
||||
'K'
|
||||
} else {
|
||||
'U'
|
||||
}
|
||||
} else {
|
||||
stat_string.push('U');
|
||||
}
|
||||
'R'
|
||||
});
|
||||
match context.status {
|
||||
context::Status::Runnable => {
|
||||
stat_string.push('R');
|
||||
@@ -79,9 +83,11 @@ pub fn resource() -> Result<Vec<u8>> {
|
||||
if let Some(ref kstack) = context.kstack {
|
||||
memory += kstack.len();
|
||||
}
|
||||
for grant in context.grants.read().iter() {
|
||||
if grant.is_owned() {
|
||||
memory += grant.size();
|
||||
if let Ok(addr_space) = context.addr_space() {
|
||||
for grant in addr_space.read().grants.iter() {
|
||||
if grant.is_owned() {
|
||||
memory += grant.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-12
@@ -13,7 +13,7 @@ use crate::event;
|
||||
use crate::paging::{PAGE_SIZE, InactivePageTable, VirtualAddress};
|
||||
use crate::scheme::{AtomicSchemeId, SchemeId};
|
||||
use crate::sync::{WaitQueue, WaitMap};
|
||||
use crate::syscall::data::{Map, OldMap, Packet, Stat, StatVfs, TimeSpec};
|
||||
use crate::syscall::data::{Map, Packet, Stat, StatVfs, TimeSpec};
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{EventFlags, EVENT_READ, O_NONBLOCK, MapFlags, PROT_READ, PROT_WRITE};
|
||||
use crate::syscall::number::*;
|
||||
@@ -145,15 +145,15 @@ impl UserInner {
|
||||
|
||||
let mut new_table = unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) };
|
||||
|
||||
let mut grants = context.grants.write();
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
|
||||
let src_address = round_down_pages(address);
|
||||
let offset = address - src_address;
|
||||
let src_region = Region::new(VirtualAddress::new(src_address), offset + size).round();
|
||||
let dst_region = grants.find_free_at(VirtualAddress::new(dst_address), src_region.size(), flags)?;
|
||||
let dst_region = addr_space.grants.find_free_at(VirtualAddress::new(dst_address), src_region.size(), flags)?;
|
||||
|
||||
//TODO: Use syscall_head and syscall_tail to avoid leaking data
|
||||
grants.insert(Grant::map_inactive(
|
||||
addr_space.grants.insert(Grant::map_inactive(
|
||||
src_region.start_address(),
|
||||
dst_region.start_address(),
|
||||
src_region.size(),
|
||||
@@ -166,7 +166,6 @@ impl UserInner {
|
||||
}
|
||||
|
||||
pub fn release(&self, address: usize) -> Result<()> {
|
||||
//dbg!(address);
|
||||
if address == DANGLING {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -174,13 +173,13 @@ impl UserInner {
|
||||
let mut context = context_lock.write();
|
||||
|
||||
let mut other_table = unsafe { InactivePageTable::from_address(context.arch.get_page_utable()) };
|
||||
let mut grants = context.grants.write();
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
|
||||
let region = match grants.contains(VirtualAddress::new(address)).map(Region::from) {
|
||||
let region = match addr_space.grants.contains(VirtualAddress::new(address)).map(Region::from) {
|
||||
Some(region) => region,
|
||||
None => return Err(Error::new(EFAULT)),
|
||||
};
|
||||
grants.take(®ion).unwrap().unmap_inactive(&mut other_table);
|
||||
addr_space.grants.take(®ion).unwrap().unmap(&mut other_table.mapper(), crate::paging::mapper::InactiveFlusher::new());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -242,8 +241,8 @@ impl UserInner {
|
||||
if let Ok(grant_address) = res {
|
||||
if let Some(context_lock) = context_weak.upgrade() {
|
||||
let context = context_lock.read();
|
||||
let mut grants = context.grants.write();
|
||||
grants.funmap.insert(
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
addr_space.grants.funmap.insert(
|
||||
Region::new(grant_address, map.size),
|
||||
VirtualAddress::new(address)
|
||||
);
|
||||
@@ -437,8 +436,8 @@ impl Scheme for UserScheme {
|
||||
let contexts = context::contexts();
|
||||
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
|
||||
let context = context_lock.read();
|
||||
let mut grants = context.grants.write();
|
||||
let funmap = &mut grants.funmap;
|
||||
let mut addr_space = context.addr_space()?.write();
|
||||
let funmap = &mut addr_space.grants.funmap;
|
||||
let entry = funmap.range(..=Region::byte(VirtualAddress::new(grant_address))).next_back();
|
||||
|
||||
let grant_address = VirtualAddress::new(grant_address);
|
||||
|
||||
Reference in New Issue
Block a user