diff --git a/Cargo.lock b/Cargo.lock index 737c75e..25dcb88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1080,7 +1080,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e961b33649994dcf69303af6b3a332c1228549e604d455d61ec5d2ab5e68d3a" dependencies = [ - "log", "plain", "scroll", ] @@ -1830,20 +1829,6 @@ name = "scroll" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" -dependencies = [ - "scroll_derive", -] - -[[package]] -name = "scroll_derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc4f90c27b57691bbaf11d8ecc7cfbfe98a4da6dbe60226115d322aa80c06e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] [[package]] name = "semver" diff --git a/abi_sys/src/lib.rs b/abi_sys/src/lib.rs index 10dea33..45df9da 100644 --- a/abi_sys/src/lib.rs +++ b/abi_sys/src/lib.rs @@ -14,7 +14,7 @@ use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers}; pub type EntryFn = fn() -> Pin>>; #[unsafe(no_mangle)] -#[unsafe(link_section = ".userapp")] +#[unsafe(link_section = ".user_reloc")] pub static mut CALL_ABI_TABLE: [usize; CallAbiTable::COUNT] = [0; CallAbiTable::COUNT]; #[repr(usize)] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 4843e70..2424867 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -79,11 +79,7 @@ static_cell = "2.1.1" bitflags = "2.9.1" heapless = "0.8.0" num_enum = { version = "0.7.4", default-features = false } -goblin = { version = "0.10.0", default-features = false, features = [ - "elf32", - "elf64", - "endian_fd", -] } +goblin = { version = "0.10.0", default-features = false, features = ["elf32"] } talc = "4.4.3" spin = "0.10.0" diff --git a/kernel/src/elf.rs b/kernel/src/elf.rs index 64cb28b..ba63483 100644 --- a/kernel/src/elf.rs +++ b/kernel/src/elf.rs @@ -1,51 +1,22 @@ #![allow(static_mut_refs)] -use crate::{abi, storage::SDCARD}; -use abi_sys::{CallAbiTable, EntryFn}; -use alloc::{boxed::Box, vec::Vec}; -use core::{ - alloc::Layout, - ffi::c_void, - pin::Pin, - ptr::NonNull, - slice::from_raw_parts_mut, - task::{Context, Poll}, + +use crate::{ + abi, + storage::{File, SDCARD}, }; +use abi_sys::{CallAbiTable, EntryFn}; +use alloc::{vec, vec::Vec}; use embedded_sdmmc::ShortFileName; -use goblin::elf::{Elf, header::ET_DYN, program_header::PT_LOAD, sym}; +use goblin::{ + elf::{ + header::header32::Header, + program_header::program_header32::{PT_LOAD, ProgramHeader}, + section_header::SHT_SYMTAB, + }, + elf32::{section_header::SectionHeader, sym::Sym}, +}; -pub async fn read_binary(name: &ShortFileName) -> Option> { - let mut guard = SDCARD.get().lock().await; - let sd = guard.as_mut()?; - - let mut buf = Vec::new(); - - defmt::info!("sd closure"); - sd.access_root_dir(|root_dir| { - // Try to open the file directly by name - defmt::info!("trying to open file: {:?}", name); - if let Ok(file) = root_dir.open_file_in_dir(name, embedded_sdmmc::Mode::ReadOnly) { - defmt::info!("opened"); - let mut temp = [0u8; 512]; - - defmt::info!("caching binary"); - loop { - match file.read(&mut temp) { - Ok(n) if n > 0 => buf.extend_from_slice(&temp[..n]), - _ => break, - } - } - - defmt::info!("done"); - let _ = file.close(); - } - }); - - if buf.is_empty() { - return None; - } - - Some(buf) -} +const ELF32_HDR_SIZE: usize = 52; // userland ram region defined in memory.x unsafe extern "C" { @@ -53,67 +24,181 @@ unsafe extern "C" { static __userapp_end__: u8; } -pub unsafe fn load_binary(bytes: &[u8]) -> Result { - let elf = Elf::parse(&bytes).expect("Failed to parse ELF"); +pub async unsafe fn load_binary(name: &ShortFileName) -> Result { + let mut sd_lock = SDCARD.get().lock().await; + let sd = sd_lock.as_mut().unwrap(); - if elf.is_64 || elf.is_lib || !elf.little_endian { - return Err("Unsupported ELF type"); - } + let mut error = ""; + let mut entry = 0; - for ph in &elf.program_headers { - if ph.p_type == PT_LOAD { - let vaddr = ph.p_vaddr as usize; - let memsz = ph.p_memsz as usize; - let filesz = ph.p_filesz as usize; - let offset = ph.p_offset as usize; + let mut header_buf = [0; ELF32_HDR_SIZE]; - let seg_start = vaddr; - let seg_end = vaddr + memsz; + sd.read_file(name, |mut file| { + file.read(&mut header_buf).unwrap(); + let elf_header = Header::from_bytes(&header_buf); - // Bounds check: make sure segment fits inside payload region - let user_start = unsafe { &__userapp_start__ as *const u8 as usize }; - let user_end = unsafe { &__userapp_end__ as *const u8 as usize }; - if seg_start < user_start || seg_end > user_end { - panic!( - "Segment out of bounds: {:x}..{:x} not within {:x}..{:x}", - seg_start, seg_end, user_start, user_end - ); + let mut program_headers_buf = vec![0_u8; elf_header.e_phentsize as usize]; + for i in 1..=elf_header.e_phnum { + file.seek_from_start(elf_header.e_phoff + (elf_header.e_phentsize * i) as u32) + .unwrap(); + file.read(&mut program_headers_buf).unwrap(); + + let ph = cast_phdr(&program_headers_buf); + + if ph.p_type == PT_LOAD { + load_segment(&mut file, &ph).unwrap() } + } - unsafe { - let dst = seg_start as *mut u8; - let src = bytes.as_ptr().add(offset); + // MUST MATCH ABI EXACTLY + let entries: &[(CallAbiTable, usize)] = &[ + (CallAbiTable::Print, abi::print as usize), + (CallAbiTable::DrawIter, abi::draw_iter as usize), + (CallAbiTable::GetKey, abi::get_key as usize), + ]; + assert!(entries.len() == CallAbiTable::COUNT); - // Copy initialized part - core::ptr::copy_nonoverlapping(src, dst, filesz); + patch_abi(entries, &elf_header, &mut file).unwrap(); - // Zero BSS region (memsz - filesz) - if memsz > filesz { - core::ptr::write_bytes(dst.add(filesz), 0, memsz - filesz); + // TODO: dynamically search for abi table + + entry = elf_header.e_entry as u32; + }) + .await + .unwrap(); + + if entry != 0 { + Ok(unsafe { core::mem::transmute(entry) }) + } else { + Err(error) + } +} + +fn patch_abi( + entries: &[(CallAbiTable, usize)], + elf_header: &Header, + file: &mut File, +) -> Result<(), ()> { + for i in 1..=elf_header.e_shnum { + let sh = read_section(file, &elf_header, i.into()); + + // find the symbol table + if sh.sh_type == SHT_SYMTAB { + let mut symtab_buf = vec![0u8; sh.sh_size as usize]; + file.seek_from_start(sh.sh_offset).unwrap(); + file.read(&mut symtab_buf).unwrap(); + + // Cast buffer into symbols + let sym_count = sh.sh_size as usize / sh.sh_entsize as usize; + for i in 0..sym_count { + let sym_bytes = + &symtab_buf[i * sh.sh_entsize as usize..(i + 1) * sh.sh_entsize as usize]; + let sym = cast_sym(sym_bytes); + + let str_sh = read_section(file, &elf_header, sh.sh_link); + + let mut name = Vec::new(); + file.seek_from_start(str_sh.sh_offset + sym.st_name) + .unwrap(); + + loop { + let mut byte = [0u8; 1]; + file.read(&mut byte).unwrap(); + if byte[0] == 0 { + break; + } + name.push(byte[0]); + } + + let symbol_name = core::str::from_utf8(&name).unwrap(); + if symbol_name == "CALL_ABI_TABLE" { + let table_base = sym.st_value as *mut usize; + + for &(abi_idx, func_ptr) in entries { + unsafe { + table_base.add(abi_idx as usize).write(func_ptr); + } + } + return Ok(()); } } } } + Err(()) +} - let call_abi_sym = elf - .syms - .iter() - .find(|s| elf.strtab.get_at(s.st_name).unwrap() == "CALL_ABI_TABLE") - .expect("syscall table not found"); +fn read_section(file: &mut File, elf_header: &Header, section: u32) -> SectionHeader { + let mut section_header_buf = vec![0_u8; elf_header.e_shentsize as usize]; - let table_base = call_abi_sym.st_value as *mut usize; + file.seek_from_start(elf_header.e_shoff + (elf_header.e_shentsize as u32 * section)) + .unwrap(); + file.read(&mut section_header_buf).unwrap(); - let entries: &[(CallAbiTable, usize)] = &[ - (CallAbiTable::Print, abi::print as usize), - (CallAbiTable::DrawIter, abi::draw_iter as usize), - (CallAbiTable::GetKey, abi::get_key as usize), - ]; - assert!(entries.len() == CallAbiTable::COUNT); + cast_shdr(§ion_header_buf) +} + +fn load_segment(file: &mut File, ph: &ProgramHeader) -> Result<(), ()> { + let dst_start = ph.p_vaddr as *mut u8; + let filesz = ph.p_filesz as usize; + let memsz = ph.p_memsz as usize; + let vaddr = ph.p_vaddr as usize; + let mut remaining = filesz; + let mut dst_ptr = dst_start; + let mut file_offset = ph.p_offset; + + let seg_start = vaddr; + let seg_end = vaddr + memsz; + + // Bounds check: make sure segment fits inside payload region + let user_start = unsafe { &__userapp_start__ as *const u8 as usize }; + let user_end = unsafe { &__userapp_end__ as *const u8 as usize }; + if seg_start < user_start || seg_end > user_end { + panic!( + "Segment out of bounds: {:x}..{:x} not within {:x}..{:x}", + seg_start, seg_end, user_start, user_end + ); + } + + // Buffer for chunked reads (512 bytes is typical SD sector size) + let mut buf = [0u8; 512]; + + while remaining > 0 { + let to_read = core::cmp::min(remaining, buf.len()); + // Read chunk from file + file.seek_from_start(file_offset).unwrap(); + file.read(&mut buf[..to_read]).unwrap(); - for &(abi_idx, func_ptr) in entries { unsafe { - table_base.add(abi_idx as usize).write(func_ptr); + // Copy chunk directly into destination memory + core::ptr::copy_nonoverlapping(buf.as_ptr(), dst_ptr, to_read); + dst_ptr = dst_ptr.add(to_read); + } + + remaining -= to_read; + file_offset += to_read as u32; + } + + // Zero BSS (memsz - filesz) + if memsz > filesz { + unsafe { + core::ptr::write_bytes(dst_ptr, 0, memsz - filesz); } } - Ok(unsafe { core::mem::transmute(elf.entry as u32) }) + + Ok(()) +} + +fn cast_phdr(buf: &[u8]) -> ProgramHeader { + assert!(buf.len() >= core::mem::size_of::()); + unsafe { core::ptr::read(buf.as_ptr() as *const ProgramHeader) } +} + +fn cast_shdr(buf: &[u8]) -> SectionHeader { + assert!(buf.len() >= core::mem::size_of::()); + unsafe { core::ptr::read(buf.as_ptr() as *const SectionHeader) } +} + +fn cast_sym(buf: &[u8]) -> Sym { + assert!(buf.len() >= core::mem::size_of::()); + unsafe { core::ptr::read(buf.as_ptr() as *const Sym) } } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index ca2bbce..e6083dc 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -138,6 +138,7 @@ async fn userland_task() { *state = TaskState::Kernel; } + defmt::info!("Executing Binary"); entry().await; // enable kernel ui diff --git a/kernel/src/storage.rs b/kernel/src/storage.rs index c16a52b..25b82e2 100644 --- a/kernel/src/storage.rs +++ b/kernel/src/storage.rs @@ -12,7 +12,7 @@ use embedded_sdmmc::{ Block, BlockCount, BlockDevice, BlockIdx, Directory, SdCard as SdmmcSdCard, TimeSource, Timestamp, Volume, VolumeIdx, VolumeManager, sdcard::Error, }; -use embedded_sdmmc::{LfnBuffer, ShortFileName}; +use embedded_sdmmc::{File as SdFile, LfnBuffer, Mode, ShortFileName}; pub const MAX_DIRS: usize = 4; pub const MAX_FILES: usize = 5; @@ -23,6 +23,7 @@ type SD = SdmmcSdCard; type VolMgr = VolumeManager; type Vol<'a> = Volume<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; type Dir<'a> = Directory<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; +pub type File<'a> = SdFile<'a, SD, DummyTimeSource, MAX_DIRS, MAX_FILES, MAX_VOLUMES>; pub static SDCARD: LazyLock>> = LazyLock::new(|| Mutex::new(None)); @@ -113,6 +114,22 @@ impl SdCard { access(root_dir); } + pub async fn read_file( + &mut self, + name: &ShortFileName, + mut access: impl FnMut(File), + ) -> Result<(), ()> { + let mut res = Err(()); + self.access_root_dir(|root_dir| { + if let Ok(file) = root_dir.open_file_in_dir(name, Mode::ReadOnly) { + res = Ok(()); + access(file); + } + }); + + res + } + /// Returns a Vec of file names (long format) that match the given extension (e.g., "BIN") pub fn list_files_by_extension(&mut self, ext: &str) -> Result, ()> { let mut result = Vec::new(); diff --git a/kernel/src/ui.rs b/kernel/src/ui.rs index 208fa3f..3485bb0 100644 --- a/kernel/src/ui.rs +++ b/kernel/src/ui.rs @@ -1,6 +1,7 @@ use crate::{ BINARY_CH, TASK_STATE, TaskState, display::{FRAMEBUFFER, SCREEN_HEIGHT, SCREEN_WIDTH}, + elf::load_binary, format, peripherals::keyboard, storage::FileName, @@ -66,14 +67,8 @@ pub async fn ui_handler() { [selections.current_selection as usize - 1] .clone(); - defmt::info!( - "loading selected binary: {:?}", - &selection.long_name.as_str() - ); - let bytes = crate::elf::read_binary(&selection.short_name) - .await - .unwrap(); - let entry = unsafe { crate::elf::load_binary(&bytes).unwrap() }; + let entry = + unsafe { load_binary(&selection.short_name).await.unwrap() }; BINARY_CH.send(entry).await; } _ => (), diff --git a/kernel/src/usb.rs b/kernel/src/usb.rs index b657211..8349652 100644 --- a/kernel/src/usb.rs +++ b/kernel/src/usb.rs @@ -17,7 +17,7 @@ pub static RESTART_USB: Signal = Signal::new(); pub static ENABLE_SCSI: AtomicBool = AtomicBool::new(false); pub async fn usb_handler(driver: Driver<'static, USB>) { - let mut config = Config::new(0xc0de, 0xcafe); + let mut config = Config::new(0xc0de, 0xbabe); config.manufacturer = Some("LegitCamper"); config.product = Some("PicoCalc"); config.serial_number = Some("01001100");