Skip to content

Commit afe34d6

Browse files
authored
Merge pull request #49 from Neotron-Compute/use-elf-loader
Use ELF executables
2 parents 8ec18a1 + 4c46861 commit afe34d6

File tree

3 files changed

+136
-32
lines changed

3 files changed

+136
-32
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ menu = "0.3"
5050
chrono = { version = "0.4", default-features = false }
5151
embedded-sdmmc = { version = "0.5", default-features = false }
5252
neotron-api = "0.1"
53+
neotron-loader = "0.1"
5354

5455
[features]
5556
lib-mode = []

src/program.rs

Lines changed: 128 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
//! Program Loading and Execution
22
3-
use crate::{print, println};
3+
use embedded_sdmmc::File;
4+
5+
use crate::{
6+
fs::{BiosBlock, BiosTime},
7+
print, println,
8+
};
49

510
#[allow(unused)]
611
static CALLBACK_TABLE: neotron_api::Api = neotron_api::Api {
@@ -34,8 +39,10 @@ pub enum Error {
3439
ProgramTooLarge,
3540
/// A filesystem error occurred
3641
Filesystem(embedded_sdmmc::Error<neotron_common_bios::Error>),
37-
/// Start Address didn't look right
38-
BadAddress(u32),
42+
/// An ELF error occurred
43+
Elf(neotron_loader::Error<embedded_sdmmc::Error<neotron_common_bios::Error>>),
44+
/// Tried to run when nothing was loaded
45+
NothingLoaded,
3946
}
4047

4148
impl From<embedded_sdmmc::Error<neotron_common_bios::Error>> for Error {
@@ -44,6 +51,89 @@ impl From<embedded_sdmmc::Error<neotron_common_bios::Error>> for Error {
4451
}
4552
}
4653

54+
impl From<neotron_loader::Error<embedded_sdmmc::Error<neotron_common_bios::Error>>> for Error {
55+
fn from(
56+
value: neotron_loader::Error<embedded_sdmmc::Error<neotron_common_bios::Error>>,
57+
) -> Self {
58+
Error::Elf(value)
59+
}
60+
}
61+
62+
/// Something the ELF loader can use to get bytes off the disk
63+
struct FileSource {
64+
mgr: core::cell::RefCell<embedded_sdmmc::VolumeManager<BiosBlock, BiosTime>>,
65+
volume: embedded_sdmmc::Volume,
66+
file: core::cell::RefCell<File>,
67+
buffer: core::cell::RefCell<[u8; Self::BUFFER_LEN]>,
68+
offset_cached: core::cell::Cell<Option<u32>>,
69+
}
70+
71+
impl FileSource {
72+
const BUFFER_LEN: usize = 128;
73+
74+
fn new(
75+
mgr: embedded_sdmmc::VolumeManager<BiosBlock, BiosTime>,
76+
volume: embedded_sdmmc::Volume,
77+
file: File,
78+
) -> FileSource {
79+
FileSource {
80+
mgr: core::cell::RefCell::new(mgr),
81+
file: core::cell::RefCell::new(file),
82+
volume,
83+
buffer: core::cell::RefCell::new([0u8; 128]),
84+
offset_cached: core::cell::Cell::new(None),
85+
}
86+
}
87+
88+
fn uncached_read(
89+
&self,
90+
offset: u32,
91+
out_buffer: &mut [u8],
92+
) -> Result<(), embedded_sdmmc::Error<neotron_common_bios::Error>> {
93+
println!("Reading from {}", offset);
94+
self.file.borrow_mut().seek_from_start(offset).unwrap();
95+
self.mgr
96+
.borrow_mut()
97+
.read(&self.volume, &mut self.file.borrow_mut(), out_buffer)?;
98+
Ok(())
99+
}
100+
}
101+
102+
impl neotron_loader::traits::Source for &FileSource {
103+
type Error = embedded_sdmmc::Error<neotron_common_bios::Error>;
104+
105+
fn read(&self, mut offset: u32, out_buffer: &mut [u8]) -> Result<(), Self::Error> {
106+
for chunk in out_buffer.chunks_mut(FileSource::BUFFER_LEN) {
107+
if let Some(offset_cached) = self.offset_cached.get() {
108+
let cached_range = offset_cached..offset_cached + FileSource::BUFFER_LEN as u32;
109+
if cached_range.contains(&offset)
110+
&& cached_range.contains(&(offset + chunk.len() as u32 - 1))
111+
{
112+
// Do a fast copy from the cache
113+
let start = (offset - offset_cached) as usize;
114+
let end = start + chunk.len();
115+
chunk.copy_from_slice(&self.buffer.borrow()[start..end]);
116+
return Ok(());
117+
}
118+
}
119+
120+
println!("Reading from {}", offset);
121+
self.file.borrow_mut().seek_from_start(offset).unwrap();
122+
self.mgr.borrow_mut().read(
123+
&self.volume,
124+
&mut self.file.borrow_mut(),
125+
self.buffer.borrow_mut().as_mut_slice(),
126+
)?;
127+
self.offset_cached.set(Some(offset));
128+
chunk.copy_from_slice(&self.buffer.borrow()[0..chunk.len()]);
129+
130+
offset += chunk.len() as u32;
131+
}
132+
133+
Ok(())
134+
}
135+
}
136+
47137
/// Represents the Transient Program Area.
48138
///
49139
/// This is a piece of memory that can be used for loading and executing programs.
@@ -52,6 +142,7 @@ impl From<embedded_sdmmc::Error<neotron_common_bios::Error>> for Error {
52142
pub struct TransientProgramArea {
53143
memory_bottom: *mut u32,
54144
memory_top: *mut u32,
145+
last_entry: u32,
55146
}
56147

57148
extern "C" {
@@ -65,6 +156,7 @@ impl TransientProgramArea {
65156
let mut tpa = TransientProgramArea {
66157
memory_bottom: start,
67158
memory_top: start.add(length_in_bytes / core::mem::size_of::<u32>()),
159+
last_entry: 0,
68160
};
69161

70162
// You have to take the address of a linker symbol to find out where
@@ -120,19 +212,38 @@ impl TransientProgramArea {
120212
// Open the first partition
121213
let mut volume = mgr.get_volume(embedded_sdmmc::VolumeIdx(0))?;
122214
let root_dir = mgr.open_root_dir(&volume)?;
123-
let mut file = mgr.open_file_in_dir(
215+
let file = mgr.open_file_in_dir(
124216
&mut volume,
125217
&root_dir,
126218
file_name,
127219
embedded_sdmmc::Mode::ReadOnly,
128220
)?;
129-
// Application space starts 4K into Cortex-M SRAM
130-
let application_ram = self.as_slice_u8();
131-
if file.length() as usize > application_ram.len() {
132-
return Err(Error::ProgramTooLarge);
133-
};
134-
let application_ram = &mut application_ram[0..file.length() as usize];
135-
mgr.read(&volume, &mut file, application_ram)?;
221+
222+
let source = FileSource::new(mgr, volume, file);
223+
let loader = neotron_loader::Loader::new(&source)?;
224+
225+
let mut iter = loader.iter_program_headers();
226+
while let Some(Ok(ph)) = iter.next() {
227+
if ph.p_vaddr() as *mut u32 >= self.memory_bottom
228+
&& ph.p_type() == neotron_loader::ProgramHeader::PT_LOAD
229+
{
230+
println!("Loading {} bytes to 0x{:08x}", ph.p_memsz(), ph.p_vaddr());
231+
let ram = unsafe {
232+
core::slice::from_raw_parts_mut(ph.p_vaddr() as *mut u8, ph.p_memsz() as usize)
233+
};
234+
// Zero all of it.
235+
for b in ram.iter_mut() {
236+
*b = 0;
237+
}
238+
// Replace some of those zeros with bytes from disk.
239+
if ph.p_filesz() != 0 {
240+
source.uncached_read(ph.p_offset(), &mut ram[0..ph.p_filesz() as usize])?;
241+
}
242+
}
243+
}
244+
245+
self.last_entry = loader.e_entry();
246+
136247
Ok(())
137248
}
138249

@@ -156,32 +267,17 @@ impl TransientProgramArea {
156267
/// of view of this API. You wanted to run a program, and the program was
157268
/// run.
158269
pub fn execute(&mut self) -> Result<i32, Error> {
159-
// Read start-ptr as a 32-bit value
160-
let application_ram = self.as_slice_u32();
161-
let start_addr = application_ram[0];
162-
// But now we want RAM as u8 values, as start_ptr will be an odd number
163-
// because it's a Thumb2 address and that's a u16 aligned value, plus 1
164-
// to indicate Thumb2 mode.
165-
let application_ram = self.as_slice_u8();
166-
print!("Start address 0x{:08x}:", start_addr);
167-
// Does this start pointer look OK?
168-
if (start_addr & 1) != 1 {
169-
println!("not thumb2 func");
170-
return Err(Error::BadAddress(start_addr));
171-
}
172-
if !application_ram
173-
.as_ptr_range()
174-
.contains(&(start_addr as *const u8))
175-
{
176-
println!("out of bounds");
177-
return Err(Error::BadAddress(start_addr));
270+
if self.last_entry == 0 {
271+
return Err(Error::NothingLoaded);
178272
}
179-
println!("OK!");
273+
180274
let result = unsafe {
181275
let code: extern "C" fn(*const neotron_api::Api) -> i32 =
182-
::core::mem::transmute(start_addr as *const ());
276+
::core::mem::transmute(self.last_entry as *const ());
183277
code(&CALLBACK_TABLE)
184278
};
279+
280+
self.last_entry = 0;
185281
Ok(result)
186282
}
187283
}

0 commit comments

Comments
 (0)