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