1 // Copyright (c) 2017-2021 Linaro LTD
2 // Copyright (c) 2018-2019 JUUL Labs
3 //
4 // SPDX-License-Identifier: Apache-2.0
5 
6 //! HAL api for MyNewt applications
7 
8 use crate::area::CAreaDesc;
9 use log::{Level, log_enabled, warn};
10 use simflash::{Result, Flash, FlashPtr};
11 use std::{
12     cell::RefCell,
13     collections::HashMap,
14     mem,
15     ptr,
16     slice,
17 };
18 
19 /// A FlashMap maintain a table of [device_id -> Flash trait]
20 pub type FlashMap = HashMap<u8, FlashPtr>;
21 
22 pub struct FlashParamsStruct {
23     align: u32,
24     erased_val: u8,
25 }
26 
27 pub type FlashParams = HashMap<u8, FlashParamsStruct>;
28 
29 /// The `boot_rsp` structure used by boot_go.
30 #[repr(C)]
31 #[derive(Debug)]
32 pub struct BootRsp {
33     pub br_hdr: *const ImageHeader,
34     pub flash_dev_id: u8,
35     pub image_off: u32,
36 }
37 
38 // TODO: Don't duplicate this image header declaration.
39 #[repr(C)]
40 #[derive(Debug)]
41 pub struct ImageHeader {
42     magic: u32,
43     load_addr: u32,
44     hdr_size: u16,
45     protect_tlv_size: u16,
46     img_size: u32,
47     flags: u32,
48     ver: ImageVersion,
49     _pad2: u32,
50 }
51 
52 #[repr(C)]
53 #[derive(Debug)]
54 pub struct ImageVersion {
55     pub major: u8,
56     pub minor: u8,
57     pub revision: u16,
58     pub build_num: u32,
59 }
60 
61 pub struct CAreaDescPtr {
62    pub ptr: *const CAreaDesc,
63 }
64 
65 pub struct FlashContext {
66     flash_map: FlashMap,
67     flash_params: FlashParams,
68     flash_areas: CAreaDescPtr,
69 }
70 
71 impl FlashContext {
new() -> FlashContext72     pub fn new() -> FlashContext {
73         FlashContext {
74             flash_map: HashMap::new(),
75             flash_params: HashMap::new(),
76             flash_areas: CAreaDescPtr{ptr: ptr::null()},
77         }
78     }
79 }
80 
81 impl Default for FlashContext {
default() -> FlashContext82     fn default() -> FlashContext {
83         FlashContext {
84             flash_map: HashMap::new(),
85             flash_params: HashMap::new(),
86             flash_areas: CAreaDescPtr{ptr: ptr::null()},
87         }
88     }
89 }
90 
91 #[repr(C)]
92 #[derive(Debug, Default)]
93 pub struct CSimContext {
94     pub flash_counter: libc::c_int,
95     pub jumped: libc::c_int,
96     pub c_asserts: u8,
97     pub c_catch_asserts: u8,
98     // NOTE: Always leave boot_jmpbuf declaration at the end; this should
99     // store a "jmp_buf" which is arch specific and not defined by libc crate.
100     // The size below is enough to store data on a x86_64 machine.
101     pub boot_jmpbuf: [u64; 16],
102 }
103 
104 pub struct CSimContextPtr {
105    pub ptr: *const CSimContext,
106 }
107 
108 impl CSimContextPtr {
new() -> CSimContextPtr109     pub fn new() -> CSimContextPtr {
110         CSimContextPtr {
111             ptr: ptr::null(),
112         }
113     }
114 }
115 
116 impl Default for CSimContextPtr {
default() -> CSimContextPtr117     fn default() -> CSimContextPtr {
118         CSimContextPtr {
119             ptr: ptr::null(),
120         }
121     }
122 }
123 
124 /// This struct describes the RAM layout of the current device.  It will be stashed, per test
125 /// thread, and queried by the C code.
126 #[repr(C)]
127 #[derive(Debug, Default)]
128 pub struct BootsimRamInfo {
129     pub start: u32,
130     pub size: u32,
131     pub base: usize,
132 }
133 
134 thread_local! {
135     pub static THREAD_CTX: RefCell<FlashContext> = RefCell::new(FlashContext::new());
136     pub static SIM_CTX: RefCell<CSimContextPtr> = RefCell::new(CSimContextPtr::new());
137     pub static RAM_CTX: RefCell<BootsimRamInfo> = RefCell::new(BootsimRamInfo::default());
138 }
139 
140 /// Set the flash device to be used by the simulation.  The pointer is unsafely stashed away.
141 ///
142 /// # Safety
143 ///
144 /// This uses mem::transmute to stash a Rust pointer into a C value to
145 /// retrieve later.  It should be safe to use this.
set_flash(dev_id: u8, dev: &mut dyn Flash)146 pub fn set_flash(dev_id: u8, dev: &mut dyn Flash) {
147     THREAD_CTX.with(|ctx| {
148         ctx.borrow_mut().flash_params.insert(dev_id, FlashParamsStruct {
149             align: dev.align() as u32,
150             erased_val: dev.erased_val(),
151         });
152         unsafe {
153             let dev: &'static mut dyn Flash = mem::transmute(dev);
154             ctx.borrow_mut().flash_map.insert(
155                 dev_id, FlashPtr{ptr: dev as *mut dyn Flash});
156         }
157     });
158 }
159 
clear_flash(dev_id: u8)160 pub fn clear_flash(dev_id: u8) {
161     THREAD_CTX.with(|ctx| {
162         ctx.borrow_mut().flash_map.remove(&dev_id);
163     });
164 }
165 
166 // This isn't meant to call directly, but by a wrapper.
167 
168 #[no_mangle]
sim_get_flash_areas() -> *const CAreaDesc169 pub extern fn sim_get_flash_areas() -> *const CAreaDesc {
170     THREAD_CTX.with(|ctx| {
171         ctx.borrow().flash_areas.ptr
172     })
173 }
174 
175 #[no_mangle]
sim_set_flash_areas(areas: *const CAreaDesc)176 pub extern fn sim_set_flash_areas(areas: *const CAreaDesc) {
177     THREAD_CTX.with(|ctx| {
178         ctx.borrow_mut().flash_areas.ptr = areas;
179     });
180 }
181 
182 #[no_mangle]
sim_reset_flash_areas()183 pub extern fn sim_reset_flash_areas() {
184     THREAD_CTX.with(|ctx| {
185         ctx.borrow_mut().flash_areas.ptr = ptr::null();
186     });
187 }
188 
189 #[no_mangle]
sim_get_context() -> *const CSimContext190 pub extern fn sim_get_context() -> *const CSimContext {
191     SIM_CTX.with(|ctx| {
192         ctx.borrow().ptr
193     })
194 }
195 
196 #[no_mangle]
sim_set_context(ptr: *const CSimContext)197 pub extern fn sim_set_context(ptr: *const CSimContext) {
198     SIM_CTX.with(|ctx| {
199         ctx.borrow_mut().ptr = ptr;
200     });
201 }
202 
203 #[no_mangle]
sim_reset_context()204 pub extern fn sim_reset_context() {
205     SIM_CTX.with(|ctx| {
206         ctx.borrow_mut().ptr = ptr::null();
207     });
208 }
209 
210 #[no_mangle]
bootsim_get_ram_info() -> *const BootsimRamInfo211 pub extern "C" fn bootsim_get_ram_info() -> *const BootsimRamInfo {
212     RAM_CTX.with(|ctx| {
213         if ctx.borrow().base == 0 {
214             // Option is messier to get a pointer out of, so just check if the base has been set to
215             // anything.
216             panic!("ram info not set, but being used");
217         }
218         ctx.as_ptr()
219     })
220 }
221 
222 /// Store a copy of this RAM info.
set_ram_info(info: BootsimRamInfo)223 pub fn set_ram_info(info: BootsimRamInfo) {
224     RAM_CTX.with(|ctx| {
225         ctx.replace(info);
226     });
227 }
228 
229 /// Clear out the ram info.
clear_ram_info()230 pub fn clear_ram_info() {
231     RAM_CTX.with(|ctx| {
232         ctx.borrow_mut().base = 0;
233     });
234 }
235 
236 #[no_mangle]
sim_flash_erase(dev_id: u8, offset: u32, size: u32) -> libc::c_int237 pub extern fn sim_flash_erase(dev_id: u8, offset: u32, size: u32) -> libc::c_int {
238     let mut rc: libc::c_int = -19;
239     THREAD_CTX.with(|ctx| {
240         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
241             let dev = unsafe { &mut *(flash.ptr) };
242             rc = map_err(dev.erase(offset as usize, size as usize));
243         }
244     });
245     rc
246 }
247 
248 #[no_mangle]
sim_flash_read(dev_id: u8, offset: u32, dest: *mut u8, size: u32) -> libc::c_int249 pub extern fn sim_flash_read(dev_id: u8, offset: u32, dest: *mut u8, size: u32) -> libc::c_int {
250     let mut rc: libc::c_int = -19;
251     THREAD_CTX.with(|ctx| {
252         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
253             let mut buf: &mut[u8] = unsafe { slice::from_raw_parts_mut(dest, size as usize) };
254             let dev = unsafe { &mut *(flash.ptr) };
255             rc = map_err(dev.read(offset as usize, &mut buf));
256         }
257     });
258     rc
259 }
260 
261 #[no_mangle]
sim_flash_write(dev_id: u8, offset: u32, src: *const u8, size: u32) -> libc::c_int262 pub extern fn sim_flash_write(dev_id: u8, offset: u32, src: *const u8, size: u32) -> libc::c_int {
263     let mut rc: libc::c_int = -19;
264     THREAD_CTX.with(|ctx| {
265         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
266             let buf: &[u8] = unsafe { slice::from_raw_parts(src, size as usize) };
267             let dev = unsafe { &mut *(flash.ptr) };
268             rc = map_err(dev.write(offset as usize, &buf));
269         }
270     });
271     rc
272 }
273 
274 #[no_mangle]
sim_flash_align(id: u8) -> u32275 pub extern fn sim_flash_align(id: u8) -> u32 {
276     THREAD_CTX.with(|ctx| {
277         ctx.borrow().flash_params.get(&id).unwrap().align
278     })
279 }
280 
281 #[no_mangle]
sim_flash_erased_val(id: u8) -> u8282 pub extern fn sim_flash_erased_val(id: u8) -> u8 {
283     THREAD_CTX.with(|ctx| {
284         ctx.borrow().flash_params.get(&id).unwrap().erased_val
285     })
286 }
287 
map_err(err: Result<()>) -> libc::c_int288 fn map_err(err: Result<()>) -> libc::c_int {
289     match err {
290         Ok(()) => 0,
291         Err(e) => {
292             warn!("{}", e);
293             -1
294         },
295     }
296 }
297 
298 /// Called by C code to determine if we should log at this level.  Levels are defined in
299 /// bootutil/bootutil_log.h.  This makes the logging from the C code controlled by bootsim::api, so
300 /// for example, it can be enabled with something like:
301 ///     RUST_LOG=bootsim::api=info cargo run --release runall
302 /// or
303 ///     RUST_LOG=bootsim=info cargo run --release runall
304 #[no_mangle]
sim_log_enabled(level: libc::c_int) -> libc::c_int305 pub extern fn sim_log_enabled(level: libc::c_int) -> libc::c_int {
306     let res = match level {
307         1 => log_enabled!(Level::Error),
308         2 => log_enabled!(Level::Warn),
309         3 => log_enabled!(Level::Info),
310         4 => log_enabled!(Level::Debug),
311         5 => log_enabled!(Level::Trace), // log level == SIM
312         _ => false,
313     };
314     if res {
315         1
316     } else {
317         0
318     }
319 }
320