can run entrypoint on user elf
syscalls via call_abi are still not working
This commit is contained in:
@@ -41,7 +41,6 @@ embassy-rp = { version = "0.4.0", features = [
|
||||
"critical-section-impl",
|
||||
"unstable-pac",
|
||||
"time-driver",
|
||||
"binary-info",
|
||||
] }
|
||||
embassy-usb = "0.4.0"
|
||||
embassy-futures = "0.1.1"
|
||||
@@ -77,11 +76,12 @@ embedded-layout = "0.4.2"
|
||||
|
||||
static_cell = "2.1.1"
|
||||
bitflags = "2.9.1"
|
||||
talc = "4.4.3"
|
||||
spin = "0.10.0"
|
||||
heapless = "0.8.0"
|
||||
num_enum = { version = "0.7.4", default-features = false }
|
||||
elf_loader = {version ="0.12.0", default-features = false, features = ["portable-atomic"]}
|
||||
goblin = { version = "0.10.0", default-features = false, features = ["elf32", "elf64", "alloc", "endian_fd"] }
|
||||
bumpalo = "3.19.0"
|
||||
talc = "4.4.3"
|
||||
spin = "0.10.0"
|
||||
|
||||
shared = { path = "../shared" }
|
||||
abi = { path = "../abi" }
|
||||
|
||||
@@ -19,7 +19,7 @@ fn main() {
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("../memory.x"))
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
|
||||
75
kernel/memory.x
Normal file
75
kernel/memory.x
Normal file
@@ -0,0 +1,75 @@
|
||||
MEMORY {
|
||||
/*
|
||||
* The RP2350 has either external or internal flash.
|
||||
*
|
||||
* 2 MiB is a safe default here, although a Pico 2 has 4 MiB.
|
||||
*/
|
||||
FLASH : ORIGIN = 0x10000000, LENGTH = 4096K
|
||||
/*
|
||||
* RAM consists of 8 banks, SRAM0-SRAM7, with a striped mapping.
|
||||
* This is usually good for performance, as it distributes load on
|
||||
* those banks evenly.
|
||||
*/
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 512K
|
||||
/*
|
||||
* RAM banks 8 and 9 use a direct mapping. They can be used to have
|
||||
* memory areas dedicated for some specific job, improving predictability
|
||||
* of access times.
|
||||
* Example: Separate stacks for core0 and core1.
|
||||
*/
|
||||
SRAM4 : ORIGIN = 0x20080000, LENGTH = 4K
|
||||
SRAM5 : ORIGIN = 0x20081000, LENGTH = 4K
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
/* ### Boot ROM info
|
||||
*
|
||||
* Goes after .vector_table, to keep it in the first 4K of flash
|
||||
* where the Boot ROM (and picotool) can find it
|
||||
*/
|
||||
.start_block : ALIGN(4)
|
||||
{
|
||||
__start_block_addr = .;
|
||||
KEEP(*(.start_block));
|
||||
KEEP(*(.boot_info));
|
||||
} > FLASH
|
||||
|
||||
} INSERT AFTER .vector_table;
|
||||
|
||||
/* move .text to start /after/ the boot info */
|
||||
_stext = ADDR(.start_block) + SIZEOF(.start_block);
|
||||
|
||||
SECTIONS {
|
||||
/* ### Picotool 'Binary Info' Entries
|
||||
*
|
||||
* Picotool looks through this block (as we have pointers to it in our
|
||||
* header) to find interesting information.
|
||||
*/
|
||||
.bi_entries : ALIGN(4)
|
||||
{
|
||||
/* We put this in the header */
|
||||
__bi_entries_start = .;
|
||||
/* Here are the entries */
|
||||
KEEP(*(.bi_entries));
|
||||
/* Keep this block a nice round size */
|
||||
. = ALIGN(4);
|
||||
/* We put this in the header */
|
||||
__bi_entries_end = .;
|
||||
} > FLASH
|
||||
} INSERT AFTER .text;
|
||||
|
||||
SECTIONS {
|
||||
/* ### Boot ROM extra info
|
||||
*
|
||||
* Goes after everything in our program, so it can contain a signature.
|
||||
*/
|
||||
.end_block : ALIGN(4)
|
||||
{
|
||||
__end_block_addr = .;
|
||||
KEEP(*(.end_block));
|
||||
} > FLASH
|
||||
|
||||
} INSERT AFTER .uninit;
|
||||
|
||||
PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
|
||||
PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
|
||||
@@ -1,4 +1,5 @@
|
||||
use abi::Syscall;
|
||||
use defmt::info;
|
||||
use embassy_futures::block_on;
|
||||
use embedded_graphics::{
|
||||
Drawable,
|
||||
@@ -9,8 +10,9 @@ use embedded_graphics::{
|
||||
|
||||
use crate::display::FRAMEBUFFER;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn call_abi(call: *const Syscall) {
|
||||
#[allow(unused)]
|
||||
pub fn call_abi(call: *const Syscall) {
|
||||
info!("called abi");
|
||||
let call = unsafe { &*call };
|
||||
match call {
|
||||
Syscall::DrawPixel { x, y, color } => {
|
||||
@@ -20,6 +22,7 @@ pub extern "C" fn call_abi(call: *const Syscall) {
|
||||
}
|
||||
|
||||
fn draw_pixel(x: u32, y: u32, color: u16) {
|
||||
info!("draw pixel abi called");
|
||||
let framebuffer = block_on(FRAMEBUFFER.lock());
|
||||
Rectangle::new(Point::new(x as i32, y as i32), Size::new(1, 1))
|
||||
.draw_styled(
|
||||
|
||||
85
kernel/src/elf.rs
Normal file
85
kernel/src/elf.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
#![allow(static_mut_refs)]
|
||||
use abi::Syscall;
|
||||
use bumpalo::Bump;
|
||||
use core::{alloc::Layout, ffi::c_void, ptr::NonNull, slice::from_raw_parts_mut};
|
||||
use goblin::{
|
||||
elf::{Elf, header::ET_DYN, program_header::PT_LOAD, sym},
|
||||
elf32,
|
||||
};
|
||||
|
||||
use crate::abi::call_abi;
|
||||
|
||||
pub fn load_elf(elf_bytes: &[u8], bump: &mut Bump) -> Result<extern "C" fn() -> !, ()> {
|
||||
let elf = Elf::parse(elf_bytes).map_err(|_| ())?;
|
||||
|
||||
if elf.is_64
|
||||
|| elf.is_lib
|
||||
|| elf.is_object_file()
|
||||
|| !elf.little_endian
|
||||
|| elf.header.e_type != ET_DYN
|
||||
|| elf.interpreter.is_some()
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Find base address (lowest virtual address of PT_LOAD segments)
|
||||
let base_vaddr = elf
|
||||
.program_headers
|
||||
.iter()
|
||||
.filter(|ph| ph.p_type == PT_LOAD)
|
||||
.map(|ph| ph.p_vaddr)
|
||||
.min()
|
||||
.ok_or(())?;
|
||||
|
||||
// Determine total memory needed for all PT_LOAD segments
|
||||
let total_size = elf
|
||||
.program_headers
|
||||
.iter()
|
||||
.filter(|ph| ph.p_type == PT_LOAD)
|
||||
.map(|ph| {
|
||||
let start = ph.p_vaddr;
|
||||
let end = ph.p_vaddr + ph.p_memsz;
|
||||
end - base_vaddr
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0) as usize;
|
||||
|
||||
// Allocate one big block from the bump heap
|
||||
let layout = Layout::from_size_align(total_size, 0x1000).map_err(|_| ())?;
|
||||
let base_ptr = bump.alloc_layout(layout).as_ptr();
|
||||
|
||||
for ph in &elf.program_headers {
|
||||
if ph.p_type != PT_LOAD {
|
||||
continue;
|
||||
}
|
||||
|
||||
let file_offset = ph.p_offset as usize;
|
||||
let file_size = ph.p_filesz as usize;
|
||||
let mem_size = ph.p_memsz as usize;
|
||||
let virt_offset = (ph.p_vaddr - base_vaddr) as usize;
|
||||
|
||||
let src = &elf_bytes[file_offset..file_offset + file_size];
|
||||
let dst = unsafe { base_ptr.add(virt_offset) };
|
||||
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(src.as_ptr(), dst, file_size);
|
||||
if mem_size > file_size {
|
||||
core::ptr::write_bytes(dst.add(file_size), 0, mem_size - file_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Patch `call_abi` symbol
|
||||
for sym in elf.syms.iter() {
|
||||
let name = elf.strtab.get_at(sym.st_name).ok_or(())?;
|
||||
if name == "call_abi" && sym.st_bind() == sym::STB_GLOBAL {
|
||||
let offset = (sym.st_value - base_vaddr) as usize;
|
||||
let ptr = unsafe { base_ptr.add(offset) as *mut usize };
|
||||
unsafe { *ptr = call_abi as usize };
|
||||
}
|
||||
}
|
||||
|
||||
// Compute relocated entry point
|
||||
let relocated_entry = unsafe { base_ptr.add((elf.entry - base_vaddr) as usize) };
|
||||
Ok(unsafe { core::mem::transmute(relocated_entry) })
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
mod abi;
|
||||
mod display;
|
||||
mod elf;
|
||||
mod peripherals;
|
||||
mod scsi;
|
||||
mod storage;
|
||||
@@ -14,6 +15,7 @@ mod utils;
|
||||
|
||||
use crate::{
|
||||
display::{display_handler, init_display},
|
||||
elf::load_elf,
|
||||
peripherals::{
|
||||
conf_peripherals,
|
||||
keyboard::{KeyCode, KeyState, read_keyboard_fifo},
|
||||
@@ -22,24 +24,10 @@ use crate::{
|
||||
usb::{ENABLE_SCSI, usb_handler},
|
||||
};
|
||||
|
||||
use defmt::unwrap;
|
||||
use elf_loader::{
|
||||
Loader, load_exec,
|
||||
mmap::MmapImpl,
|
||||
object::{ElfBinary, ElfObject},
|
||||
};
|
||||
use static_cell::StaticCell;
|
||||
use talc::*;
|
||||
|
||||
static mut ARENA: [u8; 10000] = [0; 10000];
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
|
||||
Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) })
|
||||
.lock();
|
||||
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use defmt::unwrap;
|
||||
use embassy_executor::{Executor, Spawner};
|
||||
use embassy_futures::join::join;
|
||||
use embassy_rp::{
|
||||
@@ -56,16 +44,25 @@ use embassy_rp::{
|
||||
use embassy_time::{Delay, Timer};
|
||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||
use embedded_sdmmc::SdCard as SdmmcSdCard;
|
||||
use static_cell::StaticCell;
|
||||
use talc::*;
|
||||
|
||||
embassy_rp::bind_interrupts!(struct Irqs {
|
||||
I2C1_IRQ => i2c::InterruptHandler<I2C1>;
|
||||
USBCTRL_IRQ => embassy_rp_usb::InterruptHandler<USB>;
|
||||
});
|
||||
|
||||
static mut CORE1_STACK: Stack<4096> = Stack::new();
|
||||
static mut CORE1_STACK: Stack<16384> = Stack::new();
|
||||
static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
|
||||
static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
|
||||
|
||||
static mut ARENA: [u8; 50_000] = [0; 50_000];
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: Talck<spin::Mutex<()>, ClaimOnOom> =
|
||||
Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) })
|
||||
.lock();
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
@@ -110,12 +107,14 @@ async fn main(_spawner: Spawner) {
|
||||
// runs dynamically loaded elf files
|
||||
#[embassy_executor::task]
|
||||
async fn userland_task() {
|
||||
let binary_data: &[u8] = include_bytes!("../../example.bin");
|
||||
let bin = load_exec!("example", binary_data).unwrap();
|
||||
let entry = bin.entry();
|
||||
let mut bump = Bump::with_capacity(25_000);
|
||||
|
||||
let entry_fn: extern "C" fn() = unsafe { core::mem::transmute(entry) };
|
||||
entry_fn(); // jump into user code
|
||||
let binary_data: &[u8] =
|
||||
include_bytes!("../../target/thumbv8m.main-none-eabihf/debug/calculator");
|
||||
let entry = load_elf(binary_data, &mut bump).unwrap();
|
||||
|
||||
entry();
|
||||
bump.reset(); // clear heap arena
|
||||
}
|
||||
|
||||
struct Display {
|
||||
|
||||
Reference in New Issue
Block a user