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