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) ]
611static 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
4148impl 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 {
52142pub struct TransientProgramArea {
53143 memory_bottom : * mut u32 ,
54144 memory_top : * mut u32 ,
145+ last_entry : u32 ,
55146}
56147
57148extern "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