Compare commits
No commits in common. "trunk" and "master" have entirely different histories.
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -220,7 +220,7 @@ name = "hbxrt"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hbvm",
|
||||
"memmap2",
|
||||
"nix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -256,15 +256,6 @@ version = "2.6.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
|
|
29
c-abi.md
Normal file
29
c-abi.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# C ABI (proposal)
|
||||
|
||||
## C datatypes
|
||||
| C Type | Description | Size (B) |
|
||||
|:------------|:-------------------------|-------------:|
|
||||
| char | Character / byte | 8 |
|
||||
| short | Short integer | 16 |
|
||||
| int | Integer | 32 |
|
||||
| long | Long integer | 64 |
|
||||
| long long | Long long integer | 64 |
|
||||
| T* | Pointer | 64 |
|
||||
| float | Single-precision float | 32 |
|
||||
| double | Double-precision float | 64 |
|
||||
| long double | Extended-precision float | **Bikeshed** |
|
||||
|
||||
## Registers
|
||||
| Register | ABI Name | Description | Saver |
|
||||
|:---------|:---------|:---------------|:-------|
|
||||
| `r0` | — | Zero register | N/A |
|
||||
| `r1` | `ra` | Return address | Caller |
|
||||
| `r2` | `sp` | Stack pointer | Callee |
|
||||
| `r3` | `tp` | Thread pointer | N/A |
|
||||
|
||||
**TODO:** Parameters
|
||||
|
||||
**TODO:** Saved
|
||||
|
||||
**TODO:** Temp
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
0x2E, ADDI16, RRH, "Addition with immediate (16b)" ;
|
||||
0x2F, ADDI32, RRW, "Addition with immediate (32b)" ;
|
||||
0x30, ADDI64, RRD, "Addition with immediate (64b)" ;
|
||||
0x31, MULI8, RRB, "Multiplication with immediate (8b)" ;
|
||||
0x31, MULI8, RRW, "Multiplication with immediate (8b)" ;
|
||||
0x32, MULI16, RRH, "Multiplication with immediate (16b)" ;
|
||||
0x33, MULI32, RRW, "Multiplication with immediate (32b)" ;
|
||||
0x34, MULI64, RRD, "Multiplication with immediate (64b)" ;
|
||||
|
@ -114,7 +114,7 @@
|
|||
0x71, FTI64, RRB, "Float 64 to int" ;
|
||||
0x72, FC32T64, RR, "Float 64 to Float 32" ;
|
||||
0x73, FC64T32, RRB, "Float 32 to Float 64" ;
|
||||
0x74, LRA16, RRO, "Load relative immediate (16 bit)" ;
|
||||
0x74, LRA16, RRP, "Load relative immediate (16 bit)" ;
|
||||
0x75, LDR16, RRPH, "Load from relative address (16 bit)" ;
|
||||
0x76, STR16, RRPH, "Store to relative address (16 bit)" ;
|
||||
0x77, JMP16, P, "Relative jump (16 bit)" ;
|
||||
|
|
|
@ -43,19 +43,17 @@ impl BlockCopier {
|
|||
/// - Same as for [`Memory::load`] and [`Memory::store`]
|
||||
pub unsafe fn poll(&mut self, memory: &mut impl Memory) -> Poll<Result<(), BlkCopyError>> {
|
||||
// Safety: Assuming uninit of array of MaybeUninit is sound
|
||||
let mut buf = AlignedBuf(unsafe { MaybeUninit::uninit().assume_init() });
|
||||
let mut buf = AlignedBuf(MaybeUninit::uninit().assume_init());
|
||||
|
||||
// We have at least one buffer size to copy
|
||||
if self.n_buffers != 0 {
|
||||
if let Err(e) = unsafe {
|
||||
act(
|
||||
if let Err(e) = act(
|
||||
memory,
|
||||
self.src,
|
||||
self.dst,
|
||||
buf.0.as_mut_ptr().cast(),
|
||||
BUF_SIZE,
|
||||
)
|
||||
} {
|
||||
) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
|
||||
|
@ -75,15 +73,13 @@ impl BlockCopier {
|
|||
}
|
||||
|
||||
if self.rem != 0 {
|
||||
if let Err(e) = unsafe {
|
||||
act(
|
||||
if let Err(e) = act(
|
||||
memory,
|
||||
self.src,
|
||||
self.dst,
|
||||
buf.0.as_mut_ptr().cast(),
|
||||
self.rem,
|
||||
)
|
||||
} {
|
||||
) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +97,6 @@ unsafe fn act(
|
|||
buf: *mut u8,
|
||||
count: usize,
|
||||
) -> Result<(), BlkCopyError> {
|
||||
unsafe {
|
||||
// Load to buffer
|
||||
memory
|
||||
.load(src, buf, count)
|
||||
|
@ -117,7 +112,6 @@ unsafe fn act(
|
|||
access_reason: MemoryAccessReason::Store,
|
||||
addr,
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ unsafe fn set_rounding_mode(mode: RoundingMode) {
|
|||
}
|
||||
|
||||
let fpcr: u64;
|
||||
unsafe { asm!("mrs {}, fpcr", out(reg) fpcr) };
|
||||
asm!("mrs {}, fpcr", out(reg) fpcr);
|
||||
|
||||
let fpcr = fpcr & !(0b11 << 22)
|
||||
| (match mode {
|
||||
|
@ -56,7 +56,7 @@ unsafe fn set_rounding_mode(mode: RoundingMode) {
|
|||
RoundingMode::Down => 0b10,
|
||||
}) << 22;
|
||||
|
||||
unsafe { asm!("msr fpcr, {}", in(reg) fpcr) };
|
||||
asm!("msr fpcr, {}", in(reg) fpcr);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -56,7 +56,6 @@ fnsdef! {
|
|||
/// [`default_rounding_mode`], you have to rely on inline assembly
|
||||
#[inline(always)]
|
||||
unsafe fn set_rounding_mode(mode: RoundingMode) {
|
||||
unsafe {
|
||||
arin::_MM_SET_ROUNDING_MODE(match mode {
|
||||
RoundingMode::NearestEven => return,
|
||||
RoundingMode::Truncate => arin::_MM_ROUND_TOWARD_ZERO,
|
||||
|
@ -64,7 +63,6 @@ unsafe fn set_rounding_mode(mode: RoundingMode) {
|
|||
RoundingMode::Down => arin::_MM_ROUND_DOWN,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn default_rounding_mode() {
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(fn_align))]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
|
|
@ -48,14 +48,14 @@ impl ICache {
|
|||
|
||||
let pbase = self
|
||||
.data
|
||||
.or_else(|| unsafe { self.fetch_page(self.base + self.size, root_pt) })?;
|
||||
.or_else(|| self.fetch_page(self.base + self.size, root_pt))?;
|
||||
|
||||
// Get address base
|
||||
let base = addr.map(|x| x & self.mask);
|
||||
|
||||
// Base not matching, fetch anew
|
||||
if base != self.base {
|
||||
unsafe { self.fetch_page(base, root_pt) }?;
|
||||
self.fetch_page(base, root_pt)?;
|
||||
};
|
||||
|
||||
let offset = addr.get() & !self.mask;
|
||||
|
@ -68,27 +68,25 @@ impl ICache {
|
|||
let first_copy = requ_size.saturating_sub(rem);
|
||||
|
||||
// Copy non-overflowing part
|
||||
unsafe { copy_nonoverlapping(pbase.as_ptr(), ret.as_mut_ptr().cast::<u8>(), first_copy) };
|
||||
copy_nonoverlapping(pbase.as_ptr(), ret.as_mut_ptr().cast::<u8>(), first_copy);
|
||||
|
||||
// Copy overflow
|
||||
if rem != 0 {
|
||||
let pbase = unsafe { self.fetch_page(self.base + self.size, root_pt) }?;
|
||||
let pbase = self.fetch_page(self.base + self.size, root_pt)?;
|
||||
|
||||
// Unlikely, unsupported scenario
|
||||
if rem > self.size as _ {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
copy_nonoverlapping(
|
||||
pbase.as_ptr(),
|
||||
ret.as_mut_ptr().cast::<u8>().add(first_copy),
|
||||
rem,
|
||||
)
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
Some(unsafe { ret.assume_init() })
|
||||
Some(ret.assume_init())
|
||||
}
|
||||
|
||||
/// Fetch a page
|
||||
|
|
|
@ -36,11 +36,9 @@ impl<'p, A, const OUT_PROG_EXEC: bool> SoftPagedMem<'p, A, OUT_PROG_EXEC> {
|
|||
|
||||
// Walk pagetable levels
|
||||
for lvl in (lookup_depth + 1..5).rev() {
|
||||
let entry = unsafe {
|
||||
(*current_pt)
|
||||
let entry = (*current_pt)
|
||||
.table
|
||||
.get_unchecked_mut(addr_extract_index(target, lvl))
|
||||
};
|
||||
.get_unchecked_mut(addr_extract_index(target, lvl));
|
||||
|
||||
let ptr = entry.ptr();
|
||||
match entry.permission() {
|
||||
|
@ -48,13 +46,13 @@ impl<'p, A, const OUT_PROG_EXEC: bool> SoftPagedMem<'p, A, OUT_PROG_EXEC> {
|
|||
// No worries! Let's create one (allocates).
|
||||
Permission::Empty => {
|
||||
// Increase children count
|
||||
unsafe { *current_pt }.childen += 1;
|
||||
(*current_pt).childen += 1;
|
||||
|
||||
let table = Box::into_raw(Box::new(PtPointedData {
|
||||
pt: PageTable::default(),
|
||||
}));
|
||||
|
||||
unsafe { core::ptr::write(entry, PtEntry::new(table, Permission::Node)) };
|
||||
core::ptr::write(entry, PtEntry::new(table, Permission::Node));
|
||||
current_pt = table as _;
|
||||
}
|
||||
// Continue walking
|
||||
|
@ -65,11 +63,9 @@ impl<'p, A, const OUT_PROG_EXEC: bool> SoftPagedMem<'p, A, OUT_PROG_EXEC> {
|
|||
}
|
||||
}
|
||||
|
||||
let node = unsafe {
|
||||
(*current_pt)
|
||||
let node = (*current_pt)
|
||||
.table
|
||||
.get_unchecked_mut(addr_extract_index(target, lookup_depth))
|
||||
};
|
||||
.get_unchecked_mut(addr_extract_index(target, lookup_depth));
|
||||
|
||||
// Check if node is not mapped
|
||||
if node.permission() != Permission::Empty {
|
||||
|
@ -77,10 +73,8 @@ impl<'p, A, const OUT_PROG_EXEC: bool> SoftPagedMem<'p, A, OUT_PROG_EXEC> {
|
|||
}
|
||||
|
||||
// Write entry
|
||||
unsafe {
|
||||
(*current_pt).childen += 1;
|
||||
core::ptr::write(node, PtEntry::new(host.cast(), perm));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> Memory
|
|||
target,
|
||||
count,
|
||||
perm_check::readable,
|
||||
|src, dst, count| unsafe { core::ptr::copy_nonoverlapping(src, dst, count) },
|
||||
|src, dst, count| core::ptr::copy_nonoverlapping(src, dst, count),
|
||||
)
|
||||
.map_err(LoadError)
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> Memory
|
|||
source.cast_mut(),
|
||||
count,
|
||||
perm_check::writable,
|
||||
|dst, src, count| unsafe { core::ptr::copy_nonoverlapping(src, dst, count) },
|
||||
|dst, src, count| core::ptr::copy_nonoverlapping(src, dst, count),
|
||||
)
|
||||
.map_err(StoreError)
|
||||
}
|
||||
|
@ -80,14 +80,16 @@ impl<'p, PfH: HandlePageFault, const OUT_PROG_EXEC: bool> Memory
|
|||
#[inline(always)]
|
||||
unsafe fn prog_read<T>(&mut self, addr: Address) -> T {
|
||||
if OUT_PROG_EXEC && addr.truncate_usize() > self.program.len() {
|
||||
return unsafe { self.icache.fetch::<T>(addr, self.root_pt) }
|
||||
return self
|
||||
.icache
|
||||
.fetch::<T>(addr, self.root_pt)
|
||||
.unwrap_or_else(|| unsafe { core::mem::zeroed() });
|
||||
}
|
||||
|
||||
let addr = addr.truncate_usize();
|
||||
self.program
|
||||
.get(addr..addr + size_of::<T>())
|
||||
.map(|x| unsafe { x.as_ptr().cast::<T>().read() })
|
||||
.map(|x| x.as_ptr().cast::<T>().read())
|
||||
.unwrap_or_else(|| unsafe { core::mem::zeroed() })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,7 @@ use {
|
|||
|
||||
macro_rules! handler {
|
||||
($self:expr, |$ty:ident ($($ident:pat),* $(,)?)| $expr:expr) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
let $ty($($ident),*) = unsafe { $self.decode::<$ty>() };
|
||||
let $ty($($ident),*) = $self.decode::<$ty>();
|
||||
#[allow(clippy::no_effect)] let e = $expr;
|
||||
$self.bump_pc::<$ty>();
|
||||
e
|
||||
|
@ -41,14 +40,14 @@ where
|
|||
// Contribution guide:
|
||||
// - Zero register shall never be overwitten. It's value has to always be 0.
|
||||
// - Prefer `Self::read_reg` and `Self::write_reg` functions
|
||||
// - Try to use `handler!` macro for decoding and then bumping program counter
|
||||
// - Extract parameters using `param!` macro
|
||||
// - Prioritise speed over code size
|
||||
// - Memory is cheap, CPUs not that much
|
||||
// - Do not heap allocate at any cost
|
||||
// - Yes, user-provided trap handler may allocate,
|
||||
// but that is not our »fault«.
|
||||
// - Unsafe is kinda must, but be sure you have validated everything
|
||||
// - Your contributions have to pass sanitizers, fuzzer and Miri
|
||||
// - Your contributions have to pass sanitizers and Miri
|
||||
// - Strictly follow the spec
|
||||
// - The spec does not specify how you perform actions, in what order,
|
||||
// just that the observable effects have to be performed in order and
|
||||
|
@ -375,16 +374,13 @@ where
|
|||
/// Bump instruction pointer
|
||||
#[inline(always)]
|
||||
fn bump_pc<T: Copy>(&mut self) {
|
||||
self.pc = self
|
||||
.pc
|
||||
.wrapping_add(core::mem::size_of::<T>())
|
||||
.wrapping_add(1);
|
||||
self.pc = self.pc.wrapping_add(core::mem::size_of::<T>());
|
||||
}
|
||||
|
||||
/// Decode instruction operands
|
||||
#[inline(always)]
|
||||
unsafe fn decode<T: Copy>(&mut self) -> T {
|
||||
unsafe { self.memory.prog_read::<T>(self.pc + 1_u64) }
|
||||
self.memory.prog_read::<T>(self.pc + 1_u64)
|
||||
}
|
||||
|
||||
/// Load
|
||||
|
@ -401,7 +397,6 @@ where
|
|||
_ => 0,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
self.memory.load(
|
||||
self.ldst_addr_uber(dst, base, offset, count, n)?,
|
||||
self.registers
|
||||
|
@ -409,8 +404,7 @@ where
|
|||
.add(usize::from(dst) + usize::from(n))
|
||||
.cast(),
|
||||
usize::from(count).saturating_sub(n.into()),
|
||||
)
|
||||
}?;
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -424,13 +418,11 @@ where
|
|||
offset: u64,
|
||||
count: u16,
|
||||
) -> Result<(), VmRunError> {
|
||||
unsafe {
|
||||
self.memory.store(
|
||||
self.ldst_addr_uber(dst, base, offset, count, 0)?,
|
||||
self.registers.as_ptr().add(usize::from(dst)).cast(),
|
||||
count.into(),
|
||||
)
|
||||
}?;
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -443,7 +435,7 @@ where
|
|||
/// Perform binary operating over two registers
|
||||
#[inline(always)]
|
||||
unsafe fn binary_op<T: ValueVariant>(&mut self, op: impl Fn(T, T) -> T) {
|
||||
let OpsRRR(tg, a0, a1) = unsafe { self.decode() };
|
||||
let OpsRRR(tg, a0, a1) = self.decode();
|
||||
self.write_reg(
|
||||
tg,
|
||||
op(self.read_reg(a0).cast::<T>(), self.read_reg(a1).cast::<T>()),
|
||||
|
@ -458,7 +450,7 @@ where
|
|||
#[repr(packed)]
|
||||
struct OpsRRImm<I>(OpsRR, I);
|
||||
|
||||
let OpsRRImm::<T>(OpsRR(tg, reg), imm) = unsafe { self.decode() };
|
||||
let OpsRRImm::<T>(OpsRR(tg, reg), imm) = self.decode();
|
||||
self.write_reg(tg, op(self.read_reg(reg).cast::<T>(), imm));
|
||||
self.bump_pc::<OpsRRImm<T>>();
|
||||
}
|
||||
|
@ -466,7 +458,7 @@ where
|
|||
/// Perform binary operation over register and shift immediate
|
||||
#[inline(always)]
|
||||
unsafe fn binary_op_shift<T: ValueVariant>(&mut self, op: impl Fn(T, u32) -> T) {
|
||||
let OpsRRR(tg, a0, a1) = unsafe { self.decode() };
|
||||
let OpsRRR(tg, a0, a1) = self.decode();
|
||||
self.write_reg(
|
||||
tg,
|
||||
op(
|
||||
|
@ -480,7 +472,7 @@ where
|
|||
/// Perform binary operation over register and shift immediate
|
||||
#[inline(always)]
|
||||
unsafe fn binary_op_ims<T: ValueVariant>(&mut self, op: impl Fn(T, u32) -> T) {
|
||||
let OpsRRB(tg, reg, imm) = unsafe { self.decode() };
|
||||
let OpsRRB(tg, reg, imm) = self.decode();
|
||||
self.write_reg(tg, op(self.read_reg(reg).cast::<T>(), imm.into()));
|
||||
self.bump_pc::<OpsRRW>();
|
||||
}
|
||||
|
@ -539,7 +531,7 @@ where
|
|||
/// Jump at `PC + #3` if ordering on `#0 <=> #1` is equal to expected
|
||||
#[inline(always)]
|
||||
unsafe fn cond_jmp<T: ValueVariant + Ord>(&mut self, expected: Ordering) {
|
||||
let OpsRRP(a0, a1, ja) = unsafe { self.decode() };
|
||||
let OpsRRP(a0, a1, ja) = self.decode();
|
||||
if self
|
||||
.read_reg(a0)
|
||||
.cast::<T>()
|
||||
|
|
|
@ -6,4 +6,4 @@ default-run = "hbxrt"
|
|||
|
||||
[dependencies]
|
||||
hbvm.path = "../hbvm"
|
||||
memmap2 = "0.9"
|
||||
nix = { version = "0.27", features = ["mman", "signal"] }
|
||||
|
|
|
@ -1,50 +1,65 @@
|
|||
//! Holey Bytes Experimental Runtime
|
||||
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
mod mem;
|
||||
|
||||
use {
|
||||
hbvm::{mem::Address, Vm, VmRunOk},
|
||||
memmap2::Mmap,
|
||||
std::{env::args, fs::File, mem::MaybeUninit, process::exit},
|
||||
nix::sys::mman::{mmap, MapFlags, ProtFlags},
|
||||
std::{env::args, fs::File, num::NonZeroUsize, process::exit},
|
||||
};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
eprintln!("== HB×RT (Holey Bytes Experimental Runtime) v0.1 ==");
|
||||
eprintln!("== HB×RT (Holey Bytes Linux Runtime) v0.1 ==");
|
||||
eprintln!("[W] Currently supporting only flat images");
|
||||
|
||||
let mut args = args().skip(1);
|
||||
let Some(image_path) = args.next() else {
|
||||
let Some(image_path) = args().nth(1) else {
|
||||
eprintln!("[E] Missing image path");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
let dsls = args.next().as_deref() == Some("-L");
|
||||
if cfg!(not(target_os = "linux")) && dsls {
|
||||
eprintln!("[E] Unsupported platform for Direct Linux syscall mode");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if dsls {
|
||||
eprintln!("[I] Direct Linux syscall mode activated")
|
||||
}
|
||||
|
||||
// Allocate stack
|
||||
let mut stack = Box::new(MaybeUninit::<[u8; 1024 * 1024 * 2]>::uninit());
|
||||
eprintln!("[I] Stack allocated at {:p}", stack.as_ptr());
|
||||
|
||||
// Load program
|
||||
eprintln!("[I] Loading image from \"{image_path}\"");
|
||||
let file_handle = File::open(image_path)?;
|
||||
let mmap = unsafe { Mmap::map(&file_handle) }?;
|
||||
let file = File::open(image_path)?;
|
||||
let ptr = unsafe {
|
||||
mmap(
|
||||
None,
|
||||
NonZeroUsize::new(file.metadata()?.len() as usize).ok_or("File is empty")?,
|
||||
ProtFlags::PROT_READ,
|
||||
MapFlags::MAP_PRIVATE,
|
||||
Some(&file),
|
||||
0,
|
||||
)?
|
||||
};
|
||||
|
||||
eprintln!("[I] Image loaded at {:p}", mmap.as_ptr());
|
||||
|
||||
let mut vm = unsafe { Vm::<_, 0>::new(mem::HostMemory, Address::new(mmap.as_ptr() as u64)) };
|
||||
vm.write_reg(254, stack.as_mut_ptr() as u64);
|
||||
eprintln!("[I] Image loaded at {ptr:p}");
|
||||
|
||||
// Execute program
|
||||
let mut vm = unsafe { Vm::<_, 0>::new(mem::HostMemory, Address::new(ptr as u64)) };
|
||||
|
||||
// Memory access fault handling
|
||||
unsafe {
|
||||
use nix::sys::signal;
|
||||
|
||||
extern "C" fn action(
|
||||
_: std::ffi::c_int,
|
||||
info: *mut nix::libc::siginfo_t,
|
||||
_: *mut std::ffi::c_void,
|
||||
) {
|
||||
unsafe {
|
||||
eprintln!("[E] Memory access fault at {:p}", (*info).si_addr());
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
signal::sigaction(
|
||||
signal::Signal::SIGSEGV,
|
||||
&nix::sys::signal::SigAction::new(
|
||||
signal::SigHandler::SigAction(action),
|
||||
signal::SaFlags::SA_NODEFER,
|
||||
nix::sys::signalfd::SigSet::empty(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
let stat = loop {
|
||||
match vm.run() {
|
||||
Ok(VmRunOk::Breakpoint) => eprintln!(
|
||||
|
@ -52,7 +67,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
vm.pc, vm.registers
|
||||
),
|
||||
Ok(VmRunOk::Timer) => (),
|
||||
Ok(VmRunOk::Ecall) if dsls => unsafe {
|
||||
Ok(VmRunOk::Ecall) => unsafe {
|
||||
std::arch::asm!(
|
||||
"syscall",
|
||||
inlateout("rax") vm.registers[1].0,
|
||||
|
@ -64,10 +79,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
in("r9") vm.registers[7].0,
|
||||
)
|
||||
},
|
||||
Ok(VmRunOk::Ecall) => {
|
||||
eprintln!("[E] General environment calls not supported");
|
||||
exit(1);
|
||||
}
|
||||
Ok(VmRunOk::End) => break Ok(()),
|
||||
Err(e) => break Err(e),
|
||||
}
|
||||
|
|
|
@ -26,6 +26,6 @@ impl Memory for HostMemory {
|
|||
|
||||
#[inline]
|
||||
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T {
|
||||
unsafe { core::ptr::read(addr.get() as *const T) }
|
||||
core::ptr::read(addr.get() as *const T)
|
||||
}
|
||||
}
|
||||
|
|
22
spec.md
22
spec.md
|
@ -480,26 +480,18 @@ Program counter stays on the currently executed instruction
|
|||
| long long | Long long integer | 8 |
|
||||
| float | Single-precision float | 4 |
|
||||
| double | Double-precision float | 8 |
|
||||
| long double | Extended-precision float | 8 |
|
||||
|
||||
- Bikeshedding note: `long double` is now 8 bytes as
|
||||
the base ISA does not support `f128`. an extension
|
||||
for that should be made.
|
||||
| long double | Extended-precision float | TBD |
|
||||
|
||||
## Call convention
|
||||
- Registers r1 – r31 are caller saved
|
||||
- Registers r32 – r255 are callee saved
|
||||
- Registers r1 – r30 are caller saved
|
||||
- Registers r31 – r255 are callee saved
|
||||
|
||||
| Register | Description | Saver |
|
||||
|:-----------|:--------------------|:-------|
|
||||
|:---------|:--------------------|:-------|
|
||||
| r0 | Hard-wired zero | N/A |
|
||||
| r1 - r2 | Return values | Caller |
|
||||
| r2 - r11 | Function parameters | Caller |
|
||||
| r12 - r30 | General purpose | Caller |
|
||||
| r31 | Return address | Caller |
|
||||
| r32 - r253 | General purpose | Callee |
|
||||
| r254 | Stack pointer | Callee |
|
||||
| r255 | Thread pointer | N/A |
|
||||
| r30 | Return address | Caller |
|
||||
|
||||
- If return value is too big to fit r1, r2 is also used.
|
||||
- Values larger than two double-words are passed by reference
|
||||
If return value is too big to fit one register, r2 is also used.
|
||||
TODO: Stack pointer, Thread pointer, ...
|
||||
|
|
Loading…
Reference in a new issue