xous-core/apps/app-loader/spawn/src/main.rs
bunnie 710da30952 inconsistencies in cargo fmt
running `cargo +nightly fmt -p app-loader` gives you one result.

running `cargo +nightly fmt --check` gives you another result.

manually opening and saving the files causes a result to agree
with the check.
2024-01-15 00:59:23 +08:00

185 lines
6.1 KiB
Rust

#![no_std]
#![no_main]
enum StartupCommand {
Unhandled = 0,
LoadElf = 1,
PingResponse = 2,
}
impl From<xous::MessageId> for StartupCommand {
fn from(src: xous::MessageId) -> StartupCommand {
match src {
1 => StartupCommand::LoadElf,
2 => StartupCommand::PingResponse,
_ => StartupCommand::Unhandled,
}
}
}
#[panic_handler]
fn handle_panic(arg: &core::panic::PanicInfo) -> ! {
log::info!("{arg}");
xous::terminate_process(1);
}
#[no_mangle]
pub extern "C" fn init(server1: u32, server2: u32, server3: u32, server4: u32) -> ! {
let server = xous::SID::from_u32(server1, server2, server3, server4);
// recreate the extra sections that were cut out of the stub
{
let mut memory = xous::map_memory(
None,
core::num::NonZeroUsize::new(0x40000000),
0x1000,
xous::MemoryFlags::R | xous::MemoryFlags::W,
)
.unwrap();
let connection = core::sync::atomic::AtomicU32::new(0);
let slice = unsafe {
core::slice::from_raw_parts(
&connection as *const _ as *const u8,
core::mem::size_of::<core::sync::atomic::AtomicU32>(),
)
};
// safety: memory is freshly allocated by map_memory, should be aligned and init'd to 0
unsafe {
for (dest, src) in memory.as_slice_mut::<u8>().iter_mut().skip(8).zip(slice) {
*dest = *src;
}
}
// everything should drop now
}
log_server::init_wait().unwrap();
log::set_max_level(log::LevelFilter::Info);
log::info!("my PID is {}", xous::process::id());
loop {
if let Ok(xous::Result::MessageEnvelope(mut envelope)) =
xous::rsyscall(xous::SysCall::ReceiveMessage(server))
{
match envelope.id().into() {
StartupCommand::LoadElf => {
let entry_point = read_elf(envelope.body.memory_message_mut());
drop(envelope); // we have to get rid of all messages to destroy the server
// destroy the server
xous::destroy_server(server).expect("Couldn't destroy spawn server");
jump(entry_point);
}
StartupCommand::PingResponse => ping_response(envelope),
_ => panic!("Unsupported"),
}
}
}
}
fn ping_response(envelope: xous::MessageEnvelope) {
if let Some(msg) = envelope.body.scalar_message() {
if envelope.body.is_blocking() {
xous::syscall::return_scalar(envelope.sender, msg.arg1 + 1).unwrap();
}
}
}
fn read_elf(memory: Option<&mut xous::MemoryMessage>) -> usize {
let memory = match memory {
Some(s) => s,
None => panic!(),
};
// get the elf binary from the message
// safety: buf should be aligned and correctly sized inside the MemoryMessage
let mut bin = unsafe { memory.buf.as_slice::<u8>() };
// go to the beginning of the ELF file using the provided offset
bin = &bin[memory.offset.and_then(|n| Some(n.get())).unwrap_or(0)..];
let max = bin.len();
// make sure that this is a 32 bit
assert!(bin[4] == 0x01);
// a helper function to get a region of the file as a usize
let to_usize = |start, size| {
if size == 1 {
return bin[start] as usize;
}
if size == 2 {
// assumes little endianness
return u16::from_le_bytes(bin[start..start + size].try_into().unwrap()) as usize;
}
if size == 4 {
return u32::from_le_bytes(bin[start..start + size].try_into().unwrap()) as usize;
}
panic!("Tried to get usize of invalid size!");
};
// some basic stuff to know
let entry_point = to_usize(0x18, 4);
let ph_start = to_usize(0x1c, 4);
let ph_size = to_usize(0x2A, 2);
let ph_count = to_usize(0x2C, 2);
// add the segments we should load
for i in 0..ph_count {
let start = ph_start + i * ph_size;
// only load PT_LOAD segments
if to_usize(start, 4) == 0x00000001 {
let src_addr = to_usize(start + 0x04, 4);
let vaddr = to_usize(start + 0x08, 4);
let padding = if vaddr & 0xFFF == 0 { 0 } else { vaddr & 0xFFF };
let file_size = to_usize(start + 0x10, 4);
let mem_size = to_usize(start + 0x14, 4);
let mem_size = mem_size + padding;
let mem_size = mem_size + if mem_size & 0xFFF == 0 { 0 } else { 0x1000 - (mem_size & 0xFFF) };
assert_eq!(0, mem_size & 0xFFF);
assert_eq!(0, (vaddr - padding) & 0xFFF);
log::info!(
"Loading offset {} to virtual address {} with memory size {}",
src_addr,
vaddr,
mem_size
);
let mut target_memory = xous::map_memory(
None,
core::num::NonZeroUsize::new(vaddr - padding),
mem_size,
xous::MemoryFlags::R | xous::MemoryFlags::W | xous::MemoryFlags::X,
)
.unwrap();
// TODO: rationalize why this is safe
unsafe {
for (dest, src) in target_memory
.as_slice_mut()
.iter_mut()
.skip(padding)
.zip(bin[src_addr..core::cmp::min(max, src_addr + file_size)].iter())
{
*dest = *src;
}
for dest in target_memory
.as_slice_mut()
.iter_mut()
.skip(padding + file_size)
.take(mem_size - file_size - padding)
{
*dest = 0;
}
}
}
}
memory.offset = None;
log::info!("Finished writing");
return entry_point;
}
fn jump(entry_point: usize) -> ! {
log::info!("Jumping to {}", entry_point);
let entry_fn = unsafe { core::mem::transmute::<_, fn() -> !>(entry_point as *const u8) };
entry_fn();
}