1 // Copyright (c) 2017-2021 Linaro LTD
2 // Copyright (c) 2018-2019 JUUL Labs
3 // Copyright (c) 2023 Arm Limited
4 //
5 // SPDX-License-Identifier: Apache-2.0
6 
7 //! HAL api for MyNewt applications
8 
9 use crate::area::CAreaDesc;
10 use log::{Level, log_enabled, warn};
11 use simflash::{Result, Flash, FlashPtr};
12 use std::{
13     cell::RefCell,
14     collections::HashMap,
15     mem,
16     ptr,
17     slice,
18 };
19 
20 /// A FlashMap maintain a table of [device_id -> Flash trait]
21 pub type FlashMap = HashMap<u8, FlashPtr>;
22 
23 pub struct FlashParamsStruct {
24     align: u32,
25     erased_val: u8,
26 }
27 
28 pub type FlashParams = HashMap<u8, FlashParamsStruct>;
29 
30 /// The `boot_rsp` structure used by boot_go.
31 #[repr(C)]
32 #[derive(Debug)]
33 pub struct BootRsp {
34     pub br_hdr: *const ImageHeader,
35     pub flash_dev_id: u8,
36     pub image_off: u32,
37 }
38 
39 // TODO: Don't duplicate this image header declaration.
40 #[repr(C)]
41 #[derive(Debug)]
42 pub struct ImageHeader {
43     magic: u32,
44     load_addr: u32,
45     hdr_size: u16,
46     protect_tlv_size: u16,
47     img_size: u32,
48     flags: u32,
49     ver: ImageVersion,
50     _pad2: u32,
51 }
52 
53 #[repr(C)]
54 #[derive(Debug)]
55 pub struct ImageVersion {
56     pub major: u8,
57     pub minor: u8,
58     pub revision: u16,
59     pub build_num: u32,
60 }
61 
62 pub struct CAreaDescPtr {
63    pub ptr: *const CAreaDesc,
64 }
65 
66 pub struct FlashContext {
67     flash_map: FlashMap,
68     flash_params: FlashParams,
69     flash_areas: CAreaDescPtr,
70 }
71 
72 impl FlashContext {
new() -> FlashContext73     pub fn new() -> FlashContext {
74         FlashContext {
75             flash_map: HashMap::new(),
76             flash_params: HashMap::new(),
77             flash_areas: CAreaDescPtr{ptr: ptr::null()},
78         }
79     }
80 }
81 
82 impl Default for FlashContext {
default() -> FlashContext83     fn default() -> FlashContext {
84         FlashContext {
85             flash_map: HashMap::new(),
86             flash_params: HashMap::new(),
87             flash_areas: CAreaDescPtr{ptr: ptr::null()},
88         }
89     }
90 }
91 
92 #[repr(C)]
93 #[derive(Debug)]
94 pub struct CSimContext {
95     pub flash_counter: libc::c_int,
96     pub jumped: libc::c_int,
97     pub c_asserts: u8,
98     pub c_catch_asserts: u8,
99     // NOTE: Always leave boot_jmpbuf declaration at the end; this should
100     // store a "jmp_buf" which is arch specific and not defined by libc crate.
101     // The size below is enough to store data on a x86_64 machine.
102     pub boot_jmpbuf: [u64; 48],
103 }
104 
105 impl Default for CSimContext {
default() -> Self106     fn default() -> Self {
107         CSimContext {
108             flash_counter: 0,
109             jumped: 0,
110             c_asserts: 0,
111             c_catch_asserts: 0,
112             boot_jmpbuf: [0; 48],
113         }
114     }
115 }
116 
117 pub struct CSimContextPtr {
118    pub ptr: *const CSimContext,
119 }
120 
121 impl CSimContextPtr {
new() -> CSimContextPtr122     pub fn new() -> CSimContextPtr {
123         CSimContextPtr {
124             ptr: ptr::null(),
125         }
126     }
127 }
128 
129 impl Default for CSimContextPtr {
default() -> CSimContextPtr130     fn default() -> CSimContextPtr {
131         CSimContextPtr {
132             ptr: ptr::null(),
133         }
134     }
135 }
136 
137 /// This struct describes the RAM layout of the current device.  It will be stashed, per test
138 /// thread, and queried by the C code.
139 #[repr(C)]
140 #[derive(Debug, Default)]
141 pub struct BootsimRamInfo {
142     pub start: u32,
143     pub size: u32,
144     pub base: usize,
145 }
146 
147 /// This struct stores the non-volatile security counter per image. It will be stored per test thread,
148 /// and the C code will set / get the values here.
149 #[repr(C)]
150 #[derive(Debug, Default)]
151 pub struct NvCounterStorage {
152     pub storage: Vec<u32>,
153 }
154 
155 impl NvCounterStorage {
new() -> Self156     pub fn new() -> Self {
157         let count = if cfg!(feature = "multiimage") {
158             2
159         } else {
160             1
161         };
162         Self {
163             storage: vec![0; count]
164         }
165     }
166 }
167 
168 thread_local! {
169     pub static THREAD_CTX: RefCell<FlashContext> = RefCell::new(FlashContext::new());
170     pub static SIM_CTX: RefCell<CSimContextPtr> = RefCell::new(CSimContextPtr::new());
171     pub static RAM_CTX: RefCell<BootsimRamInfo> = RefCell::new(BootsimRamInfo::default());
172     pub static NV_COUNTER_CTX: RefCell<NvCounterStorage> = RefCell::new(NvCounterStorage::new());
173 }
174 
175 /// Set the flash device to be used by the simulation.  The pointer is unsafely stashed away.
176 ///
177 /// # Safety
178 ///
179 /// This uses mem::transmute to stash a Rust pointer into a C value to
180 /// retrieve later.  It should be safe to use this.
set_flash(dev_id: u8, dev: &mut dyn Flash)181 pub fn set_flash(dev_id: u8, dev: &mut dyn Flash) {
182     THREAD_CTX.with(|ctx| {
183         ctx.borrow_mut().flash_params.insert(dev_id, FlashParamsStruct {
184             align: dev.align() as u32,
185             erased_val: dev.erased_val(),
186         });
187         unsafe {
188             let dev: &'static mut dyn Flash = mem::transmute(dev);
189             ctx.borrow_mut().flash_map.insert(
190                 dev_id, FlashPtr{ptr: dev as *mut dyn Flash});
191         }
192     });
193 }
194 
clear_flash(dev_id: u8)195 pub fn clear_flash(dev_id: u8) {
196     THREAD_CTX.with(|ctx| {
197         ctx.borrow_mut().flash_map.remove(&dev_id);
198     });
199 }
200 
201 // This isn't meant to call directly, but by a wrapper.
202 
203 #[no_mangle]
sim_get_flash_areas() -> *const CAreaDesc204 pub extern "C" fn sim_get_flash_areas() -> *const CAreaDesc {
205     THREAD_CTX.with(|ctx| {
206         ctx.borrow().flash_areas.ptr
207     })
208 }
209 
210 #[no_mangle]
sim_set_flash_areas(areas: *const CAreaDesc)211 pub extern "C" fn sim_set_flash_areas(areas: *const CAreaDesc) {
212     THREAD_CTX.with(|ctx| {
213         ctx.borrow_mut().flash_areas.ptr = areas;
214     });
215 }
216 
217 #[no_mangle]
sim_reset_flash_areas()218 pub extern "C" fn sim_reset_flash_areas() {
219     THREAD_CTX.with(|ctx| {
220         ctx.borrow_mut().flash_areas.ptr = ptr::null();
221     });
222 }
223 
224 #[no_mangle]
sim_get_context() -> *const CSimContext225 pub extern "C" fn sim_get_context() -> *const CSimContext {
226     SIM_CTX.with(|ctx| {
227         ctx.borrow().ptr
228     })
229 }
230 
231 #[no_mangle]
sim_set_context(ptr: *const CSimContext)232 pub extern "C" fn sim_set_context(ptr: *const CSimContext) {
233     SIM_CTX.with(|ctx| {
234         ctx.borrow_mut().ptr = ptr;
235     });
236 }
237 
238 #[no_mangle]
sim_reset_context()239 pub extern "C" fn sim_reset_context() {
240     SIM_CTX.with(|ctx| {
241         ctx.borrow_mut().ptr = ptr::null();
242     });
243 }
244 
245 #[no_mangle]
bootsim_get_ram_info() -> *const BootsimRamInfo246 pub extern "C" fn bootsim_get_ram_info() -> *const BootsimRamInfo {
247     RAM_CTX.with(|ctx| {
248         if ctx.borrow().base == 0 {
249             // Option is messier to get a pointer out of, so just check if the base has been set to
250             // anything.
251             panic!("ram info not set, but being used");
252         }
253         ctx.as_ptr()
254     })
255 }
256 
257 /// Store a copy of this RAM info.
set_ram_info(info: BootsimRamInfo)258 pub fn set_ram_info(info: BootsimRamInfo) {
259     RAM_CTX.with(|ctx| {
260         ctx.replace(info);
261     });
262 }
263 
264 /// Clear out the ram info.
clear_ram_info()265 pub fn clear_ram_info() {
266     RAM_CTX.with(|ctx| {
267         ctx.borrow_mut().base = 0;
268     });
269 }
270 
271 #[no_mangle]
sim_flash_erase(dev_id: u8, offset: u32, size: u32) -> libc::c_int272 pub extern "C" fn sim_flash_erase(dev_id: u8, offset: u32, size: u32) -> libc::c_int {
273     let mut rc: libc::c_int = -19;
274     THREAD_CTX.with(|ctx| {
275         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
276             let dev = unsafe { &mut *(flash.ptr) };
277             rc = map_err(dev.erase(offset as usize, size as usize));
278         }
279     });
280     rc
281 }
282 
283 #[no_mangle]
sim_flash_read(dev_id: u8, offset: u32, dest: *mut u8, size: u32) -> libc::c_int284 pub extern "C" fn sim_flash_read(dev_id: u8, offset: u32, dest: *mut u8, size: u32) -> libc::c_int {
285     let mut rc: libc::c_int = -19;
286     THREAD_CTX.with(|ctx| {
287         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
288             let mut buf: &mut[u8] = unsafe { slice::from_raw_parts_mut(dest, size as usize) };
289             let dev = unsafe { &mut *(flash.ptr) };
290             rc = map_err(dev.read(offset as usize, &mut buf));
291         }
292     });
293     rc
294 }
295 
296 #[no_mangle]
sim_flash_write(dev_id: u8, offset: u32, src: *const u8, size: u32) -> libc::c_int297 pub extern "C" fn sim_flash_write(dev_id: u8, offset: u32, src: *const u8, size: u32) -> libc::c_int {
298     let mut rc: libc::c_int = -19;
299     THREAD_CTX.with(|ctx| {
300         if let Some(flash) = ctx.borrow().flash_map.get(&dev_id) {
301             let buf: &[u8] = unsafe { slice::from_raw_parts(src, size as usize) };
302             let dev = unsafe { &mut *(flash.ptr) };
303             rc = map_err(dev.write(offset as usize, &buf));
304         }
305     });
306     rc
307 }
308 
309 #[no_mangle]
sim_flash_align(id: u8) -> u32310 pub extern "C" fn sim_flash_align(id: u8) -> u32 {
311     THREAD_CTX.with(|ctx| {
312         ctx.borrow().flash_params.get(&id).unwrap().align
313     })
314 }
315 
316 #[no_mangle]
sim_flash_erased_val(id: u8) -> u8317 pub extern "C" fn sim_flash_erased_val(id: u8) -> u8 {
318     THREAD_CTX.with(|ctx| {
319         ctx.borrow().flash_params.get(&id).unwrap().erased_val
320     })
321 }
322 
map_err(err: Result<()>) -> libc::c_int323 fn map_err(err: Result<()>) -> libc::c_int {
324     match err {
325         Ok(()) => 0,
326         Err(e) => {
327             warn!("{}", e);
328             -1
329         },
330     }
331 }
332 
333 /// Called by C code to determine if we should log at this level.  Levels are defined in
334 /// bootutil/bootutil_log.h.  This makes the logging from the C code controlled by bootsim::api, so
335 /// for example, it can be enabled with something like:
336 ///     RUST_LOG=bootsim::api=info cargo run --release runall
337 /// or
338 ///     RUST_LOG=bootsim=info cargo run --release runall
339 #[no_mangle]
sim_log_enabled(level: libc::c_int) -> libc::c_int340 pub extern "C" fn sim_log_enabled(level: libc::c_int) -> libc::c_int {
341     let res = match level {
342         1 => log_enabled!(Level::Error),
343         2 => log_enabled!(Level::Warn),
344         3 => log_enabled!(Level::Info),
345         4 => log_enabled!(Level::Debug),
346         5 => log_enabled!(Level::Trace), // log level == SIM
347         _ => false,
348     };
349     if res {
350         1
351     } else {
352         0
353     }
354 }
355 
356 #[no_mangle]
sim_set_nv_counter_for_image(image_index: u32, security_counter_value: u32) -> libc::c_int357 pub extern "C" fn sim_set_nv_counter_for_image(image_index: u32, security_counter_value: u32) -> libc::c_int {
358     let mut rc = 0;
359     NV_COUNTER_CTX.with(|ctx| {
360         let mut counter_storage = ctx.borrow_mut();
361         if image_index as usize >= counter_storage.storage.len() {
362             rc = -1;
363             return;
364         }
365         if counter_storage.storage[image_index as usize] > security_counter_value {
366             rc = -2;
367             warn!("Failed to set security counter value ({}) for image index {}", security_counter_value, image_index);
368             return;
369         }
370 
371         counter_storage.storage[image_index as usize] = security_counter_value;
372     });
373 
374     return rc;
375 }
376 
377 #[no_mangle]
sim_get_nv_counter_for_image(image_index: u32, security_counter_value: *mut u32) -> libc::c_int378 pub extern "C" fn sim_get_nv_counter_for_image(image_index: u32, security_counter_value: *mut u32) -> libc::c_int {
379     let mut rc = 0;
380     NV_COUNTER_CTX.with(|ctx| {
381         let counter_storage = ctx.borrow();
382         if image_index as usize >= counter_storage.storage.len() {
383             rc = -1;
384             return;
385         }
386         unsafe { *security_counter_value = counter_storage.storage[image_index as usize] };
387 
388     });
389     return rc;
390 }
391