kernel/dynamic_binary_storage.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2024.
4
5//! Dynamic Binary Flasher for application loading and updating at runtime.
6//!
7//! These functions facilitate dynamic application flashing and process creation
8//! during runtime without requiring the user to restart the device.
9
10use core::cell::Cell;
11
12use crate::config;
13use crate::debug;
14use crate::deferred_call::{DeferredCall, DeferredCallClient};
15use crate::hil::nonvolatile_storage::{NonvolatileStorage, NonvolatileStorageClient};
16use crate::platform::chip::Chip;
17use crate::process::ProcessLoadingAsyncClient;
18use crate::process_loading::{
19 PaddingRequirement, ProcessLoadError, SequentialProcessLoaderMachine,
20};
21use crate::process_standard::ProcessStandardDebug;
22use crate::utilities::cells::{OptionalCell, TakeCell};
23use crate::utilities::leasable_buffer::SubSliceMut;
24use crate::ErrorCode;
25
26/// Expected buffer length for storing application binaries.
27pub const BUF_LEN: usize = 512;
28
29/// The number of bytes in the TBF header for a padding app.
30const PADDING_TBF_HEADER_LENGTH: usize = 16;
31
32#[derive(Clone, Copy, PartialEq)]
33pub enum State {
34 Idle,
35 Setup,
36 AppWrite,
37 Load,
38 Abort,
39 PaddingWrite,
40 Fail,
41}
42
43/// Addresses of where the new process will be stored.
44#[derive(Clone, Copy, Default)]
45struct ProcessLoadMetadata {
46 new_app_start_addr: usize,
47 new_app_length: usize,
48 previous_app_end_addr: usize,
49 next_app_start_addr: usize,
50 padding_requirement: PaddingRequirement,
51 setup_padding: bool,
52}
53
54/// This interface supports flashing binaries at runtime.
55pub trait DynamicBinaryStore {
56 /// Call to request flashing a new binary.
57 ///
58 /// This informs the kernel we want to load a process, and the size of the
59 /// entire process binary. The kernel will try to find a suitable location
60 /// in flash to store said process.
61 ///
62 /// Return value:
63 /// - `Ok(length)`: If there is a place to load the
64 /// process, the function will return `Ok()` with the size of the region
65 /// to store the process.
66 /// - `Err(ErrorCode)`: If there is nowhere to store the process a suitable
67 /// `ErrorCode` will be returned.
68 fn setup(&self, app_length: usize) -> Result<usize, ErrorCode>;
69
70 /// Instruct the kernel to write data to the flash.
71 ///
72 /// `offset` is where to start writing within the region allocated
73 /// for the new process binary from the `setup()` call.
74 ///
75 /// The caller must write the first 8 bytes of the process with valid header
76 /// data. Writes must either be after the first 8 bytes or include the
77 /// entire first 8 bytes.
78 ///
79 /// Returns an error if the write is outside of the permitted region or is
80 /// writing an invalid header.
81 fn write(&self, buffer: SubSliceMut<'static, u8>, offset: usize) -> Result<(), ErrorCode>;
82
83 /// Signal to the kernel that the requesting process is done writing the new
84 /// binary.
85 fn finalize(&self) -> Result<(), ErrorCode>;
86
87 /// Call to abort the setup/writing process.
88 fn abort(&self) -> Result<(), ErrorCode>;
89
90 /// Sets a client for the SequentialDynamicBinaryStore Object
91 ///
92 /// When the client operation is done, it calls the `setup_done()`,
93 /// `write_done()` and `abort_done()` functions.
94 fn set_storage_client(&self, client: &'static dyn DynamicBinaryStoreClient);
95}
96
97/// The callback for dynamic binary flashing.
98pub trait DynamicBinaryStoreClient {
99 /// Any setup work is done and we are ready to write the process binary.
100 fn setup_done(&self, result: Result<(), ErrorCode>);
101
102 /// The provided app binary buffer has been stored.
103 fn write_done(&self, result: Result<(), ErrorCode>, buffer: &'static mut [u8], length: usize);
104
105 /// The kernel has successfully finished finalizing the new app and is ready
106 /// to move to the `load()` phase.
107 fn finalize_done(&self, result: Result<(), ErrorCode>);
108
109 /// Canceled any setup or writing operation and freed up reserved space.
110 fn abort_done(&self, result: Result<(), ErrorCode>);
111}
112
113/// This interface supports loading processes at runtime.
114pub trait DynamicProcessLoad {
115 /// Call to request kernel to load a new process.
116 fn load(&self) -> Result<(), ErrorCode>;
117
118 /// Sets a client for the SequentialDynamicProcessLoading Object
119 ///
120 /// When the client operation is done, it calls the `load_done()`
121 /// function.
122 fn set_load_client(&self, client: &'static dyn DynamicProcessLoadClient);
123}
124
125/// The callback for dynamic binary flashing.
126pub trait DynamicProcessLoadClient {
127 /// The new app has been loaded.
128 fn load_done(&self, result: Result<(), ProcessLoadError>);
129}
130
131/// Dynamic process loading machine.
132pub struct SequentialDynamicBinaryStorage<
133 'a,
134 'b,
135 C: Chip + 'static,
136 D: ProcessStandardDebug + 'static,
137 F: NonvolatileStorage<'b>,
138> {
139 flash_driver: &'b F,
140 loader_driver: &'a SequentialProcessLoaderMachine<'a, C, D>,
141 buffer: TakeCell<'static, [u8]>,
142 storage_client: OptionalCell<&'static dyn DynamicBinaryStoreClient>,
143 load_client: OptionalCell<&'static dyn DynamicProcessLoadClient>,
144 process_metadata: OptionalCell<ProcessLoadMetadata>,
145 state: Cell<State>,
146 deferred_call: DeferredCall,
147}
148
149impl<'a, 'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
150 SequentialDynamicBinaryStorage<'a, 'b, C, D, F>
151{
152 pub fn new(
153 flash_driver: &'b F,
154 loader_driver: &'a SequentialProcessLoaderMachine<'a, C, D>,
155 buffer: &'static mut [u8],
156 ) -> Self {
157 Self {
158 flash_driver,
159 loader_driver,
160 buffer: TakeCell::new(buffer),
161 storage_client: OptionalCell::empty(),
162 load_client: OptionalCell::empty(),
163 process_metadata: OptionalCell::empty(),
164 state: Cell::new(State::Idle),
165 deferred_call: DeferredCall::new(),
166 }
167 }
168
169 /// Function to reset variables and states.
170 fn reset_process_loading_metadata(&self) {
171 self.state.set(State::Idle);
172 self.process_metadata.take();
173 }
174
175 /// This function checks whether the new app will fit in the bounds dictated
176 /// by the start address and length provided during the setup phase. This
177 /// function then also computes where in flash the data should be written
178 /// based on whether the call is coming during the app writing phase, or the
179 /// padding phase.
180 ///
181 /// This function returns the physical address in flash where the write is
182 /// supposed to happen.
183 fn compute_address(&self, offset: usize, length: usize) -> Result<usize, ErrorCode> {
184 let mut new_app_len: usize = 0;
185 let mut new_app_addr: usize = 0;
186 if let Some(metadata) = self.process_metadata.get() {
187 new_app_addr = metadata.new_app_start_addr;
188 new_app_len = metadata.new_app_length;
189 }
190
191 match self.state.get() {
192 State::AppWrite => {
193 // Check if there is an overflow while adding length and offset.
194 match offset.checked_add(length) {
195 Some(result) => {
196 // Check if the new app is trying to write beyond
197 // the bounds of the flash region allocated to it.
198 if result > new_app_len {
199 // This means the app is out of bounds.
200 Err(ErrorCode::INVAL)
201 } else {
202 // We compute the new address to write the app
203 // binary segment.
204 Ok(offset + new_app_addr)
205 }
206 }
207 None => Err(ErrorCode::INVAL),
208 }
209 }
210 // If we are going to write the padding header, we already know
211 // where to write in flash, so we don't have to add the start
212 // address
213 State::Setup | State::Load | State::PaddingWrite | State::Abort => Ok(offset),
214 // We aren't supposed to be able to write unless we are in one of
215 // the first two write states
216 _ => Err(ErrorCode::FAIL),
217 }
218 }
219
220 /// Compute the physical address where we should write the data and then
221 /// write it.
222 fn write_buffer(
223 &self,
224 user_buffer: SubSliceMut<'static, u8>,
225 offset: usize,
226 ) -> Result<(), ErrorCode> {
227 let length = user_buffer.len();
228 // Take the buffer to perform tbf header validation and write with.
229 let buffer = user_buffer.take();
230
231 let physical_address = self.compute_address(offset, length)?;
232
233 // The kernel needs to check if the app is trying to write/overwrite the
234 // header. So the app can only write to the first 8 bytes if the app is
235 // writing all 8 bytes. Else, the kernel must raise an error. The app is
236 // not allowed to write from say, offset 4 because we have to ensure the
237 // validity of the header.
238 //
239 // This means the app is trying to manipulate the space where the TBF
240 // header should go. Ideally, we want the app to only write the complete
241 // set of 8 bytes which is used to determine if the header is valid. We
242 // don't want apps to do this, so we return an error.
243 if (offset == 0 && length < 8) || (offset != 0 && offset < 8) {
244 return Err(ErrorCode::INVAL);
245 }
246
247 // Check if we are writing the start of the TBF header.
248 //
249 // The app is not allowed to manipulate parts of the TBF header, so if
250 // it is trying to write at the very beginning of the promised flash
251 // region, we require the app writes the entire 8 bytes of the header.
252 // This header is then checked for validity.
253 if offset == 0 {
254 // Pass the first eight bytes of the tbf header to parse out the
255 // length of the header and app. We then use those values to see if
256 // the app is going to be valid.
257 let test_header_slice = buffer.get(0..8).ok_or(ErrorCode::INVAL)?;
258 let header = test_header_slice.try_into().or(Err(ErrorCode::FAIL))?;
259 let (_version, _header_length, entry_length) =
260 match tock_tbf::parse::parse_tbf_header_lengths(header) {
261 Ok((v, hl, el)) => (v, hl, el),
262 Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(_entry_length)) => {
263 // If we have an invalid header, so we return an error
264 return Err(ErrorCode::INVAL);
265 }
266 Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => {
267 // If we could not parse the header, then that's an
268 // issue. We return an Error.
269 return Err(ErrorCode::INVAL);
270 }
271 };
272
273 // Check if the length in the header is matching what the app
274 // requested during the setup phase also check if the kernel
275 // version matches the version indicated in the new application.
276 let mut new_app_len = 0;
277 if let Some(metadata) = self.process_metadata.get() {
278 new_app_len = metadata.new_app_length;
279 }
280 if entry_length as usize != new_app_len {
281 return Err(ErrorCode::INVAL);
282 }
283 }
284 self.flash_driver.write(buffer, physical_address, length)
285 }
286
287 /// Function to generate the padding header to append after the new app.
288 /// This header is created and written to ensure the integrity of the
289 /// processes linked list
290 fn write_padding_app(&self, padding_app_length: usize, offset: usize) -> Result<(), ErrorCode> {
291 // Write the header into the array
292 self.buffer.map(|buffer| {
293 // First two bytes are the TBF version (2).
294 buffer[0] = 2;
295 buffer[1] = 0;
296
297 // The next two bytes are the header length (fixed to 16 bytes for
298 // padding).
299 buffer[2] = (PADDING_TBF_HEADER_LENGTH & 0xff) as u8;
300 buffer[3] = ((PADDING_TBF_HEADER_LENGTH >> 8) & 0xff) as u8;
301
302 // The next 4 bytes are the total app length including the header.
303 buffer[4] = (padding_app_length & 0xff) as u8;
304 buffer[5] = ((padding_app_length >> 8) & 0xff) as u8;
305 buffer[6] = ((padding_app_length >> 16) & 0xff) as u8;
306 buffer[7] = ((padding_app_length >> 24) & 0xff) as u8;
307
308 // We set the flags to 0.
309 for i in 8..12 {
310 buffer[i] = 0x00_u8;
311 }
312
313 // xor of the previous values
314 buffer[12] = buffer[0] ^ buffer[4] ^ buffer[8];
315 buffer[13] = buffer[1] ^ buffer[5] ^ buffer[9];
316 buffer[14] = buffer[2] ^ buffer[6] ^ buffer[10];
317 buffer[15] = buffer[3] ^ buffer[7] ^ buffer[11];
318 });
319
320 self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
321 match self
322 .loader_driver
323 .check_if_within_flash_bounds(offset, PADDING_TBF_HEADER_LENGTH)
324 {
325 true => {
326 // Write the header only if there are more than 16 bytes.
327 // available in the flash.
328 let mut padding_slice = SubSliceMut::new(buffer);
329 padding_slice.slice(..PADDING_TBF_HEADER_LENGTH);
330 // We are only writing the header, so 16 bytes is enough.
331 self.write_buffer(padding_slice, offset)
332 }
333 false => Err(ErrorCode::NOMEM),
334 }
335 })
336 }
337}
338
339impl<'b, C: Chip, D: ProcessStandardDebug, F: NonvolatileStorage<'b>> DeferredCallClient
340 for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
341{
342 fn handle_deferred_call(&self) {
343 // We use deferred call to signal the completion of finalize
344 self.storage_client.map(|client| {
345 client.finalize_done(Ok(()));
346 });
347 }
348
349 fn register(&'static self) {
350 self.deferred_call.register(self);
351 }
352}
353
354/// This is the callback client for the underlying physical storage driver.
355impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
356 NonvolatileStorageClient for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
357{
358 fn read_done(&self, _buffer: &'static mut [u8], _length: usize) {
359 // We will never use this, but we need to implement this anyway.
360 unimplemented!();
361 }
362
363 fn write_done(&self, buffer: &'static mut [u8], length: usize) {
364 match self.state.get() {
365 State::AppWrite => {
366 self.state.set(State::AppWrite);
367 // Switch on which user generated this callback and trigger
368 // client callback.
369 self.storage_client.map(|client| {
370 client.write_done(Ok(()), buffer, length);
371 });
372 }
373 State::PaddingWrite => {
374 // Replace the buffer after the padding is written.
375 self.reset_process_loading_metadata();
376 self.buffer.replace(buffer);
377 }
378 State::Fail => {
379 // If we failed at any of writing, we want to set the state to
380 // PaddingWrite so that the callback after writing the padding
381 // app will get triggererd.
382 self.buffer.replace(buffer);
383 if let Some(metadata) = self.process_metadata.get() {
384 let _ = self
385 .write_padding_app(metadata.new_app_length, metadata.new_app_start_addr);
386 }
387 // Clear all metadata specific to this load.
388 self.reset_process_loading_metadata();
389 }
390 State::Setup => {
391 // We have finished writing the post app padding.
392 self.buffer.replace(buffer);
393
394 if let Some(mut metadata) = self.process_metadata.get() {
395 if !metadata.setup_padding {
396 // Write padding header to the beginning of the new app address.
397 // This ensures that the linked list is not broken in the event of a
398 // powercycle before the app is fully written and loaded.
399 metadata.setup_padding = true;
400 let _ = self.write_padding_app(
401 metadata.new_app_length,
402 metadata.new_app_start_addr,
403 );
404 self.process_metadata.set(metadata);
405 } else {
406 self.state.set(State::AppWrite);
407 // Let the client know we are done setting up.
408 self.storage_client.map(|client| {
409 client.setup_done(Ok(()));
410 });
411 }
412 }
413 }
414 State::Load => {
415 // We finished writing pre-padding and we need to Load the app.
416 self.buffer.replace(buffer);
417 self.storage_client.map(|client| {
418 client.finalize_done(Ok(()));
419 });
420 }
421 State::Abort => {
422 self.buffer.replace(buffer);
423 // Reset metadata and let client know we are done aborting.
424 self.reset_process_loading_metadata();
425 self.storage_client.map(|client| {
426 client.abort_done(Ok(()));
427 });
428 }
429 State::Idle => {
430 self.buffer.replace(buffer);
431 }
432 }
433 }
434}
435
436/// Callback client for the async process loader
437impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
438 ProcessLoadingAsyncClient for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
439{
440 fn process_loaded(&self, result: Result<(), ProcessLoadError>) {
441 self.load_client.map(|client| {
442 client.load_done(result);
443 });
444 }
445
446 fn process_loading_finished(&self) {}
447}
448
449/// Storage interface exposed to the app_loader capsule
450impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
451 DynamicBinaryStore for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
452{
453 fn set_storage_client(&self, client: &'static dyn DynamicBinaryStoreClient) {
454 self.storage_client.set(client);
455 }
456
457 fn setup(&self, app_length: usize) -> Result<usize, ErrorCode> {
458 self.process_metadata.set(ProcessLoadMetadata::default());
459
460 if self.state.get() == State::Idle {
461 self.state.set(State::Setup);
462 match self.loader_driver.check_flash_for_new_address(app_length) {
463 Ok((
464 new_app_start_address,
465 padding_requirement,
466 previous_app_end_addr,
467 next_app_start_addr,
468 )) => {
469 if let Some(mut metadata) = self.process_metadata.get() {
470 metadata.new_app_start_addr = new_app_start_address;
471 metadata.new_app_length = app_length;
472 metadata.previous_app_end_addr = previous_app_end_addr;
473 metadata.next_app_start_addr = next_app_start_addr;
474 metadata.padding_requirement = padding_requirement;
475 self.process_metadata.set(metadata);
476 }
477
478 match padding_requirement {
479 // If we decided we need to write a padding app after
480 // the new app, we go ahead and do it.
481 PaddingRequirement::PostPad | PaddingRequirement::PreAndPostPad => {
482 // Calculating the distance between our app and
483 // either the next app.
484 let new_app_end_address = new_app_start_address + app_length;
485 let post_pad_length = next_app_start_addr - new_app_end_address;
486
487 let padding_result =
488 self.write_padding_app(post_pad_length, new_app_end_address);
489 let _ = match padding_result {
490 Ok(()) => Ok(()),
491 Err(e) => {
492 // This means we were unable to write the
493 // padding app.
494 self.reset_process_loading_metadata();
495 Err(e)
496 }
497 };
498 }
499 // Otherwise we let the client know we are done with the
500 // setup, and we are ready to write the app to flash.
501 PaddingRequirement::None | PaddingRequirement::PrePad => {
502 if let Some(mut metadata) = self.process_metadata.get() {
503 if !metadata.setup_padding {
504 // Write padding header to the beginning of the new app address.
505 // This ensures that the linked list is not broken in the event of a
506 // powercycle before the app is fully written and loaded.
507
508 metadata.setup_padding = true;
509 let _ = self.write_padding_app(
510 metadata.new_app_length,
511 metadata.new_app_start_addr,
512 );
513 self.process_metadata.set(metadata);
514 }
515 }
516 }
517 }
518 Ok(app_length)
519 }
520 Err(_err) => {
521 // Reset the state to None because we did not find any
522 // available address for this app.
523 self.reset_process_loading_metadata();
524 Err(ErrorCode::FAIL)
525 }
526 }
527 } else {
528 // We are in the wrong mode of operation. Ideally we should never reach
529 // here, but this error exists as a failsafe. The capsule should send
530 // a busy error out to the userland app.
531 Err(ErrorCode::INVAL)
532 }
533 }
534
535 fn write(&self, buffer: SubSliceMut<'static, u8>, offset: usize) -> Result<(), ErrorCode> {
536 match self.state.get() {
537 State::AppWrite => {
538 let res = self.write_buffer(buffer, offset);
539 match res {
540 Ok(()) => Ok(()),
541 Err(e) => {
542 // If we fail here, let us erase the app we just wrote.
543 self.state.set(State::Fail);
544 Err(e)
545 }
546 }
547 }
548 _ => {
549 // We are in the wrong mode of operation. Ideally we should never reach
550 // here, but this error exists as a failsafe. The capsule should send
551 // a busy error out to the userland app.
552 Err(ErrorCode::INVAL)
553 }
554 }
555 }
556
557 fn finalize(&self) -> Result<(), ErrorCode> {
558 match self.state.get() {
559 State::AppWrite => {
560 if let Some(metadata) = self.process_metadata.get() {
561 match metadata.padding_requirement {
562 // If we decided we need to write a padding app before the new
563 // app, we go ahead and do it.
564 PaddingRequirement::PrePad | PaddingRequirement::PreAndPostPad => {
565 // Calculate the distance between our app and the previous
566 // app.
567 let previous_app_end_addr = metadata.previous_app_end_addr;
568 let pre_pad_length =
569 metadata.new_app_start_addr - previous_app_end_addr;
570 self.state.set(State::Load);
571 let padding_result =
572 self.write_padding_app(pre_pad_length, previous_app_end_addr);
573 match padding_result {
574 Ok(()) => {
575 if config::CONFIG.debug_load_processes {
576 debug!("Successfully writing prepadding app");
577 }
578 Ok(())
579 }
580 Err(_e) => {
581 // This means we were unable to write the padding
582 // app.
583 self.reset_process_loading_metadata();
584 Err(ErrorCode::FAIL)
585 }
586 }
587 }
588 // We should never reach here if we are not writing a prepad
589 // app.
590 PaddingRequirement::None | PaddingRequirement::PostPad => {
591 if config::CONFIG.debug_load_processes {
592 debug!("No PrePad app to write.");
593 }
594 self.state.set(State::Load);
595 self.deferred_call.set();
596 Ok(())
597 }
598 }
599 } else {
600 Err(ErrorCode::INVAL)
601 }
602 }
603 _ => Err(ErrorCode::INVAL),
604 }
605 }
606
607 fn abort(&self) -> Result<(), ErrorCode> {
608 match self.state.get() {
609 State::Setup | State::AppWrite => {
610 self.state.set(State::Abort);
611 if let Some(metadata) = self.process_metadata.get() {
612 // Write padding header to the beginning of the new app address.
613 // This ensures that the flash space is reclaimed for future use.
614 match self
615 .write_padding_app(metadata.new_app_length, metadata.new_app_start_addr)
616 {
617 Ok(()) => Ok(()),
618 // If abort() returns ErrorCode::BUSY,
619 // the userland app is expected to retry abort.
620 Err(_) => Err(ErrorCode::BUSY),
621 }
622 } else {
623 Err(ErrorCode::FAIL)
624 }
625 }
626 _ => {
627 // We are in the wrong mode of operation. Ideally we should never reach
628 // here, but this error exists as a failsafe. The capsule should send
629 // a busy error out to the userland app.
630 Err(ErrorCode::INVAL)
631 }
632 }
633 }
634}
635
636/// Loading interface exposed to the app_loader capsule
637impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
638 DynamicProcessLoad for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
639{
640 fn set_load_client(&self, client: &'static dyn DynamicProcessLoadClient) {
641 self.load_client.set(client);
642 }
643
644 fn load(&self) -> Result<(), ErrorCode> {
645 // We have finished writing the last user data segment, next step is to
646 // load the process.
647 match self.state.get() {
648 State::Load => {
649 if let Some(metadata) = self.process_metadata.get() {
650 let _ = match self.loader_driver.load_new_process_binary(
651 metadata.new_app_start_addr,
652 metadata.new_app_length,
653 ) {
654 Ok(()) => Ok::<(), ProcessLoadError>(()),
655 Err(_e) => {
656 self.reset_process_loading_metadata();
657 return Err(ErrorCode::FAIL);
658 }
659 };
660 } else {
661 self.reset_process_loading_metadata();
662 return Err(ErrorCode::FAIL);
663 }
664 self.reset_process_loading_metadata();
665 Ok(())
666 }
667 _ => Err(ErrorCode::INVAL),
668 }
669 }
670}