Skip to content

Commit 34dc28e

Browse files
committed
Use the neotron_loader.
Can load ELF files from disk.
1 parent 8ec18a1 commit 34dc28e

File tree

3 files changed

+117
-32
lines changed

3 files changed

+117
-32
lines changed

Cargo.lock

Lines changed: 6 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 = { git = "https://github.com/neotron-compute/neotron-loader" }
5354

5455
[features]
5556
lib-mode = []

src/program.rs

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

511
#[allow(unused)]
612
static CALLBACK_TABLE: neotron_api::Api = neotron_api::Api {
@@ -34,8 +40,10 @@ pub enum Error {
3440
ProgramTooLarge,
3541
/// A filesystem error occurred
3642
Filesystem(embedded_sdmmc::Error<neotron_common_bios::Error>),
37-
/// Start Address didn't look right
38-
BadAddress(u32),
43+
/// An ELF error occurred
44+
Elf(neotron_loader::Error<embedded_sdmmc::Error<neotron_common_bios::Error>>),
45+
/// Tried to run when nothing was loaded
46+
NothingLoaded,
3947
}
4048

4149
impl From<embedded_sdmmc::Error<neotron_common_bios::Error>> for Error {
@@ -44,6 +52,76 @@ impl From<embedded_sdmmc::Error<neotron_common_bios::Error>> for Error {
4452
}
4553
}
4654

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

57136
extern "C" {
@@ -65,6 +144,7 @@ impl TransientProgramArea {
65144
let mut tpa = TransientProgramArea {
66145
memory_bottom: start,
67146
memory_top: start.add(length_in_bytes / core::mem::size_of::<u32>()),
147+
last_entry: 0,
68148
};
69149

70150
// You have to take the address of a linker symbol to find out where
@@ -120,19 +200,32 @@ impl TransientProgramArea {
120200
// Open the first partition
121201
let mut volume = mgr.get_volume(embedded_sdmmc::VolumeIdx(0))?;
122202
let root_dir = mgr.open_root_dir(&volume)?;
123-
let mut file = mgr.open_file_in_dir(
203+
let file = mgr.open_file_in_dir(
124204
&mut volume,
125205
&root_dir,
126206
file_name,
127207
embedded_sdmmc::Mode::ReadOnly,
128208
)?;
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)?;
209+
210+
let source = FileSource::new(mgr, volume, file);
211+
let loader = neotron_loader::Loader::new(&source)?;
212+
213+
let mut iter = loader.iter_program_headers();
214+
while let Some(Ok(ph)) = iter.next() {
215+
if ph.p_vaddr() >= 0x2000_1000 {
216+
println!("Loading {} bytes to 0x{:08x}", ph.p_memsz(), ph.p_vaddr());
217+
let ram = unsafe {
218+
core::slice::from_raw_parts_mut(ph.p_vaddr() as *mut u8, ph.p_memsz() as usize)
219+
};
220+
for b in ram.iter_mut() {
221+
*b = 0;
222+
}
223+
(&source).read(ph.p_offset(), &mut ram[0..ph.p_filesz() as usize])?;
224+
}
225+
}
226+
227+
self.last_entry = loader.e_entry();
228+
136229
Ok(())
137230
}
138231

@@ -156,32 +249,17 @@ impl TransientProgramArea {
156249
/// of view of this API. You wanted to run a program, and the program was
157250
/// run.
158251
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));
252+
if self.last_entry == 0 {
253+
return Err(Error::NothingLoaded);
171254
}
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));
178-
}
179-
println!("OK!");
255+
180256
let result = unsafe {
181257
let code: extern "C" fn(*const neotron_api::Api) -> i32 =
182-
::core::mem::transmute(start_addr as *const ());
258+
::core::mem::transmute(self.last_entry as *const ());
183259
code(&CALLBACK_TABLE)
184260
};
261+
262+
self.last_entry = 0;
185263
Ok(result)
186264
}
187265
}

0 commit comments

Comments
 (0)