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::ErrorCode;
13use crate::Kernel;
14use crate::config;
15use crate::debug;
16use crate::deferred_call::{DeferredCall, DeferredCallClient};
17use crate::hil::nonvolatile_storage::{NonvolatileStorage, NonvolatileStorageClient};
18use crate::platform::chip::Chip;
19use crate::process::{ProcessLoadingAsyncClient, ShortId};
20use crate::process_loading::{
21 PaddingRequirement, ProcessLoadError, SequentialProcessLoaderMachine,
22};
23use crate::process_standard::ProcessStandardDebug;
24use crate::utilities::cells::{OptionalCell, TakeCell};
25use crate::utilities::leasable_buffer::SubSliceMut;
26
27/// Expected buffer length for storing application binaries.
28pub const BUF_LEN: usize = 4096;
29
30/// The number of bytes in the TBF header for a padding app.
31const PADDING_TBF_HEADER_LENGTH: usize = 16;
32
33#[derive(Clone, Copy, PartialEq)]
34pub enum State {
35 Idle,
36 Setup,
37 AppWrite,
38 Load,
39 Abort,
40 Unload(Result<(), ErrorCode>, usize),
41 Uninstall,
42 PaddingWrite,
43 Fail,
44}
45
46/// Addresses of where the new process will be stored.
47#[derive(Clone, Copy, Default)]
48struct ProcessLoadMetadata {
49 new_app_start_addr: usize,
50 new_app_length: usize,
51 previous_app_end_addr: usize,
52 next_app_start_addr: usize,
53 padding_requirement: PaddingRequirement,
54 setup_padding: bool,
55 erase_chunk_offset: usize,
56 uninstall_app_end_address: usize,
57}
58
59/// This interface supports flashing binaries at runtime.
60pub trait DynamicBinaryStore {
61 /// Call to request flashing a new binary.
62 ///
63 /// This informs the kernel we want to load a process, and the size of the
64 /// entire process binary. The kernel will try to find a suitable location
65 /// in flash to store said process.
66 ///
67 /// Return value:
68 /// - `Ok(length)`: If there is a place to load the
69 /// process, the function will return `Ok()` with the size of the region
70 /// to store the process.
71 /// - `Err(ErrorCode)`: If there is nowhere to store the process a suitable
72 /// `ErrorCode` will be returned.
73 fn setup(&self, app_length: usize) -> Result<usize, ErrorCode>;
74
75 /// Instruct the kernel to write data to the flash.
76 ///
77 /// `offset` is where to start writing within the region allocated
78 /// for the new process binary from the `setup()` call.
79 ///
80 /// The caller must write the first 8 bytes of the process with valid header
81 /// data. Writes must either be after the first 8 bytes or include the
82 /// entire first 8 bytes.
83 ///
84 /// Returns an error if the write is outside of the permitted region or is
85 /// writing an invalid header.
86 fn write(
87 &self,
88 buffer: SubSliceMut<'static, u8>,
89 offset: usize,
90 ) -> Result<(), (ErrorCode, SubSliceMut<'static, u8>)>;
91
92 /// Signal to the kernel that the requesting process is done writing the new
93 /// binary.
94 fn finalize(&self) -> Result<(), ErrorCode>;
95
96 /// Call to abort the setup/writing process.
97 fn abort(&self) -> Result<(), ErrorCode>;
98
99 /// Sets a client for the SequentialDynamicBinaryStore Object
100 ///
101 /// When the client operation is done, it calls the `setup_done()`,
102 /// `write_done()` and `abort_done()` functions.
103 fn set_storage_client(&self, client: &'static dyn DynamicBinaryStoreClient);
104}
105
106/// The callback for dynamic binary flashing.
107pub trait DynamicBinaryStoreClient {
108 /// Any setup work is done and we are ready to write the process binary.
109 fn setup_done(&self, result: Result<(), ErrorCode>);
110
111 /// The provided app binary buffer has been stored.
112 fn write_done(&self, result: Result<(), ErrorCode>, buffer: &'static mut [u8], length: usize);
113
114 /// The kernel has successfully finished finalizing the new app and is ready
115 /// to move to the `load()` phase.
116 fn finalize_done(&self, result: Result<(), ErrorCode>);
117
118 /// Canceled any setup or writing operation and freed up reserved space.
119 fn abort_done(&self, result: Result<(), ErrorCode>);
120}
121
122/// This interface supports loading processes at runtime.
123pub trait DynamicProcessLoad {
124 /// Call to request kernel to load a new process.
125 fn load(&self) -> Result<(), ErrorCode>;
126
127 /// Sets a client for the SequentialDynamicProcessLoading Object
128 ///
129 /// When the client operation is done, it calls the `load_done()`
130 /// function.
131 fn set_load_client(&self, client: &'static dyn DynamicProcessLoadClient);
132}
133
134/// The callback for dynamic process loading.
135pub trait DynamicProcessLoadClient {
136 /// The new app has been loaded.
137 fn load_done(&self, result: Result<(), ProcessLoadError>);
138}
139
140/// This interface supports unloading processes at runtime.
141pub trait DynamicProcessUnload {
142 /// Call to terminate a process with given ShortId.
143 fn unload(&self, app: ShortId) -> Result<(), ErrorCode>;
144
145 /// Sets a client for the SequentialDynamicProcessUnload Object
146 ///
147 /// When the client operation is done, it calls the `unload_done()`
148 /// function.
149 fn set_unload_client(&self, client: &'static dyn DynamicProcessUnloadClient);
150}
151
152/// The callback for dynamic process unloading.
153pub trait DynamicProcessUnloadClient {
154 /// Terminated app (if running).
155 fn unload_done(&self, result: Result<(), ErrorCode>, app_handle: usize);
156}
157
158/// This interface supports uninstalling processes during runtime.
159pub trait DynamicProcessUninstall {
160 /// Call to uninstall an application binary with a specified `app_handle`.
161 /// `app_handle` is defined by the return value of the `Unload()` operation.
162 fn uninstall_with_app_handle(&self, app_handle: usize) -> Result<(), ErrorCode>;
163
164 /// Sets a client for the SequentialDynamicProcessUninstall Object
165 ///
166 /// When the client operation is done, it calls the `uninstall_done()`
167 /// function.
168 fn set_uninstall_client(&self, client: &'static dyn DynamicProcessUninstallClient);
169}
170
171/// The callback for dynamic process unloading.
172pub trait DynamicProcessUninstallClient {
173 /// Removed application binary from storage.
174 fn uninstall_done(&self, result: Result<(), ErrorCode>);
175}
176
177/// Dynamic process loading machine.
178pub struct SequentialDynamicBinaryStorage<
179 'a,
180 'b,
181 C: Chip + 'static,
182 D: ProcessStandardDebug + 'static,
183 F: NonvolatileStorage<'b>,
184> {
185 kernel: &'static Kernel,
186 flash_driver: &'b F,
187 loader_driver: &'a SequentialProcessLoaderMachine<'a, C, D>,
188 buffer: TakeCell<'static, [u8]>,
189 storage_client: OptionalCell<&'static dyn DynamicBinaryStoreClient>,
190 load_client: OptionalCell<&'static dyn DynamicProcessLoadClient>,
191 unload_client: OptionalCell<&'static dyn DynamicProcessUnloadClient>,
192 uninstall_client: OptionalCell<&'static dyn DynamicProcessUninstallClient>,
193 process_metadata: OptionalCell<ProcessLoadMetadata>,
194 state: Cell<State>,
195 deferred_call: DeferredCall,
196}
197
198impl<'a, 'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
199 SequentialDynamicBinaryStorage<'a, 'b, C, D, F>
200{
201 pub fn new(
202 kernel: &'static Kernel,
203 flash_driver: &'b F,
204 loader_driver: &'a SequentialProcessLoaderMachine<'a, C, D>,
205 buffer: &'static mut [u8],
206 ) -> Self {
207 Self {
208 kernel,
209 flash_driver,
210 loader_driver,
211 buffer: TakeCell::new(buffer),
212 storage_client: OptionalCell::empty(),
213 load_client: OptionalCell::empty(),
214 unload_client: OptionalCell::empty(),
215 uninstall_client: OptionalCell::empty(),
216 process_metadata: OptionalCell::empty(),
217 state: Cell::new(State::Idle),
218 deferred_call: DeferredCall::new(),
219 }
220 }
221
222 /// Function to reset variables and states.
223 fn reset_process_loading_metadata(&self) {
224 self.state.set(State::Idle);
225 self.process_metadata.take();
226 }
227
228 /// This function checks whether the new app will fit in the bounds dictated
229 /// by the start address and length provided during the setup phase. This
230 /// function then also computes where in flash the data should be written
231 /// based on whether the call is coming during the app writing phase, or the
232 /// padding phase.
233 ///
234 /// This function returns the physical address in flash where the write is
235 /// supposed to happen.
236 fn compute_address(&self, offset: usize, length: usize) -> Result<usize, ErrorCode> {
237 let mut new_app_len: usize = 0;
238 let mut new_app_addr: usize = 0;
239 if let Some(metadata) = self.process_metadata.get() {
240 new_app_addr = metadata.new_app_start_addr;
241 new_app_len = metadata.new_app_length;
242 }
243
244 match self.state.get() {
245 State::AppWrite => {
246 // Check if there is an overflow while adding length and offset.
247 match offset.checked_add(length) {
248 Some(result) => {
249 // Check if the new app is trying to write beyond
250 // the bounds of the flash region allocated to it.
251 if result > new_app_len {
252 // This means the app is out of bounds.
253 Err(ErrorCode::INVAL)
254 } else {
255 // We compute the new address to write the app
256 // binary segment.
257 Ok(offset + new_app_addr)
258 }
259 }
260 None => Err(ErrorCode::INVAL),
261 }
262 }
263 // If we are going to write the padding header, we already know
264 // where to write in flash, so we don't have to add the start
265 // address
266 State::Setup | State::Load | State::PaddingWrite | State::Abort | State::Uninstall => {
267 Ok(offset)
268 }
269 // We aren't supposed to be able to write unless we are in one of
270 // the first two write states
271 _ => Err(ErrorCode::FAIL),
272 }
273 }
274
275 /// Compute the physical address where we should write the data and then
276 /// write it.
277 fn write_buffer(
278 &self,
279 user_buffer: SubSliceMut<'static, u8>,
280 offset: usize,
281 ) -> Result<(), (ErrorCode, SubSliceMut<'static, u8>)> {
282 let length = user_buffer.len();
283
284 let physical_address = match self.compute_address(offset, length) {
285 Ok(addr) => addr,
286 Err(e) => return Err((e, user_buffer)),
287 };
288
289 // The kernel needs to check if the app is trying to write/overwrite the
290 // header. So the app can only write to the first 8 bytes if the app is
291 // writing all 8 bytes. Else, the kernel must raise an error. The app is
292 // not allowed to write from say, offset 4 because we have to ensure the
293 // validity of the header.
294 //
295 // This means the app is trying to manipulate the space where the TBF
296 // header should go. Ideally, we want the app to only write the complete
297 // set of 8 bytes which is used to determine if the header is valid. We
298 // don't want apps to do this, so we return an error.
299 if (offset == 0 && length < 8) || (offset != 0 && offset < 8) {
300 return Err((ErrorCode::INVAL, user_buffer));
301 }
302
303 // Check if we are writing the start of the TBF header.
304 //
305 // The app is not allowed to manipulate parts of the TBF header, so if
306 // it is trying to write at the very beginning of the promised flash
307 // region, we require the app writes the entire 8 bytes of the header.
308 // This header is then checked for validity.
309 //
310 // We validate through a borrow of `user_buffer` here (rather than
311 // consuming it via `take()`) so that we still own it and can return
312 // it back to the caller on every error path.
313 if offset == 0 {
314 // Pass the first eight bytes of the tbf header to parse out the
315 // length of the header and app. We then use those values to see if
316 // the app is going to be valid.
317 let test_header_slice = match user_buffer.as_slice().get(0..8) {
318 Some(slice) => slice,
319 None => {
320 return Err((ErrorCode::INVAL, user_buffer));
321 }
322 };
323 let header = match test_header_slice.try_into() {
324 Ok(header) => header,
325 Err(_) => {
326 return Err((ErrorCode::FAIL, user_buffer));
327 }
328 };
329 let (_version, _header_length, entry_length) =
330 match tock_tbf::parse::parse_tbf_header_lengths(header) {
331 Ok((v, hl, el)) => (v, hl, el),
332 Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(_entry_length)) => {
333 // If we have an invalid header, so we return an error
334 return Err((ErrorCode::INVAL, user_buffer));
335 }
336 Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => {
337 // If we could not parse the header, then that's an
338 // issue. We return an Error.
339 return Err((ErrorCode::INVAL, user_buffer));
340 }
341 };
342
343 // Check if the length in the header is matching what the app
344 // requested during the setup phase also check if the kernel
345 // version matches the version indicated in the new application.
346 let mut new_app_len = 0;
347 if let Some(metadata) = self.process_metadata.get() {
348 new_app_len = metadata.new_app_length;
349 }
350 if entry_length as usize != new_app_len {
351 return Err((ErrorCode::INVAL, user_buffer));
352 }
353 }
354
355 // Take the buffer to write with.
356 let buffer = user_buffer.take();
357 self.flash_driver
358 .write(buffer, physical_address, length)
359 .map_err(|(e, buf)| (e, SubSliceMut::new(buf)))
360 }
361
362 /// Function to generate the padding header to append after the new app.
363 /// This header is created and written to ensure the integrity of the
364 /// processes linked list
365 fn write_padding_app(&self, padding_app_length: usize, offset: usize) -> Result<(), ErrorCode> {
366 // Write the header into the array
367 self.buffer.map(|buffer| {
368 // First two bytes are the TBF version (2).
369 buffer[0] = 2;
370 buffer[1] = 0;
371
372 // The next two bytes are the header length (fixed to 16 bytes for
373 // padding).
374 buffer[2] = (PADDING_TBF_HEADER_LENGTH & 0xff) as u8;
375 buffer[3] = ((PADDING_TBF_HEADER_LENGTH >> 8) & 0xff) as u8;
376
377 // The next 4 bytes are the total app length including the header.
378 buffer[4] = (padding_app_length & 0xff) as u8;
379 buffer[5] = ((padding_app_length >> 8) & 0xff) as u8;
380 buffer[6] = ((padding_app_length >> 16) & 0xff) as u8;
381 buffer[7] = ((padding_app_length >> 24) & 0xff) as u8;
382
383 // We set the flags to 0.
384 for i in 8..12 {
385 buffer[i] = 0x00_u8;
386 }
387
388 // xor of the previous values
389 buffer[12] = buffer[0] ^ buffer[4] ^ buffer[8];
390 buffer[13] = buffer[1] ^ buffer[5] ^ buffer[9];
391 buffer[14] = buffer[2] ^ buffer[6] ^ buffer[10];
392 buffer[15] = buffer[3] ^ buffer[7] ^ buffer[11];
393
394 for i in 16..BUF_LEN {
395 buffer[i] = 0xff_u8;
396 }
397 });
398
399 self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
400 match self
401 .loader_driver
402 .check_if_within_flash_bounds(offset, PADDING_TBF_HEADER_LENGTH)
403 {
404 true => {
405 // Write the header only if there are more than 16 bytes.
406 // available in the flash.
407 let padding_slice = SubSliceMut::new(buffer);
408 // padding_slice.slice(..PADDING_TBF_HEADER_LENGTH);
409 // // We are only writing the header, so 16 bytes is enough.
410 self.write_buffer(padding_slice, offset)
411 .map_err(|(e, buf)| {
412 self.buffer.replace(buf.take());
413 e
414 })
415 }
416 false => Err(ErrorCode::NOMEM),
417 }
418 })
419 }
420
421 /// Function to erase a chunk of non-volatile storage.
422 /// Takes in arguments for the offset and chunk size.
423 fn erase_chunk(&self, offset: usize, chunk_size: usize) -> Result<(), ErrorCode> {
424 self.buffer.map(|buffer| {
425 for i in 0..chunk_size {
426 buffer[i] = 0xff_u8;
427 }
428 });
429
430 // We risk the chance of skipping a page on reset
431 if let Some(mut metadata) = self.process_metadata.get() {
432 metadata.erase_chunk_offset = offset + chunk_size;
433 self.process_metadata.set(metadata);
434 }
435
436 // If power cycle happens here
437 self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
438 let erase_chunk = SubSliceMut::new(buffer);
439 self.write_buffer(erase_chunk, offset).map_err(|(e, buf)| {
440 self.buffer.replace(buf.take());
441 e
442 })
443 })
444 }
445}
446
447impl<'b, C: Chip, D: ProcessStandardDebug, F: NonvolatileStorage<'b>> DeferredCallClient
448 for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
449{
450 fn handle_deferred_call(&self) {
451 // We use deferred call to signal the completion of finalize or unload
452 match self.state.get() {
453 State::Load => {
454 self.storage_client.map(|client| {
455 client.finalize_done(Ok(()));
456 });
457 }
458 State::Unload(result, app_handle) => {
459 self.reset_process_loading_metadata();
460
461 self.unload_client.map(|client| {
462 client.unload_done(result, app_handle);
463 });
464 }
465 _ => {}
466 }
467 }
468
469 fn register(&'static self) {
470 self.deferred_call.register(self);
471 }
472}
473
474/// This is the callback client for the underlying physical storage driver.
475impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
476 NonvolatileStorageClient for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
477{
478 fn read_done(&self, _buffer: &'static mut [u8], _length: usize) {
479 // We will never use this, but we need to implement this anyway.
480 unimplemented!();
481 }
482
483 fn write_done(&self, buffer: &'static mut [u8], length: usize) {
484 match self.state.get() {
485 State::AppWrite => {
486 self.state.set(State::AppWrite);
487 // Switch on which user generated this callback and trigger
488 // client callback.
489 self.storage_client.map(|client| {
490 client.write_done(Ok(()), buffer, length);
491 });
492 }
493 State::PaddingWrite => {
494 // Replace the buffer after the padding is written.
495 self.reset_process_loading_metadata();
496 self.buffer.replace(buffer);
497 }
498 State::Fail => {
499 // If we failed at any of writing, we want to set the state to
500 // PaddingWrite so that the callback after writing the padding
501 // app will get triggererd.
502 self.buffer.replace(buffer);
503 if let Some(metadata) = self.process_metadata.get() {
504 let _ = self
505 .write_padding_app(metadata.new_app_length, metadata.new_app_start_addr);
506 }
507 // Clear all metadata specific to this load.
508 self.reset_process_loading_metadata();
509 }
510 State::Setup => {
511 // We have finished writing the post app padding.
512 self.buffer.replace(buffer);
513
514 if let Some(mut metadata) = self.process_metadata.get() {
515 if !metadata.setup_padding {
516 // Write padding header to the beginning of the new app address.
517 // This ensures that the linked list is not broken in the event of a
518 // powercycle before the app is fully written and loaded.
519 metadata.setup_padding = true;
520 let _ = self.write_padding_app(
521 metadata.new_app_length,
522 metadata.new_app_start_addr,
523 );
524 self.process_metadata.set(metadata);
525 } else {
526 self.state.set(State::AppWrite);
527 // Let the client know we are done setting up.
528 self.storage_client.map(|client| {
529 client.setup_done(Ok(()));
530 });
531 }
532 }
533 }
534 State::Load => {
535 // We finished writing pre-padding and we need to Load the app.
536 self.buffer.replace(buffer);
537 self.storage_client.map(|client| {
538 client.finalize_done(Ok(()));
539 });
540 }
541 State::Abort => {
542 self.buffer.replace(buffer);
543 // Reset metadata and let client know we are done aborting.
544 self.reset_process_loading_metadata();
545 self.storage_client.map(|client| {
546 client.abort_done(Ok(()));
547 });
548 }
549 State::Unload(_, _) => {
550 self.buffer.replace(buffer);
551 }
552 State::Uninstall => {
553 self.buffer.replace(buffer);
554 if let Some(metadata) = self.process_metadata.get() {
555 if metadata.erase_chunk_offset < metadata.uninstall_app_end_address {
556 // We are still within the bounds of this application, so let
557 // us erase it.
558 let _ = self.erase_chunk(metadata.erase_chunk_offset, BUF_LEN);
559 } else {
560 // Reset metadata and let client know we are done uninstalling the app.
561 self.reset_process_loading_metadata();
562 self.uninstall_client.map(|client| {
563 client.uninstall_done(Ok(()));
564 });
565 }
566 }
567 }
568 State::Idle => {
569 self.buffer.replace(buffer);
570 }
571 }
572 }
573}
574
575/// Callback client for the async process loader
576impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
577 ProcessLoadingAsyncClient for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
578{
579 fn process_loaded(&self, result: Result<(), ProcessLoadError>) {
580 self.load_client.map(|client| {
581 client.load_done(result);
582 });
583 }
584
585 fn process_loading_finished(&self) {
586 self.load_client.map(|client| {
587 client.load_done(Ok(()));
588 });
589 }
590}
591
592/// Storage interface exposed to the app_loader capsule
593impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
594 DynamicBinaryStore for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
595{
596 fn set_storage_client(&self, client: &'static dyn DynamicBinaryStoreClient) {
597 self.storage_client.set(client);
598 }
599
600 fn setup(&self, app_length: usize) -> Result<usize, ErrorCode> {
601 self.process_metadata.set(ProcessLoadMetadata::default());
602
603 if self.state.get() == State::Idle {
604 self.state.set(State::Setup);
605 match self.loader_driver.check_flash_for_new_address(app_length) {
606 Ok((
607 new_app_start_address,
608 padding_requirement,
609 previous_app_end_addr,
610 next_app_start_addr,
611 )) => {
612 if let Some(mut metadata) = self.process_metadata.get() {
613 metadata.new_app_start_addr = new_app_start_address;
614 metadata.new_app_length = app_length;
615 metadata.previous_app_end_addr = previous_app_end_addr;
616 metadata.next_app_start_addr = next_app_start_addr;
617 metadata.padding_requirement = padding_requirement;
618 self.process_metadata.set(metadata);
619 }
620
621 match padding_requirement {
622 // If we decided we need to write a padding app after
623 // the new app, we go ahead and do it.
624 PaddingRequirement::PostPad | PaddingRequirement::PreAndPostPad => {
625 // Calculating the distance between our app and
626 // either the next app.
627 let new_app_end_address = new_app_start_address + app_length;
628 let post_pad_length = next_app_start_addr - new_app_end_address;
629
630 let padding_result =
631 self.write_padding_app(post_pad_length, new_app_end_address);
632 let _ = match padding_result {
633 Ok(()) => Ok(()),
634 Err(e) => {
635 // This means we were unable to write the
636 // padding app.
637 self.reset_process_loading_metadata();
638 Err(e)
639 }
640 };
641 }
642 // Otherwise we let the client know we are done with the
643 // setup, and we are ready to write the app to flash.
644 PaddingRequirement::None | PaddingRequirement::PrePad => {
645 if let Some(mut metadata) = self.process_metadata.get() {
646 if !metadata.setup_padding {
647 // Write padding header to the beginning of the new app address.
648 // This ensures that the linked list is not broken in the event of a
649 // powercycle before the app is fully written and loaded.
650
651 metadata.setup_padding = true;
652 let _ = self.write_padding_app(
653 metadata.new_app_length,
654 metadata.new_app_start_addr,
655 );
656 self.process_metadata.set(metadata);
657 }
658 }
659 }
660 }
661 Ok(app_length)
662 }
663 Err(_err) => {
664 // Reset the state to None because we did not find any
665 // available address for this app.
666 self.reset_process_loading_metadata();
667 Err(ErrorCode::FAIL)
668 }
669 }
670 } else {
671 // We are in the wrong mode of operation. Ideally we should never reach
672 // here, but this error exists as a failsafe. The capsule should send
673 // a busy error out to the userland app.
674 Err(ErrorCode::INVAL)
675 }
676 }
677
678 fn write(
679 &self,
680 buffer: SubSliceMut<'static, u8>,
681 offset: usize,
682 ) -> Result<(), (ErrorCode, SubSliceMut<'static, u8>)> {
683 match self.state.get() {
684 State::AppWrite => {
685 let res = self.write_buffer(buffer, offset);
686 match res {
687 Ok(()) => Ok(()),
688 Err((e, buffer)) => {
689 // If we fail here, let us erase the app we just wrote.
690 self.state.set(State::Fail);
691 Err((e, buffer))
692 }
693 }
694 }
695 _ => {
696 // We are in the wrong mode of operation. Ideally we should never reach
697 // here, but this error exists as a failsafe. The capsule should send
698 // a busy error out to the userland app.
699 Err((ErrorCode::INVAL, buffer))
700 }
701 }
702 }
703
704 fn finalize(&self) -> Result<(), ErrorCode> {
705 match self.state.get() {
706 State::AppWrite => {
707 if let Some(metadata) = self.process_metadata.get() {
708 match metadata.padding_requirement {
709 // If we decided we need to write a padding app before the new
710 // app, we go ahead and do it.
711 PaddingRequirement::PrePad | PaddingRequirement::PreAndPostPad => {
712 // Calculate the distance between our app and the previous
713 // app.
714 let previous_app_end_addr = metadata.previous_app_end_addr;
715 let pre_pad_length =
716 metadata.new_app_start_addr - previous_app_end_addr;
717 self.state.set(State::Load);
718 let padding_result =
719 self.write_padding_app(pre_pad_length, previous_app_end_addr);
720 match padding_result {
721 Ok(()) => {
722 if config::CONFIG.debug_load_processes {
723 debug!("Successfully writing prepadding app");
724 }
725 Ok(())
726 }
727 Err(_e) => {
728 // This means we were unable to write the padding
729 // app.
730 self.reset_process_loading_metadata();
731 Err(ErrorCode::FAIL)
732 }
733 }
734 }
735 // We should never reach here if we are not writing a prepad
736 // app.
737 PaddingRequirement::None | PaddingRequirement::PostPad => {
738 if config::CONFIG.debug_load_processes {
739 debug!("No PrePad app to write.");
740 }
741 self.state.set(State::Load);
742 self.deferred_call.set();
743 Ok(())
744 }
745 }
746 } else {
747 Err(ErrorCode::INVAL)
748 }
749 }
750 _ => Err(ErrorCode::INVAL),
751 }
752 }
753
754 fn abort(&self) -> Result<(), ErrorCode> {
755 match self.state.get() {
756 State::Setup | State::AppWrite => {
757 self.state.set(State::Abort);
758 if let Some(metadata) = self.process_metadata.get() {
759 // Write padding header to the beginning of the new app address.
760 // This ensures that the flash space is reclaimed for future use.
761 match self
762 .write_padding_app(metadata.new_app_length, metadata.new_app_start_addr)
763 {
764 Ok(()) => Ok(()),
765 // If abort() returns ErrorCode::BUSY,
766 // the userland app is expected to retry abort.
767 Err(_) => Err(ErrorCode::BUSY),
768 }
769 } else {
770 Err(ErrorCode::FAIL)
771 }
772 }
773 _ => {
774 // We are in the wrong mode of operation. Ideally we should never reach
775 // here, but this error exists as a failsafe. The capsule should send
776 // a busy error out to the userland app.
777 Err(ErrorCode::INVAL)
778 }
779 }
780 }
781}
782
783/// Loading interface exposed to the app_loader capsule
784impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
785 DynamicProcessLoad for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
786{
787 fn set_load_client(&self, client: &'static dyn DynamicProcessLoadClient) {
788 self.load_client.set(client);
789 }
790
791 fn load(&self) -> Result<(), ErrorCode> {
792 // We have finished writing the last user data segment, next step is to
793 // load the process.
794 match self.state.get() {
795 State::Load => {
796 if let Some(metadata) = self.process_metadata.get() {
797 let _ = match self.loader_driver.load_new_process_binary(
798 metadata.new_app_start_addr,
799 metadata.new_app_length,
800 ) {
801 Ok(()) => Ok::<(), ProcessLoadError>(()),
802 Err(_e) => {
803 self.reset_process_loading_metadata();
804 return Err(ErrorCode::FAIL);
805 }
806 };
807 } else {
808 self.reset_process_loading_metadata();
809 return Err(ErrorCode::FAIL);
810 }
811 self.reset_process_loading_metadata();
812 Ok(())
813 }
814 _ => Err(ErrorCode::INVAL),
815 }
816 }
817}
818
819/// Loading interface exposed to the app_loader capsule
820impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
821 DynamicProcessUnload for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
822{
823 fn set_unload_client(&self, client: &'static dyn DynamicProcessUnloadClient) {
824 self.unload_client.set(client);
825 }
826
827 fn unload(&self, app: ShortId) -> Result<(), ErrorCode> {
828 match self.state.get() {
829 State::Idle => {
830 self.state.set(State::Unload(Err(ErrorCode::BUSY), 0)); // To ensure the state machine knows not to service other apps
831
832 let (result, _app_handle) = match self
833 .kernel
834 .remove_process_from_active_processes(app, |proc| {
835 proc.get_addresses().flash_start
836 }) {
837 Ok(id) => {
838 let res = Ok(());
839 let handle = id;
840
841 self.state.set(State::Unload(res, handle));
842 self.deferred_call.set();
843
844 (res, handle)
845 }
846 Err(()) => (Err(ErrorCode::INVAL), 0),
847 };
848
849 result
850 }
851 _ => {
852 // We are in the wrong mode of operation. Ideally we should never reach
853 // here, but this error exists as a failsafe. The capsule should send
854 // a busy error out to the userland app.
855 Err(ErrorCode::BUSY)
856 }
857 }
858 }
859}
860
861/// Uninstall interface exposed to the app_loader capsule
862impl<'b, C: Chip + 'static, D: ProcessStandardDebug + 'static, F: NonvolatileStorage<'b>>
863 DynamicProcessUninstall for SequentialDynamicBinaryStorage<'_, 'b, C, D, F>
864{
865 fn set_uninstall_client(&self, client: &'static dyn DynamicProcessUninstallClient) {
866 self.uninstall_client.set(client);
867 }
868
869 fn uninstall_with_app_handle(&self, app_handle: usize) -> Result<(), ErrorCode> {
870 match self.state.get() {
871 State::Idle => {
872 self.state.set(State::Uninstall);
873 self.process_metadata.set(ProcessLoadMetadata::default());
874
875 // This relation is true for this implementation, however,
876 // app_handle could encompass other identifiers.
877 let application_binary_address = app_handle;
878
879 let application_binary_size =
880 match self.loader_driver.fetch_application_binary_size(app_handle) {
881 Ok(app_size) => app_size as usize,
882 Err(_) => {
883 self.reset_process_loading_metadata();
884 return Err(ErrorCode::FAIL);
885 }
886 };
887
888 let padding_result =
889 self.write_padding_app(application_binary_size, application_binary_address);
890 let _ = match padding_result {
891 Ok(()) => {
892 if let Some(mut metadata) = self.process_metadata.get() {
893 metadata.erase_chunk_offset = application_binary_address + BUF_LEN;
894 metadata.uninstall_app_end_address =
895 application_binary_address + application_binary_size;
896 self.process_metadata.set(metadata);
897 }
898 Ok(())
899 }
900 Err(_) => {
901 // This means we were unable to write the
902 // padding app.
903 self.reset_process_loading_metadata();
904 Err(ErrorCode::FAIL)
905 }
906 };
907 padding_result
908 }
909 _ => {
910 // We are in the wrong mode of operation. Ideally we should never reach
911 // here, but this error exists as a failsafe. The capsule should send
912 // a busy error out to the userland app.
913 Err(ErrorCode::BUSY)
914 }
915 }
916 }
917}