proc: reimplement argument passing

This commit is contained in:
Mark Poliakov 2023-11-16 16:12:32 +02:00
parent aae4874f35
commit de63a0456b
3 changed files with 64 additions and 86 deletions

View File

@ -16,7 +16,8 @@
rustc_private,
allocator_api,
trait_alias,
strict_provenance
strict_provenance,
slice_ptr_get
)]
#![allow(
clippy::new_without_default,

View File

@ -1,7 +1,11 @@
//! Binary execution functions
use core::mem::size_of;
use core::alloc::Layout;
use abi::error::Error;
use abi::{
error::Error,
pass::{Place, Placer},
process::ProgramArgumentInner,
};
use alloc::{string::String, sync::Arc};
use vfs::FileRef;
@ -14,105 +18,79 @@ use crate::{
task::{context::TaskContextImpl, process::Process, TaskContext},
};
pub struct EnvWriter<'a> {
data: &'a mut [u8],
pub struct BufferPlacer<'a> {
buffer: &'a mut [u8],
virtual_offset: usize,
offset: usize,
}
impl<'a> EnvWriter<'a> {
pub fn new(data: &'a mut [u8]) -> Self {
Self { data, offset: 0 }
}
pub fn write_usize(&mut self, value: usize) -> Result<(), Error> {
self.write_bytes(&value.to_ne_bytes())
}
pub fn write_bytes(&mut self, value: &[u8]) -> Result<(), Error> {
if value.len() + self.offset > self.data.len() {
return Err(Error::InvalidArgument);
impl<'a> BufferPlacer<'a> {
pub fn new(virtual_offset: usize, buffer: &'a mut [u8]) -> Self {
Self {
buffer,
virtual_offset,
offset: 0,
}
}
unsafe fn alloc_layout(&mut self, layout: Layout) -> Result<(*mut u8, usize), Error> {
// TODO checks
let aligned = (self.offset + layout.align() - 1) & !(layout.align() - 1);
self.offset = aligned + layout.size();
Ok((
self.buffer.as_mut_ptr().add(aligned),
self.virtual_offset + aligned,
))
}
}
impl<'a> Placer for BufferPlacer<'a> {
fn place_ref<T: Place>(&mut self, r: &T) -> Result<*const T::Output, Error> {
let layout = Layout::new::<T::Output>();
unsafe {
let (ptr, addr) = self.alloc_layout(layout)?;
let ptr = ptr as *mut T::Output;
ptr.write(r.place(self)?);
Ok(&*(addr as *const T::Output))
}
}
fn place_slice<T: Place>(&mut self, r: &[T]) -> Result<*const [T::Output], Error> {
let layout = Layout::array::<T>(r.len()).unwrap();
unsafe {
let (ptr, addr) = self.alloc_layout(layout)?;
let ptr_slice = core::ptr::slice_from_raw_parts_mut(ptr as *mut T::Output, r.len());
for (i, elem) in r.iter().enumerate() {
ptr_slice.get_unchecked_mut(i).write(elem.place(self)?);
}
Ok(core::slice::from_raw_parts(
addr as *const T::Output,
r.len(),
))
}
debugln!("write_bytes {:#x} {:x?}", self.offset, value);
self.data[self.offset..self.offset + value.len()].copy_from_slice(value);
self.offset += value.len();
Ok(())
}
}
// TODO I hate this function, it's ugly
fn setup_program_env(
space: &ProcessAddressSpace,
virt: usize,
args: &[&str],
envs: &[&str],
) -> Result<(), Error> {
const fn str_array_size(n: usize) -> usize {
// length of the array itself (1 usize) + ptr:len pairs (2x usize * n)
(1 + n * 2) * size_of::<usize>()
}
const HEADER_SIZE: usize = 2 * size_of::<usize>();
// arg data len
let args_data_size: usize = args.iter().map(|x| x.len()).sum();
let envs_data_size: usize = envs.iter().map(|x| x.len()).sum();
let ptrs_size = str_array_size(args.len()) + str_array_size(envs.len());
let total_size = args_data_size + envs_data_size + ptrs_size + HEADER_SIZE;
if total_size > 0x1000 {
todo!();
}
// debugln!("arg data size = {}", args_size);
// debugln!("env data size = {}", envs_size);
env: &[&str],
) -> Result<usize, Error> {
// TODO growing buffer
let phys_page = phys::alloc_page()?;
space.map_single(
virt,
phys_page,
MapAttributes::USER_READ | MapAttributes::USER_WRITE | MapAttributes::NON_GLOBAL,
)?;
let mut slice = unsafe { PhysicalRefMut::map_slice(phys_page, 4096) };
let mut writer = EnvWriter::new(&mut slice);
let mut buffer = unsafe { PhysicalRefMut::map_slice(phys_page, 4096) };
let mut placer = BufferPlacer::new(virt, &mut buffer);
let args_array_offset = HEADER_SIZE;
let envs_array_offset = args_array_offset + str_array_size(args.len());
let args_data_offset = envs_array_offset + str_array_size(envs.len());
let envs_data_offset = args_data_offset + args_data_size;
let in_kernel = ProgramArgumentInner { args, env };
let in_user = in_kernel.place_ref(&mut placer)?;
// Header
writer.write_usize(virt + args_array_offset)?;
writer.write_usize(virt + envs_array_offset)?;
// Args array
writer.write_usize(args.len())?;
let mut offset = args_data_offset;
for arg in args.iter() {
writer.write_usize(arg.len())?;
writer.write_usize(virt + offset)?;
offset += arg.len();
}
// Envs array
writer.write_usize(envs.len())?;
let mut offset = envs_data_offset;
for env in envs.iter() {
writer.write_usize(env.len())?;
writer.write_usize(virt + offset)?;
offset += env.len();
}
// String data
for arg in args.iter() {
writer.write_bytes(arg.as_bytes())?;
}
for env in envs.iter() {
writer.write_bytes(env.as_bytes())?;
}
Ok(())
Ok(in_user as *const _ as usize)
}
fn setup_binary<S: Into<String>>(
@ -135,7 +113,7 @@ fn setup_binary<S: Into<String>>(
MapAttributes::USER_WRITE | MapAttributes::USER_READ | MapAttributes::NON_GLOBAL,
)?;
setup_program_env(&space, virt_args_base, args, envs)?;
let arg = setup_program_env(&space, virt_args_base, args, envs)?;
debugln!(
"Entry: {:#x}, Stack: {:#x}..{:#x}, Args: {:#x}",
@ -157,7 +135,7 @@ fn setup_binary<S: Into<String>>(
}
}
let context = TaskContext::user(entry, virt_args_base, space.as_address_with_asid(), user_sp)?;
let context = TaskContext::user(entry, arg, space.as_address_with_asid(), user_sp)?;
Ok(Process::new_with_context(name, Some(space), context))
}

View File

@ -277,7 +277,6 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
SyscallFunction::Spawn => {
let options = arg_user_ref::<SpawnOptions>(args[0] as usize)?;
debugln!("Spawn {:#?}", options);
let proc = Process::current();
run_with_io(|mut io| {