1 // Copyright (c) 2017-2019 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: u16,
24     erased_val: u8,
25 }
26 
27 pub type FlashParams = HashMap<u8, FlashParamsStruct>;
28 
29 pub struct CAreaDescPtr {
30    pub ptr: *const CAreaDesc,
31 }
32 
33 pub struct FlashContext {
34     flash_map: FlashMap,
35     flash_params: FlashParams,
36     flash_areas: CAreaDescPtr,
37 }
38 
39 impl FlashContext {
new() -> FlashContext40     pub fn new() -> FlashContext {
41         FlashContext {
42             flash_map: HashMap::new(),
43             flash_params: HashMap::new(),
44             flash_areas: CAreaDescPtr{ptr: ptr::null()},
45         }
46     }
47 }
48 
49 impl Default for FlashContext {
default() -> FlashContext50     fn default() -> FlashContext {
51         FlashContext {
52             flash_map: HashMap::new(),
53             flash_params: HashMap::new(),
54             flash_areas: CAreaDescPtr{ptr: ptr::null()},
55         }
56     }
57 }
58 
59 #[repr(C)]
60 #[derive(Debug, Default)]
61 pub struct CSimContext {
62     pub flash_counter: libc::c_int,
63     pub jumped: libc::c_int,
64     pub c_asserts: u8,
65     pub c_catch_asserts: u8,
66     // NOTE: Always leave boot_jmpbuf declaration at the end; this should
67     // store a "jmp_buf" which is arch specific and not defined by libc crate.
68     // The size below is enough to store data on a x86_64 machine.
69     pub boot_jmpbuf: [u64; 16],
70 }
71 
72 pub struct CSimContextPtr {
73    pub ptr: *const CSimContext,
74 }
75 
76 impl CSimContextPtr {
new() -> CSimContextPtr77     pub fn new() -> CSimContextPtr {
78         CSimContextPtr {
79             ptr: ptr::null(),
80         }
81     }
82 }
83 
84 impl Default for CSimContextPtr {
default() -> CSimContextPtr85     fn default() -> CSimContextPtr {
86         CSimContextPtr {
87             ptr: ptr::null(),
88         }
89     }
90 }
91 
92 thread_local! {
93     pub static THREAD_CTX: RefCell<FlashContext> = RefCell::new(FlashContext::new());
94     pub static SIM_CTX: RefCell<CSimContextPtr> = RefCell::new(CSimContextPtr::new());
95 }
96 
97 /// Set the flash device to be used by the simulation.  The pointer is unsafely stashed away.
98 ///
99 /// # Safety
100 ///
101 /// This uses mem::transmute to stash a Rust pointer into a C value to
102 /// retrieve later.  It should be safe to use this.
set_flash(dev_id: u8, dev: &mut dyn Flash)103 pub fn set_flash(dev_id: u8, dev: &mut dyn Flash) {
104     THREAD_CTX.with(|ctx| {
105         ctx.borrow_mut().flash_params.insert(dev_id, FlashParamsStruct {
106             align: dev.align() as u16,
107             erased_val: dev.erased_val(),
108         });
109         unsafe {
110             let dev: &'static mut dyn Flash = mem::transmute(dev);
111             ctx.borrow_mut().flash_map.insert(
112                 dev_id, FlashPtr{ptr: dev as *mut dyn Flash});
113         }
114     });
115 }
116 
clear_flash(dev_id: u8)117 pub fn clear_flash(dev_id: u8) {
118     THREAD_CTX.with(|ctx| {
119         ctx.borrow_mut().flash_map.remove(&dev_id);
120     });
121 }
122 
123 // This isn't meant to call directly, but by a wrapper.
124 
125 #[no_mangle]
sim_get_flash_areas() -> *const CAreaDesc126 pub extern fn sim_get_flash_areas() -> *const CAreaDesc {
127     THREAD_CTX.with(|ctx| {
128         ctx.borrow().flash_areas.ptr
129     })
130 }
131 
132 #[no_mangle]
sim_set_flash_areas(areas: *const CAreaDesc)133 pub extern fn sim_set_flash_areas(areas: *const CAreaDesc) {
134     THREAD_CTX.with(|ctx| {
135         ctx.borrow_mut().flash_areas.ptr = areas;
136     });
137 }
138 
139 #[no_mangle]
sim_reset_flash_areas()140 pub extern fn sim_reset_flash_areas() {
141     THREAD_CTX.with(|ctx| {
142         ctx.borrow_mut().flash_areas.ptr = ptr::null();
143     });
144 }
145 
146 #[no_mangle]
sim_get_context() -> *const CSimContext147 pub extern fn sim_get_context() -> *const CSimContext {
148     SIM_CTX.with(|ctx| {
149         ctx.borrow().ptr
150     })
151 }
152 
153 #[no_mangle]
sim_set_context(ptr: *const CSimContext)154 pub extern fn sim_set_context(ptr: *const CSimContext) {
155     SIM_CTX.with(|ctx| {
156         ctx.borrow_mut().ptr = ptr;
157     });
158 }
159 
160 #[no_mangle]
sim_reset_context()161 pub extern fn sim_reset_context() {
162     SIM_CTX.with(|ctx| {
163         ctx.borrow_mut().ptr = ptr::null();
164     });
165 }
166 
167 #[no_mangle]
sim_flash_erase(dev_id: u8, offset: u32, size: u32) -> libc::c_int168 pub extern fn sim_flash_erase(dev_id: u8, offset: u32, size: u32) -> libc::c_int {
169     let mut rc: libc::c_int = -19;
170     THREAD_CTX.with(|ctx| {
171         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
172             let dev = unsafe { &mut *(flash.ptr) };
173             rc = map_err(dev.erase(offset as usize, size as usize));
174         }
175     });
176     rc
177 }
178 
179 #[no_mangle]
sim_flash_read(dev_id: u8, offset: u32, dest: *mut u8, size: u32) -> libc::c_int180 pub extern fn sim_flash_read(dev_id: u8, offset: u32, dest: *mut u8, size: u32) -> libc::c_int {
181     let mut rc: libc::c_int = -19;
182     THREAD_CTX.with(|ctx| {
183         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
184             let mut buf: &mut[u8] = unsafe { slice::from_raw_parts_mut(dest, size as usize) };
185             let dev = unsafe { &mut *(flash.ptr) };
186             rc = map_err(dev.read(offset as usize, &mut buf));
187         }
188     });
189     rc
190 }
191 
192 #[no_mangle]
sim_flash_write(dev_id: u8, offset: u32, src: *const u8, size: u32) -> libc::c_int193 pub extern fn sim_flash_write(dev_id: u8, offset: u32, src: *const u8, size: u32) -> libc::c_int {
194     let mut rc: libc::c_int = -19;
195     THREAD_CTX.with(|ctx| {
196         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
197             let buf: &[u8] = unsafe { slice::from_raw_parts(src, size as usize) };
198             let dev = unsafe { &mut *(flash.ptr) };
199             rc = map_err(dev.write(offset as usize, &buf));
200         }
201     });
202     rc
203 }
204 
205 #[no_mangle]
sim_flash_align(id: u8) -> u16206 pub extern fn sim_flash_align(id: u8) -> u16 {
207     THREAD_CTX.with(|ctx| {
208         ctx.borrow().flash_params.get(&id).unwrap().align
209     })
210 }
211 
212 #[no_mangle]
sim_flash_erased_val(id: u8) -> u8213 pub extern fn sim_flash_erased_val(id: u8) -> u8 {
214     THREAD_CTX.with(|ctx| {
215         ctx.borrow().flash_params.get(&id).unwrap().erased_val
216     })
217 }
218 
map_err(err: Result<()>) -> libc::c_int219 fn map_err(err: Result<()>) -> libc::c_int {
220     match err {
221         Ok(()) => 0,
222         Err(e) => {
223             warn!("{}", e);
224             -1
225         },
226     }
227 }
228 
229 /// Called by C code to determine if we should log at this level.  Levels are defined in
230 /// bootutil/bootutil_log.h.  This makes the logging from the C code controlled by bootsim::api, so
231 /// for example, it can be enabled with something like:
232 ///     RUST_LOG=bootsim::api=info cargo run --release runall
233 /// or
234 ///     RUST_LOG=bootsim=info cargo run --release runall
235 #[no_mangle]
sim_log_enabled(level: libc::c_int) -> libc::c_int236 pub extern fn sim_log_enabled(level: libc::c_int) -> libc::c_int {
237     let res = match level {
238         1 => log_enabled!(Level::Error),
239         2 => log_enabled!(Level::Warn),
240         3 => log_enabled!(Level::Info),
241         4 => log_enabled!(Level::Debug),
242         5 => log_enabled!(Level::Trace), // log level == SIM
243         _ => false,
244     };
245     if res {
246         1
247     } else {
248         0
249     }
250 }
251