Added convenience macro for defining host externals

This commit is contained in:
Erin 2022-05-10 18:41:45 +02:00 committed by ondra05
parent 86f466cdb0
commit 0775577d31

View file

@ -4,135 +4,165 @@ use wasmi::{
Signature, Trap, ValueType, Signature, Trap, ValueType,
}; };
const ADD_FUNC_INDEX: usize = 0; macro_rules! wasmi_type {
const GET_TIME_INDEX: usize = 2; (bool) => {
const GET_RANDOM_INDEX: usize = 3; ValueType::I32
const GET_INPUT_INDEX: usize = 4; };
const PRINT_CHAR: usize = 6; (u8) => {
const SEND_SIGNAL_INDEX: usize = 1; ValueType::I32
};
pub struct HostExternals; (u16) => {
impl HostExternals { ValueType::I32
fn check_signature(&self, index: usize, signature: &Signature) -> bool { };
let (params, ret_ty): (&[_], _) = match index { (u32) => {
ADD_FUNC_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)), ValueType::I32
SEND_SIGNAL_INDEX => (&[ValueType::I32, ValueType::I32], Some(ValueType::I32)), };
GET_TIME_INDEX => (&[], Some(ValueType::I32)), (u64) => {
GET_RANDOM_INDEX => (&[], Some(ValueType::I32)), ValueType::I64
GET_INPUT_INDEX => (&[], Some(ValueType::I32)), };
PRINT_CHAR => (&[ValueType::I32], None), (i8) => {
_ => return false, ValueType::I32
}; };
(i16) => {
if params.len() != signature.params().len() || ret_ty != signature.return_type() { ValueType::I32
false };
} else { (i32) => {
params ValueType::I32
.iter() };
.zip(signature.params()) (i64) => {
.find(|(ty, param)| ty != param) ValueType::I64
.is_none() };
} (f32) => {
} ValueType::F32
};
(f64) => {
ValueType::F64
};
} }
impl Externals for HostExternals { macro_rules! wasmi_return_type {
fn invoke_index( () => {
&mut self, None
index: usize, };
args: RuntimeArgs, ($type: ident) => {
) -> Result<Option<RuntimeValue>, Trap> { Some(wasmi_type!($type))
match index { };
ADD_FUNC_INDEX => { }
let a: u32 = args.nth_checked(0)?;
let b: u32 = args.nth_checked(1)?;
let result = a + b;
trace!("SYSCALL: {} + {} = {}", a, b, result);
Ok(Some(RuntimeValue::I32(result as i32)))
}
SEND_SIGNAL_INDEX => { macro_rules! host_externals {
let pid: u32 = args.nth_checked(0)?; (
let signal: u32 = args.nth_checked(1)?; $($index: literal: $name: ident ($($arg_name: ident: $arg_type: ident),* $(,)?) $(-> $return_type: ident)? $block: block)*
) => {
trace!("SYSCALL: send signal {} to pid {}", signal, pid); pub struct HostExternals;
let ret = RuntimeValue::I32(0); impl Externals for HostExternals {
Ok(Some(ret)) fn invoke_index(
} &mut self,
index: usize,
GET_TIME_INDEX => { args: RuntimeArgs
use core::sync::atomic::Ordering::*; ) -> Result<Option<RuntimeValue>, Trap> {
trace!("SYSCALL: get time"); match index {
$(
x86_64::instructions::interrupts::disable(); $index => match args.as_ref() {
let tick_time = kernel::TICK.load(Relaxed); [$($arg_name),*] => {
x86_64::instructions::interrupts::enable(); $(
let $arg_name: $arg_type = (*$arg_name)
let ret = RuntimeValue::I32(tick_time.try_into().unwrap()); .try_into()
Ok(Some(ret)) .ok_or(wasmi::TrapKind::UnexpectedSignature)?;
} )*
$block
GET_RANDOM_INDEX => { },
trace!("SYSCALL: get random"); _ => return Err(wasmi::TrapKind::UnexpectedSignature.into()),
let rand = generate_process_pass(); }
),*
let ret = RuntimeValue::I32(rand as i32); _ => {
error!("Unimplemented function at {index}");
// let ret = RuntimeValue::I32(rand.try_into().unwrap()); Err(Trap::new(wasmi::TrapKind::Unreachable))
Ok(Some(ret)) }
}
GET_INPUT_INDEX => {
let input = None;
x86_64::instructions::interrupts::without_interrupts(|| KEYBUFF.lock().pop());
if let Some(chr) = input {
trace!("SYSCALL: input: {}", chr);
} }
let ret = RuntimeValue::I32(input.unwrap_or(0x00 as char) as i32);
Ok(Some(ret))
}
PRINT_CHAR => {
let chr: u8 = args.nth_checked(0)?;
trace!("SYSCALL: print: {}", chr);
print!("{}", char::from(chr));
Ok(None)
}
_ => {
error!("Unimplemented function at {}", index);
Err(Trap::new(wasmi::TrapKind::Unreachable))
} }
} }
}
impl ModuleImportResolver for HostExternals {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
let (index, param_types, return_type): (usize, &[ValueType], Option<ValueType>) =
match field_name {
$(
stringify!($name) => (
$index,
&[$(wasmi_type!($arg_type)),*],
wasmi_return_type!($($return_type)?),
),
)*
_ => {
return Err(Error::Instantiation(format!(
"Export {field_name} not found",
)));
},
};
if !(param_types.len() != signature.params().len() || return_type != signature.return_type())
&& param_types
.iter()
.zip(signature.params())
.find(|(ty, param)| ty != param)
.is_none()
{
trace!("Resolved export {field_name} at func {index}");
Ok(FuncInstance::alloc_host(signature.clone(), index))
} else {
Err(Error::Instantiation(format!(
"Export {field_name} has a bad signature {signature:?}",
)))
}
}
}
};
} }
impl ModuleImportResolver for HostExternals { host_externals! {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> { 0: add(a: u32, b: u32) -> u32 {
let index = match field_name { let result = a + b;
"add" => ADD_FUNC_INDEX, trace!("SYSCALL: {} + {} = {}", a, b, result);
"send_signal" => SEND_SIGNAL_INDEX, Ok(Some(result.into()))
"get_time" => GET_TIME_INDEX, }
"get_random" => GET_RANDOM_INDEX,
"get_input" => GET_INPUT_INDEX,
"print_char" => PRINT_CHAR,
_ => {
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
};
if !self.check_signature(index, signature) { 1: send_signal(pid: u32, signal: u32) -> i32 {
return Err(Error::Instantiation(format!( trace!("SYSCALL: send signal {} to pid {}", signal, pid);
"Export {} has a bad signature {:?}", Ok(Some(0.into()))
field_name, signature }
)));
2: get_time() -> u32 {
use core::sync::atomic::Ordering::*;
trace!("SYSCALL: get time");
x86_64::instructions::interrupts::disable();
let tick_time = kernel::TICK.load(Relaxed);
x86_64::instructions::interrupts::enable();
let ret: u32 = tick_time.try_into().unwrap();
Ok(Some(ret.into()))
}
3: get_random() -> i32 {
trace!("SYSCALL: get random");
let rand = generate_process_pass() as i32;
Ok(Some(rand.into()))
}
4: get_input() -> i32 {
let input = None;
x86_64::instructions::interrupts::without_interrupts(|| KEYBUFF.lock().pop());
if let Some(chr) = input {
trace!("SYSCALL: input: {}", chr);
} }
trace!("Resolved export {} as func {}", field_name, index); let ret = input.unwrap_or(0x00 as char) as i32;
Ok(FuncInstance::alloc_host(signature.clone(), index)) Ok(Some(ret.into()))
}
5: print_char(chr: u8) {
trace!("SYSCALL: print: {}", chr);
print!("{}", char::from(chr));
Ok(None)
} }
} }