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) ]
612static 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
4149impl 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 {
52130pub struct TransientProgramArea {
53131 memory_bottom : * mut u32 ,
54132 memory_top : * mut u32 ,
133+ last_entry : u32 ,
55134}
56135
57136extern "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