Files
RedBear-OS/src/syscall/process.rs
T
2026-05-20 13:35:47 +07:00

297 lines
11 KiB
Rust

use alloc::sync::Arc;
use core::{mem, num::NonZeroUsize};
use rmm::Arch;
use syscall::data::GlobalSchemes;
use crate::{
context::{
context::SyscallFrame,
file::{FileDescription, FileDescriptor, InternalFlags},
memory::{AddrSpace, Grant, PageSpan},
ContextRef,
},
event,
sync::{CleanLockToken, RwLock},
syscall::flag::{EventFlags, O_CREAT, O_RDWR},
};
use crate::{
context::{self, context::FdTbl},
memory::{Page, VirtualAddress, PAGE_SIZE},
scheme::{
KernelScheme, SchemeExt, SchemeId, SchemeList, ALL_KERNEL_SCHEMES, KERNEL_SCHEMES_COUNT,
},
startup::Bootstrap,
syscall::{error::*, flag::MapFlags},
CurrentRmmArch,
};
use super::usercopy::UserSliceWo;
pub fn exit_this_context(excp: Option<syscall::Exception>, token: &mut CleanLockToken) -> ! {
let context_lock = context::current();
let (addrspace_opt, mut close_files) = {
let mut context = context_lock.write(token.token());
let (context, mut token) = context.token_split();
let close_files = Arc::try_unwrap(mem::take(&mut context.files))
.map_or_else(|_| FdTbl::new(), RwLock::into_inner);
let addrspace_opt = context.set_addr_space(None, token);
drop(mem::replace(&mut context.syscall_head, SyscallFrame::Dummy));
drop(mem::replace(&mut context.syscall_tail, SyscallFrame::Dummy));
(addrspace_opt, close_files)
};
// Files must be closed while context is valid so that messages can be passed
close_files.force_close_all(token);
if let Some(addrspace) = addrspace_opt {
// TODO: addrspace utable should be dropped immediately but it's not the case.
// the utable leaves us with 8 memory pages (32K) leak per context
if let Ok(addrspace) = Arc::try_unwrap(addrspace) {
addrspace.into_drop(token);
}
}
// TODO: Should status == Status::HardBlocked be handled differently?
let owner = {
let mut guard = context_lock.write(token.token());
guard.status = context::Status::Dead { excp };
// TODO: context should be dropped immediately but it's not the case.
// we drop kstack to prevent 32 memory pages (128K) leaking
drop(guard.kstack.take());
guard.owner_proc_id
};
if let Some(owner) = owner {
event::trigger(
GlobalSchemes::Proc.scheme_id(),
owner.get(),
EventFlags::EVENT_READ,
token,
);
}
{
if !context::contexts_mut(token.downgrade()).remove(&ContextRef(context_lock)) {
#[cfg(feature = "drop_panic")]
{
panic!("This context is not in the cpu")
}
}
}
context::switch(token);
unreachable!();
}
pub fn mprotect(
address: usize,
size: usize,
flags: MapFlags,
token: &mut CleanLockToken,
) -> Result<()> {
// println!("mprotect {:#X}, {}, {:#X}", address, size, flags);
let span = PageSpan::validate_nonempty(VirtualAddress::new(address), size)
.ok_or(Error::new(EINVAL))?;
AddrSpace::current()?.mprotect(span, flags, token)
}
const KERNEL_METADATA_BASE: usize = crate::USER_END_OFFSET - syscall::KERNEL_METADATA_SIZE;
const KERNEL_METADATA_PAGE_COUNT: usize = syscall::KERNEL_METADATA_SIZE / PAGE_SIZE + {
if syscall::KERNEL_METADATA_SIZE.is_multiple_of(PAGE_SIZE) {
0
} else {
1
}
};
pub unsafe fn usermode_bootstrap(bootstrap: &Bootstrap, token: &mut CleanLockToken) {
assert_ne!(bootstrap.page_count, 0);
{
let addr_space = Arc::clone(
context::current()
.read(token.token())
.addr_space()
.expect("expected bootstrap context to have an address space"),
);
let base = Page::containing_address(VirtualAddress::new(PAGE_SIZE));
let flags = MapFlags::MAP_FIXED_NOREPLACE
| MapFlags::PROT_EXEC
| MapFlags::PROT_READ
| MapFlags::PROT_WRITE;
let page_count =
NonZeroUsize::new(bootstrap.page_count).expect("bootstrap contained no pages!");
let _base_page = {
let mut lock_token = token.token();
let mut addr_space_lock = addr_space.acquire_write(lock_token.downgrade());
addr_space_lock
.mmap(
&addr_space,
Some(base),
page_count,
flags,
None,
|page, flags, mapper, flusher| {
let shared = false;
Ok(Grant::zeroed(
PageSpan::new(page, bootstrap.page_count),
flags,
mapper,
flusher,
shared,
)?)
},
)
.expect("Failed to allocate bootstrap pages")
};
// Insert kernel schemes root capabilities.
let mut kernel_schemes_infos =
[syscall::data::KernelSchemeInfo::default(); KERNEL_SCHEMES_COUNT];
assert_eq!(kernel_schemes_infos.len(), ALL_KERNEL_SCHEMES.len());
for (i, scheme) in ALL_KERNEL_SCHEMES.iter().enumerate() {
if let Some(inner) = kernel_schemes_infos.get_mut(i) {
inner.scheme_id = scheme.scheme_id().get() as u8;
inner.fd = {
let cap_fd = match scheme.as_scheme().scheme_root(token) {
Ok(fd) => fd,
Err(_) => usize::MAX,
};
insert_fd(
scheme.scheme_id(),
cap_fd,
matches!(scheme, GlobalSchemes::Proc),
token,
)
};
}
}
// Insert a scheme creation capability for the usermode bootstrap.
let scheme_creation_cap = {
// First, get the scheme root to initialize the schemelist.
let cap_fd = match &SchemeList.scheme_root(token) {
Ok(fd) => *fd,
Err(_) => usize::MAX,
};
// Second, retrieve the scheme ID.
let scheme_id = &SchemeList.id();
insert_fd(*scheme_id, cap_fd, false, token)
};
let mut lock_token = token.token();
let kernel_schemes_info_page = addr_space
.acquire_write(lock_token.downgrade())
.mmap(
&addr_space,
Some(Page::containing_address(VirtualAddress::new(
KERNEL_METADATA_BASE,
))),
NonZeroUsize::new(KERNEL_METADATA_PAGE_COUNT).unwrap(),
MapFlags::MAP_FIXED_NOREPLACE | MapFlags::PROT_READ | MapFlags::PROT_WRITE,
None,
|page, flags, mapper, flusher| {
let shared = false;
Ok(Grant::zeroed(
PageSpan::new(page, KERNEL_METADATA_PAGE_COUNT),
flags,
mapper,
flusher,
shared,
)?)
},
)
.expect("Failed to allocate kernel scheme info page");
let mut cursor = kernel_schemes_info_page.start_address().data();
const HEADER_SIZE: usize = size_of::<usize>();
UserSliceWo::new(cursor, HEADER_SIZE)
.expect("failed to create kernel schemes header user slice")
.copy_common_bytes_from_slice(&KERNEL_SCHEMES_COUNT.to_ne_bytes())
.expect("failed to copy kernel schemes count");
cursor += HEADER_SIZE;
let info_bytes = unsafe {
core::slice::from_raw_parts(
kernel_schemes_infos.as_ptr() as *const u8,
KERNEL_SCHEMES_COUNT * size_of::<syscall::data::KernelSchemeInfo>(),
)
};
UserSliceWo::new(
cursor,
KERNEL_SCHEMES_COUNT * size_of::<syscall::data::KernelSchemeInfo>(),
)
.expect("failed to create kernel schemes info user slice")
.copy_common_bytes_from_slice(info_bytes)
.expect("failed to copy kernel schemes info");
cursor += KERNEL_SCHEMES_COUNT * size_of::<syscall::data::KernelSchemeInfo>();
UserSliceWo::new(cursor, size_of::<usize>())
.expect("failed to create scheme creation cap user slice")
.copy_common_bytes_from_slice(&scheme_creation_cap.to_ne_bytes())
.expect("failed to copy scheme creation cap");
mprotect(
KERNEL_METADATA_BASE,
KERNEL_METADATA_PAGE_COUNT * PAGE_SIZE,
MapFlags::PROT_READ,
token,
)
.expect("failed to mprotect kernel schemes info page");
}
let bootstrap_slice = unsafe { bootstrap_mem(bootstrap) };
UserSliceWo::new(PAGE_SIZE, bootstrap.page_count * PAGE_SIZE)
.expect("failed to create bootstrap user slice")
.copy_from_slice(bootstrap_slice)
.expect("failed to copy memory to bootstrap");
let bootstrap_entry = u64::from_le_bytes(bootstrap_slice[0x1a..0x22].try_into().unwrap());
debug!("Bootstrap entry point: {:X}", bootstrap_entry);
assert_ne!(bootstrap_entry, 0);
// Start in a minimal environment without any stack.
let ctx = context::current();
let mut lock = ctx.write(token.token());
let regs = &mut lock
.regs_mut()
.expect("bootstrap needs registers to be available");
{
regs.init();
regs.set_instr_pointer(bootstrap_entry.try_into().unwrap());
}
}
unsafe fn bootstrap_mem(bootstrap: &crate::startup::Bootstrap) -> &'static [u8] {
unsafe {
core::slice::from_raw_parts(
CurrentRmmArch::phys_to_virt(bootstrap.base.base()).data() as *const u8,
bootstrap.page_count * PAGE_SIZE,
)
}
}
fn insert_fd(scheme: SchemeId, number: usize, cloexec: bool, token: &mut CleanLockToken) -> usize {
let current_lock = context::current();
let mut current = current_lock.read(token.token());
let (context, mut token) = current.token_split();
context
.add_file_min(
FileDescriptor {
description: Arc::new(RwLock::new(FileDescription {
scheme,
number,
offset: 0,
flags: (O_CREAT | O_RDWR) as u32,
internal_flags: InternalFlags::empty(),
})),
cloexec,
},
syscall::flag::UPPER_FDTBL_TAG + scheme.get(),
&mut token,
)
.expect("failed to insert fd to current context")
.get()
}