kernel/process_loading.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 2022.
4
5//! Helper functions and machines for loading process binaries into in-memory
6//! Tock processes.
7//!
8//! Process loaders are responsible for parsing the binary formats of Tock
9//! processes, checking whether they are allowed to be loaded, and if so
10//! initializing a process structure to run it.
11//!
12//! This module provides multiple process loader options depending on which
13//! features a particular board requires.
14
15use core::cell::Cell;
16use core::fmt;
17
18use crate::capabilities::ProcessManagementCapability;
19use crate::config;
20use crate::debug;
21use crate::deferred_call::{DeferredCall, DeferredCallClient};
22use crate::kernel::Kernel;
23use crate::platform::chip::Chip;
24use crate::process::{Process, ShortId};
25use crate::process_binary::{ProcessBinary, ProcessBinaryError};
26use crate::process_checker::AcceptedCredential;
27use crate::process_checker::{AppIdPolicy, ProcessCheckError, ProcessCheckerMachine};
28use crate::process_policies::ProcessFaultPolicy;
29use crate::process_policies::ProcessStandardStoragePermissionsPolicy;
30use crate::process_standard::ProcessStandard;
31use crate::process_standard::{ProcessStandardDebug, ProcessStandardDebugFull};
32use crate::utilities::cells::{MapCell, OptionalCell};
33
34/// Errors that can occur when trying to load and create processes.
35pub enum ProcessLoadError {
36 /// Not enough memory to meet the amount requested by a process. Modify the
37 /// process to request less memory, flash fewer processes, or increase the
38 /// size of the region your board reserves for process memory.
39 NotEnoughMemory,
40
41 /// A process was loaded with a length in flash that the MPU does not
42 /// support. The fix is probably to correct the process size, but this could
43 /// also be caused by a bad MPU implementation.
44 MpuInvalidFlashLength,
45
46 /// The MPU configuration failed for some other, unspecified reason. This
47 /// could be of an internal resource exhaustion, or a mismatch between the
48 /// (current) MPU constraints and process requirements.
49 MpuConfigurationError,
50
51 /// A process specified a fixed memory address that it needs its memory
52 /// range to start at, and the kernel did not or could not give the process
53 /// a memory region starting at that address.
54 MemoryAddressMismatch {
55 actual_address: u32,
56 expected_address: u32,
57 },
58
59 /// There is nowhere in the `PROCESSES` array to store this process.
60 NoProcessSlot,
61
62 /// Process loading failed because parsing the binary failed.
63 BinaryError(ProcessBinaryError),
64
65 /// Process loading failed because checking the process failed.
66 CheckError(ProcessCheckError),
67
68 /// Process loading error due (likely) to a bug in the kernel. If you get
69 /// this error please open a bug report.
70 InternalError,
71}
72
73impl fmt::Debug for ProcessLoadError {
74 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 match self {
76 ProcessLoadError::NotEnoughMemory => {
77 write!(f, "Not able to provide RAM requested by app")
78 }
79
80 ProcessLoadError::MpuInvalidFlashLength => {
81 write!(f, "App flash length not supported by MPU")
82 }
83
84 ProcessLoadError::MpuConfigurationError => {
85 write!(f, "Configuring the MPU failed")
86 }
87
88 ProcessLoadError::MemoryAddressMismatch {
89 actual_address,
90 expected_address,
91 } => write!(
92 f,
93 "App memory does not match requested address Actual:{:#x}, Expected:{:#x}",
94 actual_address, expected_address
95 ),
96
97 ProcessLoadError::NoProcessSlot => {
98 write!(f, "Nowhere to store the loaded process")
99 }
100
101 ProcessLoadError::BinaryError(binary_error) => {
102 writeln!(f, "Error parsing process binary")?;
103 write!(f, "{:?}", binary_error)
104 }
105
106 ProcessLoadError::CheckError(check_error) => {
107 writeln!(f, "Error checking process")?;
108 write!(f, "{:?}", check_error)
109 }
110
111 ProcessLoadError::InternalError => write!(f, "Error in kernel. Likely a bug."),
112 }
113 }
114}
115
116////////////////////////////////////////////////////////////////////////////////
117// SYNCHRONOUS PROCESS LOADING
118////////////////////////////////////////////////////////////////////////////////
119
120/// Load processes into runnable process structures.
121///
122/// Load processes (stored as TBF objects in flash) into runnable process
123/// structures stored in the `procs` array and mark all successfully loaded
124/// processes as runnable. This method does not check the cryptographic
125/// credentials of TBF objects. Platforms for which code size is tight and do
126/// not need to check TBF credentials can call this method because it results in
127/// a smaller kernel, as it does not invoke the credential checking state
128/// machine.
129///
130/// This function is made `pub` so that board files can use it, but loading
131/// processes from slices of flash an memory is fundamentally unsafe. Therefore,
132/// we require the `ProcessManagementCapability` to call this function.
133// Mark inline always to reduce code size. Since this is only called in one
134// place (a board's main.rs), by inlining the load_*processes() functions, the
135// compiler can elide many checks which reduces code size appreciably. Note,
136// however, these functions require a rather large stack frame, which may be an
137// issue for boards small kernel stacks.
138#[inline(always)]
139pub fn load_processes<C: Chip>(
140 kernel: &'static Kernel,
141 chip: &'static C,
142 app_flash: &'static [u8],
143 app_memory: &'static mut [u8],
144 mut procs: &'static mut [Option<&'static dyn Process>],
145 fault_policy: &'static dyn ProcessFaultPolicy,
146 _capability_management: &dyn ProcessManagementCapability,
147) -> Result<(), ProcessLoadError> {
148 load_processes_from_flash::<C, ProcessStandardDebugFull>(
149 kernel,
150 chip,
151 app_flash,
152 app_memory,
153 &mut procs,
154 fault_policy,
155 )?;
156
157 if config::CONFIG.debug_process_credentials {
158 debug!("Checking: no checking, load and run all processes");
159 }
160 for proc in procs.iter() {
161 proc.map(|p| {
162 if config::CONFIG.debug_process_credentials {
163 debug!("Running {}", p.get_process_name());
164 }
165 });
166 }
167 Ok(())
168}
169
170/// Helper function to load processes from flash into an array of active
171/// processes. This is the default template for loading processes, but a board
172/// is able to create its own `load_processes()` function and use that instead.
173///
174/// Processes are found in flash starting from the given address and iterating
175/// through Tock Binary Format (TBF) headers. Processes are given memory out of
176/// the `app_memory` buffer until either the memory is exhausted or the
177/// allocated number of processes are created. This buffer is a non-static slice,
178/// ensuring that this code cannot hold onto the slice past the end of this function
179/// (instead, processes store a pointer and length), which necessary for later
180/// creation of `ProcessBuffer`s in this memory region to be sound.
181/// A reference to each process is stored in the provided `procs` array.
182/// How process faults are handled by the
183/// kernel must be provided and is assigned to every created process.
184///
185/// Returns `Ok(())` if process discovery went as expected. Returns a
186/// `ProcessLoadError` if something goes wrong during TBF parsing or process
187/// creation.
188#[inline(always)]
189fn load_processes_from_flash<C: Chip, D: ProcessStandardDebug + 'static>(
190 kernel: &'static Kernel,
191 chip: &'static C,
192 app_flash: &'static [u8],
193 app_memory: &'static mut [u8],
194 procs: &mut &'static mut [Option<&'static dyn Process>],
195 fault_policy: &'static dyn ProcessFaultPolicy,
196) -> Result<(), ProcessLoadError> {
197 if config::CONFIG.debug_load_processes {
198 debug!(
199 "Loading processes from flash={:#010X}-{:#010X} into sram={:#010X}-{:#010X}",
200 app_flash.as_ptr() as usize,
201 app_flash.as_ptr() as usize + app_flash.len() - 1,
202 app_memory.as_ptr() as usize,
203 app_memory.as_ptr() as usize + app_memory.len() - 1
204 );
205 }
206
207 let mut remaining_flash = app_flash;
208 let mut remaining_memory = app_memory;
209 // Try to discover up to `procs.len()` processes in flash.
210 let mut index = 0;
211 let num_procs = procs.len();
212 while index < num_procs {
213 let load_binary_result = discover_process_binary(remaining_flash);
214
215 match load_binary_result {
216 Ok((new_flash, process_binary)) => {
217 remaining_flash = new_flash;
218
219 let load_result = load_process::<C, D>(
220 kernel,
221 chip,
222 process_binary,
223 remaining_memory,
224 ShortId::LocallyUnique,
225 index,
226 fault_policy,
227 &(),
228 );
229 match load_result {
230 Ok((new_mem, proc)) => {
231 remaining_memory = new_mem;
232 match proc {
233 Some(p) => {
234 if config::CONFIG.debug_load_processes {
235 debug!("Loaded process {}", p.get_process_name())
236 }
237 procs[index] = proc;
238 index += 1;
239 }
240 None => {
241 if config::CONFIG.debug_load_processes {
242 debug!("No process loaded.");
243 }
244 }
245 }
246 }
247 Err((new_mem, err)) => {
248 remaining_memory = new_mem;
249 if config::CONFIG.debug_load_processes {
250 debug!("Processes load error: {:?}.", err);
251 }
252 }
253 }
254 }
255 Err((new_flash, err)) => {
256 remaining_flash = new_flash;
257 match err {
258 ProcessBinaryError::NotEnoughFlash | ProcessBinaryError::TbfHeaderNotFound => {
259 if config::CONFIG.debug_load_processes {
260 debug!("No more processes to load: {:?}.", err);
261 }
262 // No more processes to load.
263 break;
264 }
265
266 ProcessBinaryError::TbfHeaderParseFailure(_)
267 | ProcessBinaryError::IncompatibleKernelVersion { .. }
268 | ProcessBinaryError::IncorrectFlashAddress { .. }
269 | ProcessBinaryError::NotEnabledProcess
270 | ProcessBinaryError::Padding => {
271 if config::CONFIG.debug_load_processes {
272 debug!("Unable to use process binary: {:?}.", err);
273 }
274
275 // Skip this binary and move to the next one.
276 continue;
277 }
278 }
279 }
280 }
281 }
282 Ok(())
283}
284
285////////////////////////////////////////////////////////////////////////////////
286// HELPER FUNCTIONS
287////////////////////////////////////////////////////////////////////////////////
288
289/// Find a process binary stored at the beginning of `flash` and create a
290/// `ProcessBinary` object if the process is viable to run on this kernel.
291fn discover_process_binary(
292 flash: &'static [u8],
293) -> Result<(&'static [u8], ProcessBinary), (&'static [u8], ProcessBinaryError)> {
294 if config::CONFIG.debug_load_processes {
295 debug!(
296 "Looking for process binary in flash={:#010X}-{:#010X}",
297 flash.as_ptr() as usize,
298 flash.as_ptr() as usize + flash.len() - 1
299 );
300 }
301
302 // If this fails, not enough remaining flash to check for an app.
303 let test_header_slice = flash
304 .get(0..8)
305 .ok_or((flash, ProcessBinaryError::NotEnoughFlash))?;
306
307 // Pass the first eight bytes to tbfheader to parse out the length of
308 // the tbf header and app. We then use those values to see if we have
309 // enough flash remaining to parse the remainder of the header.
310 //
311 // Start by converting [u8] to [u8; 8].
312 let header = test_header_slice
313 .try_into()
314 .or(Err((flash, ProcessBinaryError::NotEnoughFlash)))?;
315
316 let (version, header_length, app_length) =
317 match tock_tbf::parse::parse_tbf_header_lengths(header) {
318 Ok((v, hl, el)) => (v, hl, el),
319 Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(app_length)) => {
320 // If we could not parse the header, then we want to skip over
321 // this app and look for the next one.
322 (0, 0, app_length)
323 }
324 Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => {
325 // Since Tock apps use a linked list, it is very possible the
326 // header we started to parse is intentionally invalid to signal
327 // the end of apps. This is ok and just means we have finished
328 // loading apps.
329 return Err((flash, ProcessBinaryError::TbfHeaderNotFound));
330 }
331 };
332
333 // Now we can get a slice which only encompasses the length of flash
334 // described by this tbf header. We will either parse this as an actual
335 // app, or skip over this region.
336 let app_flash = flash
337 .get(0..app_length as usize)
338 .ok_or((flash, ProcessBinaryError::NotEnoughFlash))?;
339
340 // Advance the flash slice for process discovery beyond this last entry.
341 // This will be the start of where we look for a new process since Tock
342 // processes are allocated back-to-back in flash.
343 let remaining_flash = flash
344 .get(app_flash.len()..)
345 .ok_or((flash, ProcessBinaryError::NotEnoughFlash))?;
346
347 let pb = ProcessBinary::create(app_flash, header_length as usize, version, true)
348 .map_err(|e| (remaining_flash, e))?;
349
350 Ok((remaining_flash, pb))
351}
352
353/// Load a process stored as a TBF process binary with `app_memory` as the RAM
354/// pool that its RAM should be allocated from. Returns `Ok` if the process
355/// object was created, `Err` with a relevant error if the process object could
356/// not be created.
357fn load_process<C: Chip, D: ProcessStandardDebug>(
358 kernel: &'static Kernel,
359 chip: &'static C,
360 process_binary: ProcessBinary,
361 app_memory: &'static mut [u8],
362 app_id: ShortId,
363 index: usize,
364 fault_policy: &'static dyn ProcessFaultPolicy,
365 storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy<C, D>,
366) -> Result<(&'static mut [u8], Option<&'static dyn Process>), (&'static mut [u8], ProcessLoadError)>
367{
368 if config::CONFIG.debug_load_processes {
369 debug!(
370 "Loading: process flash={:#010X}-{:#010X} ram={:#010X}-{:#010X}",
371 process_binary.flash.as_ptr() as usize,
372 process_binary.flash.as_ptr() as usize + process_binary.flash.len() - 1,
373 app_memory.as_ptr() as usize,
374 app_memory.as_ptr() as usize + app_memory.len() - 1
375 );
376 }
377
378 // Need to reassign remaining_memory in every iteration so the compiler
379 // knows it will not be re-borrowed.
380 // If we found an actual app header, try to create a `Process`
381 // object. We also need to shrink the amount of remaining memory
382 // based on whatever is assigned to the new process if one is
383 // created.
384
385 // Try to create a process object from that app slice. If we don't
386 // get a process and we didn't get a loading error (aka we got to
387 // this point), then the app is a disabled process or just padding.
388 let (process_option, unused_memory) = unsafe {
389 ProcessStandard::<C, D>::create(
390 kernel,
391 chip,
392 process_binary,
393 app_memory,
394 fault_policy,
395 storage_policy,
396 app_id,
397 index,
398 )
399 .map_err(|(e, memory)| (memory, e))?
400 };
401
402 process_option.map(|process| {
403 if config::CONFIG.debug_load_processes {
404 debug!(
405 "Loading: {} [{}] flash={:#010X}-{:#010X} ram={:#010X}-{:#010X}",
406 process.get_process_name(),
407 index,
408 process.get_addresses().flash_start,
409 process.get_addresses().flash_end,
410 process.get_addresses().sram_start,
411 process.get_addresses().sram_end - 1,
412 );
413 }
414 });
415
416 Ok((unused_memory, process_option))
417}
418
419////////////////////////////////////////////////////////////////////////////////
420// ASYNCHRONOUS PROCESS LOADING
421////////////////////////////////////////////////////////////////////////////////
422
423/// Client for asynchronous process loading.
424///
425/// This supports a client that is notified after trying to load each process in
426/// flash. Also there is a callback for after all processes have been
427/// discovered.
428pub trait ProcessLoadingAsyncClient {
429 /// A process was successfully found in flash, checked, and loaded into a
430 /// `ProcessStandard` object.
431 fn process_loaded(&self, result: Result<(), ProcessLoadError>);
432
433 /// There are no more processes in flash to be loaded.
434 fn process_loading_finished(&self);
435}
436
437/// Asynchronous process loading.
438///
439/// Machines which implement this trait perform asynchronous process loading and
440/// signal completion through `ProcessLoadingAsyncClient`.
441///
442/// Various process loaders may exist. This includes a loader from a MCU's
443/// integrated flash, or a loader from an external flash chip.
444pub trait ProcessLoadingAsync<'a> {
445 /// Set the client to receive callbacks about process loading and when
446 /// process loading has finished.
447 fn set_client(&self, client: &'a dyn ProcessLoadingAsyncClient);
448
449 /// Set the credential checking policy for the loader.
450 fn set_policy(&self, policy: &'a dyn AppIdPolicy);
451
452 /// Start the process loading operation.
453 fn start(&self);
454}
455
456/// Operating mode of the loader.
457#[derive(Clone, Copy)]
458enum SequentialProcessLoaderMachineState {
459 /// Phase of discovering `ProcessBinary` objects in flash.
460 DiscoverProcessBinaries,
461 /// Phase of loading `ProcessBinary`s into `Process`es.
462 LoadProcesses,
463}
464
465/// Operating mode of the sequential process loader.
466///
467/// The loader supports loading processes from flash at boot, and loading processes
468/// that were written to flash dynamically at runtime. Most of the internal logic is the
469/// same (and therefore reused), but we need to track which mode of operation the
470/// loader is in.
471#[derive(Clone, Copy)]
472enum SequentialProcessLoaderMachineRunMode {
473 /// The loader was called by a board's main function at boot.
474 BootMode,
475 /// The loader was called by a dynamic process loader at runtime.
476 RuntimeMode,
477}
478
479/// Enum to hold the padding requirements for a new application.
480#[derive(Clone, Copy, PartialEq, Default)]
481pub enum PaddingRequirement {
482 #[default]
483 None,
484 PrePad,
485 PostPad,
486 PreAndPostPad,
487}
488
489/// A machine for loading processes stored sequentially in a region of flash.
490///
491/// Load processes (stored as TBF objects in flash) into runnable process
492/// structures stored in the `procs` array. This machine scans the footers in
493/// the TBF for cryptographic credentials for binary integrity, passing them to
494/// the checker to decide whether the process has sufficient credentials to run.
495pub struct SequentialProcessLoaderMachine<'a, C: Chip + 'static, D: ProcessStandardDebug + 'static>
496{
497 /// Client to notify as processes are loaded and process loading finishes after boot.
498 boot_client: OptionalCell<&'a dyn ProcessLoadingAsyncClient>,
499 /// Client to notify as processes are loaded and process loading finishes during runtime.
500 runtime_client: OptionalCell<&'a dyn ProcessLoadingAsyncClient>,
501 /// Machine to use to check process credentials.
502 checker: &'static ProcessCheckerMachine,
503 /// Array of stored process references for loaded processes.
504 procs: MapCell<&'static mut [Option<&'static dyn Process>]>,
505 /// Array to store `ProcessBinary`s after checking credentials.
506 proc_binaries: MapCell<&'static mut [Option<ProcessBinary>]>,
507 /// Total available flash for process binaries on this board.
508 flash_bank: Cell<&'static [u8]>,
509 /// Flash memory region to load processes from.
510 flash: Cell<&'static [u8]>,
511 /// Memory available to assign to applications.
512 app_memory: Cell<&'static mut [u8]>,
513 /// Mechanism for generating async callbacks.
514 deferred_call: DeferredCall,
515 /// Reference to the kernel object for creating Processes.
516 kernel: &'static Kernel,
517 /// Reference to the Chip object for creating Processes.
518 chip: &'static C,
519 /// The policy to use when determining ShortIds and process uniqueness.
520 policy: OptionalCell<&'a dyn AppIdPolicy>,
521 /// The fault policy to assign to each created Process.
522 fault_policy: &'static dyn ProcessFaultPolicy,
523 /// The storage permissions policy to assign to each created Process.
524 storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy<C, D>,
525 /// Current mode of the loading machine.
526 state: OptionalCell<SequentialProcessLoaderMachineState>,
527 /// Current operating mode of the loading machine.
528 run_mode: OptionalCell<SequentialProcessLoaderMachineRunMode>,
529}
530
531impl<'a, C: Chip, D: ProcessStandardDebug> SequentialProcessLoaderMachine<'a, C, D> {
532 /// This function is made `pub` so that board files can use it, but loading
533 /// processes from slices of flash an memory is fundamentally unsafe.
534 /// Therefore, we require the `ProcessManagementCapability` to call this
535 /// function.
536 pub fn new(
537 checker: &'static ProcessCheckerMachine,
538 procs: &'static mut [Option<&'static dyn Process>],
539 proc_binaries: &'static mut [Option<ProcessBinary>],
540 kernel: &'static Kernel,
541 chip: &'static C,
542 flash: &'static [u8],
543 app_memory: &'static mut [u8],
544 fault_policy: &'static dyn ProcessFaultPolicy,
545 storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy<C, D>,
546 policy: &'static dyn AppIdPolicy,
547 _capability_management: &dyn ProcessManagementCapability,
548 ) -> Self {
549 Self {
550 deferred_call: DeferredCall::new(),
551 checker,
552 boot_client: OptionalCell::empty(),
553 runtime_client: OptionalCell::empty(),
554 run_mode: OptionalCell::empty(),
555 procs: MapCell::new(procs),
556 proc_binaries: MapCell::new(proc_binaries),
557 kernel,
558 chip,
559 flash_bank: Cell::new(flash),
560 flash: Cell::new(flash),
561 app_memory: Cell::new(app_memory),
562 policy: OptionalCell::new(policy),
563 fault_policy,
564 storage_policy,
565 state: OptionalCell::empty(),
566 }
567 }
568
569 /// Set the runtime client to receive callbacks about process loading and when
570 /// process loading has finished.
571 pub fn set_runtime_client(&self, client: &'a dyn ProcessLoadingAsyncClient) {
572 self.runtime_client.set(client);
573 }
574
575 /// Find the current active client based on the operation mode.
576 fn get_current_client(&self) -> Option<&dyn ProcessLoadingAsyncClient> {
577 match self.run_mode.get()? {
578 SequentialProcessLoaderMachineRunMode::BootMode => self.boot_client.get(),
579 SequentialProcessLoaderMachineRunMode::RuntimeMode => self.runtime_client.get(),
580 }
581 }
582
583 /// Find a slot in the `PROCESSES` array to store this process.
584 fn find_open_process_slot(&self) -> Option<usize> {
585 self.procs.map_or(None, |procs| {
586 for (i, p) in procs.iter().enumerate() {
587 if p.is_none() {
588 return Some(i);
589 }
590 }
591 None
592 })
593 }
594
595 /// Find a slot in the `PROCESS_BINARIES` array to store this process.
596 fn find_open_process_binary_slot(&self) -> Option<usize> {
597 self.proc_binaries.map_or(None, |proc_bins| {
598 for (i, p) in proc_bins.iter().enumerate() {
599 if p.is_none() {
600 return Some(i);
601 }
602 }
603 None
604 })
605 }
606
607 fn load_and_check(&self) {
608 let ret = self.discover_process_binary();
609 match ret {
610 Ok(pb) => match self.checker.check(pb) {
611 Ok(()) => {}
612 Err(e) => {
613 self.get_current_client().map(|client| {
614 client.process_loaded(Err(ProcessLoadError::CheckError(e)));
615 });
616 }
617 },
618 Err(ProcessBinaryError::NotEnoughFlash)
619 | Err(ProcessBinaryError::TbfHeaderNotFound) => {
620 // These two errors occur when there are no more app binaries in
621 // flash. Now we can move to actually loading process binaries
622 // into full processes.
623
624 self.state
625 .set(SequentialProcessLoaderMachineState::LoadProcesses);
626 self.deferred_call.set();
627 }
628 Err(e) => {
629 if config::CONFIG.debug_load_processes {
630 debug!("Loading: unable to create ProcessBinary: {:?}", e);
631 }
632
633 // Other process binary errors indicate the process is not
634 // compatible. Signal error and try the next item in flash.
635 self.get_current_client().map(|client| {
636 client.process_loaded(Err(ProcessLoadError::BinaryError(e)));
637 });
638
639 self.deferred_call.set();
640 }
641 }
642 }
643
644 /// Try to parse a process binary from flash.
645 ///
646 /// Returns the process binary object or an error if a valid process
647 /// binary could not be extracted.
648 fn discover_process_binary(&self) -> Result<ProcessBinary, ProcessBinaryError> {
649 let flash = self.flash.get();
650
651 if config::CONFIG.debug_load_processes {
652 debug!(
653 "Looking for process binary in flash={:#010X}-{:#010X}",
654 flash.as_ptr() as usize,
655 flash.as_ptr() as usize + flash.len() - 1
656 );
657 }
658
659 // If this fails, not enough remaining flash to check for an app.
660 let test_header_slice = flash.get(0..8).ok_or(ProcessBinaryError::NotEnoughFlash)?;
661
662 // Pass the first eight bytes to tbfheader to parse out the length of
663 // the tbf header and app. We then use those values to see if we have
664 // enough flash remaining to parse the remainder of the header.
665 //
666 // Start by converting [u8] to [u8; 8].
667 let header = test_header_slice
668 .try_into()
669 .or(Err(ProcessBinaryError::NotEnoughFlash))?;
670
671 let (version, header_length, app_length) =
672 match tock_tbf::parse::parse_tbf_header_lengths(header) {
673 Ok((v, hl, el)) => (v, hl, el),
674 Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(app_length)) => {
675 // If we could not parse the header, then we want to skip over
676 // this app and look for the next one.
677 (0, 0, app_length)
678 }
679 Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => {
680 // Since Tock apps use a linked list, it is very possible the
681 // header we started to parse is intentionally invalid to signal
682 // the end of apps. This is ok and just means we have finished
683 // loading apps.
684 return Err(ProcessBinaryError::TbfHeaderNotFound);
685 }
686 };
687
688 // Now we can get a slice which only encompasses the length of flash
689 // described by this tbf header. We will either parse this as an actual
690 // app, or skip over this region.
691 let app_flash = flash
692 .get(0..app_length as usize)
693 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
694
695 // Advance the flash slice for process discovery beyond this last entry.
696 // This will be the start of where we look for a new process since Tock
697 // processes are allocated back-to-back in flash.
698 let remaining_flash = flash
699 .get(app_flash.len()..)
700 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
701 self.flash.set(remaining_flash);
702
703 let pb = ProcessBinary::create(app_flash, header_length as usize, version, true)?;
704
705 Ok(pb)
706 }
707
708 /// Create process objects from the discovered process binaries.
709 ///
710 /// This verifies that the discovered processes are valid to run.
711 fn load_process_objects(&self) -> Result<(), ()> {
712 let proc_binaries = self.proc_binaries.take().ok_or(())?;
713 let proc_binaries_len = proc_binaries.len();
714
715 // Iterate all process binary entries.
716 for i in 0..proc_binaries_len {
717 // We are either going to load this process binary or discard it, so
718 // we can use `take()` here.
719 if let Some(process_binary) = proc_binaries[i].take() {
720 // We assume the process can be loaded. This is not the case
721 // if there is a conflicting process.
722 let mut ok_to_load = true;
723
724 // Start by iterating all other process binaries and seeing
725 // if any are in conflict (same AppID with newer version).
726 for proc_bin in proc_binaries.iter() {
727 match proc_bin {
728 Some(other_process_binary) => {
729 let blocked = self
730 .is_blocked_from_loading_by(&process_binary, other_process_binary);
731
732 if blocked {
733 ok_to_load = false;
734 break;
735 }
736 }
737 None => {}
738 }
739 }
740
741 // Go to next ProcessBinary if we cannot load this process.
742 if !ok_to_load {
743 continue;
744 }
745
746 // Now scan the already loaded processes and make sure this
747 // doesn't conflict with any of those. Since those processes
748 // are already loaded, we just need to check if this process
749 // binary has the same AppID as an already loaded process.
750 self.procs.map(|procs| {
751 for proc in procs.iter() {
752 match proc {
753 Some(p) => {
754 let blocked =
755 self.is_blocked_from_loading_by_process(&process_binary, *p);
756
757 if blocked {
758 ok_to_load = false;
759 break;
760 }
761 }
762 None => {}
763 }
764 }
765 });
766
767 if !ok_to_load {
768 continue;
769 }
770
771 // If we get here it is ok to load the process.
772 match self.find_open_process_slot() {
773 Some(index) => {
774 // Calculate the ShortId for this new process.
775 let short_app_id = self.policy.map_or(ShortId::LocallyUnique, |policy| {
776 policy.to_short_id(&process_binary)
777 });
778
779 // Try to create a `Process` object.
780 let load_result = load_process(
781 self.kernel,
782 self.chip,
783 process_binary,
784 self.app_memory.take(),
785 short_app_id,
786 index,
787 self.fault_policy,
788 self.storage_policy,
789 );
790 match load_result {
791 Ok((new_mem, proc)) => {
792 self.app_memory.set(new_mem);
793 match proc {
794 Some(p) => {
795 if config::CONFIG.debug_load_processes {
796 debug!(
797 "Loading: Loaded process {}",
798 p.get_process_name()
799 )
800 }
801
802 // Store the `ProcessStandard` object in the `PROCESSES`
803 // array.
804 self.procs.map(|procs| {
805 procs[index] = proc;
806 });
807 // Notify the client the process was loaded
808 // successfully.
809 self.get_current_client().map(|client| {
810 client.process_loaded(Ok(()));
811 });
812 }
813 None => {
814 if config::CONFIG.debug_load_processes {
815 debug!("No process loaded.");
816 }
817 }
818 }
819 }
820 Err((new_mem, err)) => {
821 self.app_memory.set(new_mem);
822 if config::CONFIG.debug_load_processes {
823 debug!("Could not load process: {:?}.", err);
824 }
825 self.get_current_client().map(|client| {
826 client.process_loaded(Err(err));
827 });
828 }
829 }
830 }
831 None => {
832 // Nowhere to store the process.
833 self.get_current_client().map(|client| {
834 client.process_loaded(Err(ProcessLoadError::NoProcessSlot));
835 });
836 }
837 }
838 }
839 }
840 self.proc_binaries.put(proc_binaries);
841
842 // We have iterated all discovered `ProcessBinary`s and loaded what we
843 // could so now we can signal that process loading is finished.
844 self.get_current_client().map(|client| {
845 client.process_loading_finished();
846 });
847
848 self.state.clear();
849 Ok(())
850 }
851
852 /// Check if `pb1` is blocked from running by `pb2`.
853 ///
854 /// `pb2` blocks `pb1` if:
855 ///
856 /// - They both have the same AppID or they both have the same ShortId, and
857 /// - `pb2` has a higher version number.
858 fn is_blocked_from_loading_by(&self, pb1: &ProcessBinary, pb2: &ProcessBinary) -> bool {
859 let same_app_id = self
860 .policy
861 .map_or(false, |policy| !policy.different_identifier(pb1, pb2));
862 let same_short_app_id = self.policy.map_or(false, |policy| {
863 policy.to_short_id(pb1) == policy.to_short_id(pb2)
864 });
865 let other_newer = pb2.header.get_binary_version() > pb1.header.get_binary_version();
866
867 let blocks = (same_app_id || same_short_app_id) && other_newer;
868
869 if config::CONFIG.debug_process_credentials {
870 debug!(
871 "Loading: ProcessBinary {}({:#02x}) does{} block {}({:#02x})",
872 pb2.header.get_package_name().unwrap_or(""),
873 pb2.flash.as_ptr() as usize,
874 if blocks { " not" } else { "" },
875 pb1.header.get_package_name().unwrap_or(""),
876 pb1.flash.as_ptr() as usize,
877 );
878 }
879
880 blocks
881 }
882
883 /// Check if `pb` is blocked from running by `process`.
884 ///
885 /// `process` blocks `pb` if:
886 ///
887 /// - They both have the same AppID, or
888 /// - They both have the same ShortId
889 ///
890 /// Since `process` is already loaded, we only have to enforce the AppID and
891 /// ShortId uniqueness guarantees.
892 fn is_blocked_from_loading_by_process(
893 &self,
894 pb: &ProcessBinary,
895 process: &dyn Process,
896 ) -> bool {
897 let same_app_id = self.policy.map_or(false, |policy| {
898 !policy.different_identifier_process(pb, process)
899 });
900 let same_short_app_id = self.policy.map_or(false, |policy| {
901 policy.to_short_id(pb) == process.short_app_id()
902 });
903
904 let blocks = same_app_id || same_short_app_id;
905
906 if config::CONFIG.debug_process_credentials {
907 debug!(
908 "Loading: Process {}({:#02x}) does{} block {}({:#02x})",
909 process.get_process_name(),
910 process.get_addresses().flash_start,
911 if blocks { " not" } else { "" },
912 pb.header.get_package_name().unwrap_or(""),
913 pb.flash.as_ptr() as usize,
914 );
915 }
916
917 blocks
918 }
919
920 ////////////////////////////////////////////////////////////////////////////////
921 // DYNAMIC PROCESS LOADING HELPERS
922 ////////////////////////////////////////////////////////////////////////////////
923
924 /// Scan the entire flash to populate lists of existing binaries addresses.
925 fn scan_flash_for_process_binaries(
926 &self,
927 flash: &'static [u8],
928 process_binaries_start_addresses: &mut [usize],
929 process_binaries_end_addresses: &mut [usize],
930 ) -> Result<(), ()> {
931 fn inner_function(
932 flash: &'static [u8],
933 process_binaries_start_addresses: &mut [usize],
934 process_binaries_end_addresses: &mut [usize],
935 ) -> Result<(), ProcessBinaryError> {
936 let flash_end = flash.as_ptr() as usize + flash.len() - 1;
937 let mut addresses = flash.as_ptr() as usize;
938 let mut index: usize = 0;
939
940 while addresses < flash_end {
941 let flash_offset = addresses - flash.as_ptr() as usize;
942
943 let test_header_slice = flash
944 .get(flash_offset..flash_offset + 8)
945 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
946
947 let header = test_header_slice
948 .try_into()
949 .or(Err(ProcessBinaryError::NotEnoughFlash))?;
950
951 let (_version, header_length, app_length) =
952 match tock_tbf::parse::parse_tbf_header_lengths(header) {
953 Ok((v, hl, el)) => (v, hl, el),
954 Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(app_length)) => {
955 (0, 0, app_length)
956 }
957 Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => {
958 return Ok(());
959 }
960 };
961
962 let app_flash = flash
963 .get(flash_offset..flash_offset + app_length as usize)
964 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
965
966 let app_header = flash
967 .get(flash_offset..flash_offset + header_length as usize)
968 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
969
970 let remaining_flash = flash
971 .get(flash_offset + app_flash.len()..)
972 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
973
974 // Get the rest of the header. The `remaining_header` variable
975 // will continue to hold the remainder of the header we have
976 // not processed.
977 let remaining_header = app_header
978 .get(16..)
979 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
980
981 if remaining_header.len() == 0 {
982 // This is a padding app.
983 if config::CONFIG.debug_load_processes {
984 debug!("Is padding!");
985 }
986 } else {
987 // This is an app binary, add it to the pb arrays.
988 process_binaries_start_addresses[index] = app_flash.as_ptr() as usize;
989 process_binaries_end_addresses[index] =
990 app_flash.as_ptr() as usize + app_length as usize;
991
992 if config::CONFIG.debug_load_processes {
993 debug!(
994 "[Metadata] Process binary start address at index {}: {:#010x}, with end_address {:#010x}",
995 index,
996 process_binaries_start_addresses[index],
997 process_binaries_end_addresses[index]
998 );
999 }
1000 index += 1;
1001 if index > process_binaries_start_addresses.len() - 1 {
1002 return Err(ProcessBinaryError::NotEnoughFlash);
1003 }
1004 }
1005 addresses = remaining_flash.as_ptr() as usize;
1006 }
1007
1008 Ok(())
1009 }
1010
1011 inner_function(
1012 flash,
1013 process_binaries_start_addresses,
1014 process_binaries_end_addresses,
1015 )
1016 .or(Err(()))
1017 }
1018
1019 /// Helper function to find the next potential aligned address for the
1020 /// new app with size `app_length` assuming Cortex-M alignment rules.
1021 fn find_next_cortex_m_aligned_address(&self, address: usize, app_length: usize) -> usize {
1022 let remaining = address % app_length;
1023 if remaining == 0 {
1024 address
1025 } else {
1026 address + (app_length - remaining)
1027 }
1028 }
1029
1030 /// Function to compute the address for a new app with size `app_size`.
1031 fn compute_new_process_binary_address(
1032 &self,
1033 app_size: usize,
1034 process_binaries_start_addresses: &mut [usize],
1035 process_binaries_end_addresses: &mut [usize],
1036 ) -> usize {
1037 let mut start_count = 0;
1038 let mut end_count = 0;
1039
1040 // Remove zeros from addresses in place.
1041 for i in 0..process_binaries_start_addresses.len() {
1042 if process_binaries_start_addresses[i] != 0 {
1043 process_binaries_start_addresses[start_count] = process_binaries_start_addresses[i];
1044 start_count += 1;
1045 }
1046 }
1047
1048 for i in 0..process_binaries_end_addresses.len() {
1049 if process_binaries_end_addresses[i] != 0 {
1050 process_binaries_end_addresses[end_count] = process_binaries_end_addresses[i];
1051 end_count += 1;
1052 }
1053 }
1054
1055 // If there is only one application in flash:
1056 if start_count == 1 {
1057 let potential_address = self
1058 .find_next_cortex_m_aligned_address(process_binaries_end_addresses[0], app_size);
1059 return potential_address;
1060 }
1061
1062 // Otherwise, iterate through the sorted start and end addresses to find gaps for the new app.
1063 for i in 0..start_count - 1 {
1064 let gap_start = process_binaries_end_addresses[i];
1065 let gap_end = process_binaries_start_addresses[i + 1];
1066
1067 // Ensure gap_end is valid (skip zeros - these indicate there are no process binaries).
1068 if gap_end == 0 {
1069 continue;
1070 }
1071
1072 // If there is a valid gap, i.e., (gap_end > gap_start), check alignment.
1073 if gap_end > gap_start {
1074 let potential_address =
1075 self.find_next_cortex_m_aligned_address(gap_start, app_size);
1076 if potential_address + app_size < gap_end {
1077 return potential_address;
1078 }
1079 }
1080 }
1081 // If no gaps found, check after the last app.
1082 let last_app_end_address = process_binaries_end_addresses[end_count - 1];
1083 let potential_address =
1084 self.find_next_cortex_m_aligned_address(last_app_end_address, app_size);
1085 potential_address
1086 }
1087
1088 /// This function checks if there is a need to pad either before or after
1089 /// the new app to preserve the linked list.
1090 ///
1091 /// When do we pad?
1092 ///
1093 /// 1. When there is a binary located in flash after the new app but
1094 /// not immediately after, we need to add padding between the new
1095 /// app and the existing app.
1096 /// 2. Due to MPU alignment, the new app may be similarly placed not
1097 /// immediately after an existing process, in that case, we need to add
1098 /// padding between the previous app and the new app.
1099 /// 3. If both the above conditions are met, we add both a prepadding and a
1100 /// postpadding.
1101 /// 4. If either of these conditions are not met, we don't pad.
1102 ///
1103 /// Change checks against process binaries instead of processes?
1104 fn compute_padding_requirement_and_neighbors(
1105 &self,
1106 new_app_start_address: usize,
1107 app_length: usize,
1108 process_binaries_start_addresses: &[usize],
1109 process_binaries_end_addresses: &[usize],
1110 ) -> (PaddingRequirement, usize, usize) {
1111 // The end address of our newly loaded application.
1112 let new_app_end_address = new_app_start_address + app_length;
1113 // To store the address until which we need to write the padding app.
1114 let mut next_app_start_addr = 0;
1115 // To store the address from which we need to write the padding app.
1116 let mut previous_app_end_addr = 0;
1117 let mut padding_requirement: PaddingRequirement = PaddingRequirement::None;
1118
1119 // We compute the closest neighbor to our app such that:
1120 //
1121 // 1. If the new app is placed in between two existing binaries, we
1122 // compute the closest located binaries.
1123 // 2. Once we compute these values, we determine if we need to write a
1124 // pre pad header, or a post pad header, or both.
1125 // 3. If there are no apps after ours in the process binary array, we don't
1126 // do anything.
1127
1128 // Postpad requirement.
1129 if let Some(next_closest_neighbor) = process_binaries_start_addresses
1130 .iter()
1131 .filter(|&&x| x > new_app_end_address - 1)
1132 .min()
1133 {
1134 // We found the next closest app in flash.
1135 next_app_start_addr = *next_closest_neighbor;
1136 if next_app_start_addr != 0 {
1137 padding_requirement = PaddingRequirement::PostPad;
1138 }
1139 } else {
1140 if config::CONFIG.debug_load_processes {
1141 debug!("No App Found after the new app so not adding post padding.");
1142 }
1143 }
1144
1145 // Prepad requirement.
1146 if let Some(previous_closest_neighbor) = process_binaries_end_addresses
1147 .iter()
1148 .filter(|&&x| x < new_app_start_address + 1)
1149 .max()
1150 {
1151 // We found the previous closest app in flash.
1152 previous_app_end_addr = *previous_closest_neighbor;
1153 if new_app_start_address - previous_app_end_addr != 0 {
1154 if padding_requirement == PaddingRequirement::PostPad {
1155 padding_requirement = PaddingRequirement::PreAndPostPad;
1156 } else {
1157 padding_requirement = PaddingRequirement::PrePad;
1158 }
1159 }
1160 } else {
1161 if config::CONFIG.debug_load_processes {
1162 debug!("No Previous App Found, so not padding before the new app.");
1163 }
1164 }
1165 (
1166 padding_requirement,
1167 previous_app_end_addr,
1168 next_app_start_addr,
1169 )
1170 }
1171
1172 /// This function scans flash, checks for, and returns an address that follows alignment rules given
1173 /// an app size of `new_app_size`.
1174 fn check_flash_for_valid_address(
1175 &self,
1176 new_app_size: usize,
1177 pb_start_address: &mut [usize],
1178 pb_end_address: &mut [usize],
1179 ) -> Result<usize, ProcessBinaryError> {
1180 let total_flash = self.flash_bank.get();
1181 let total_flash_start = total_flash.as_ptr() as usize;
1182 let total_flash_end = total_flash_start + total_flash.len() - 1;
1183
1184 match self.scan_flash_for_process_binaries(total_flash, pb_start_address, pb_end_address) {
1185 Ok(()) => {
1186 if config::CONFIG.debug_load_processes {
1187 debug!("Successfully scanned flash");
1188 }
1189 let new_app_address = self.compute_new_process_binary_address(
1190 new_app_size,
1191 pb_start_address,
1192 pb_end_address,
1193 );
1194 if new_app_address + new_app_size - 1 > total_flash_end {
1195 Err(ProcessBinaryError::NotEnoughFlash)
1196 } else {
1197 Ok(new_app_address)
1198 }
1199 }
1200 Err(()) => Err(ProcessBinaryError::NotEnoughFlash),
1201 }
1202 }
1203
1204 /// Function to check if the object with address `offset` of size `length` lies
1205 /// within flash bounds.
1206 pub fn check_if_within_flash_bounds(&self, offset: usize, length: usize) -> bool {
1207 let flash = self.flash_bank.get();
1208 let flash_end = flash.as_ptr() as usize + flash.len() - 1;
1209
1210 (flash_end - offset) >= length
1211 }
1212
1213 /// Function to compute an available address for the new application binary.
1214 pub fn check_flash_for_new_address(
1215 &self,
1216 new_app_size: usize,
1217 ) -> Result<(usize, PaddingRequirement, usize, usize), ProcessBinaryError> {
1218 const MAX_PROCS: usize = 10;
1219 let mut pb_start_address: [usize; MAX_PROCS] = [0; MAX_PROCS];
1220 let mut pb_end_address: [usize; MAX_PROCS] = [0; MAX_PROCS];
1221 match self.check_flash_for_valid_address(
1222 new_app_size,
1223 &mut pb_start_address,
1224 &mut pb_end_address,
1225 ) {
1226 Ok(app_address) => {
1227 let (pr, prev_app_addr, next_app_addr) = self
1228 .compute_padding_requirement_and_neighbors(
1229 app_address,
1230 new_app_size,
1231 &pb_start_address,
1232 &pb_end_address,
1233 );
1234 let (padding_requirement, previous_app_end_addr, next_app_start_addr) =
1235 (pr, prev_app_addr, next_app_addr);
1236 Ok((
1237 app_address,
1238 padding_requirement,
1239 previous_app_end_addr,
1240 next_app_start_addr,
1241 ))
1242 }
1243 Err(e) => Err(e),
1244 }
1245 }
1246
1247 /// Function to check if the app binary at address `app_address` is valid.
1248 fn check_new_binary_validity(&self, app_address: usize) -> bool {
1249 let flash = self.flash_bank.get();
1250 // Pass the first eight bytes of the tbfheader to parse out the
1251 // length of the tbf header and app. We then use those values to see
1252 // if we have enough flash remaining to parse the remainder of the
1253 // header.
1254 let binary_header = match flash.get(app_address..app_address + 8) {
1255 Some(slice) if slice.len() == 8 => slice,
1256 _ => return false, // Ensure exactly 8 bytes are available
1257 };
1258
1259 let binary_header_array: &[u8; 8] = match binary_header.try_into() {
1260 Ok(arr) => arr,
1261 Err(_) => return false,
1262 };
1263
1264 match tock_tbf::parse::parse_tbf_header_lengths(binary_header_array) {
1265 Ok((_version, _header_length, _entry_length)) => true,
1266 Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(_entry_length)) => false,
1267 Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => false,
1268 }
1269 }
1270
1271 /// Function to start loading the new application at address `app_address` with size
1272 /// `app_size`.
1273 pub fn load_new_process_binary(
1274 &self,
1275 app_address: usize,
1276 app_size: usize,
1277 ) -> Result<(), ProcessLoadError> {
1278 let flash = self.flash_bank.get();
1279 let process_address = app_address - flash.as_ptr() as usize;
1280 let process_flash = flash.get(process_address..process_address + app_size);
1281 let result = self.check_new_binary_validity(process_address);
1282 match result {
1283 true => {
1284 if let Some(flash) = process_flash {
1285 self.flash.set(flash);
1286 } else {
1287 return Err(ProcessLoadError::BinaryError(
1288 ProcessBinaryError::TbfHeaderNotFound,
1289 ));
1290 }
1291
1292 self.state
1293 .set(SequentialProcessLoaderMachineState::DiscoverProcessBinaries);
1294
1295 self.run_mode
1296 .set(SequentialProcessLoaderMachineRunMode::RuntimeMode);
1297 // Start an asynchronous flow so we can issue a callback on error.
1298 self.deferred_call.set();
1299
1300 Ok(())
1301 }
1302 false => Err(ProcessLoadError::BinaryError(
1303 ProcessBinaryError::TbfHeaderNotFound,
1304 )),
1305 }
1306 }
1307}
1308
1309impl<'a, C: Chip, D: ProcessStandardDebug> ProcessLoadingAsync<'a>
1310 for SequentialProcessLoaderMachine<'a, C, D>
1311{
1312 fn set_client(&self, client: &'a dyn ProcessLoadingAsyncClient) {
1313 self.boot_client.set(client);
1314 }
1315
1316 fn set_policy(&self, policy: &'a dyn AppIdPolicy) {
1317 self.policy.replace(policy);
1318 }
1319
1320 fn start(&self) {
1321 self.state
1322 .set(SequentialProcessLoaderMachineState::DiscoverProcessBinaries);
1323 self.run_mode
1324 .set(SequentialProcessLoaderMachineRunMode::BootMode);
1325 // Start an asynchronous flow so we can issue a callback on error.
1326 self.deferred_call.set();
1327 }
1328}
1329
1330impl<C: Chip, D: ProcessStandardDebug> DeferredCallClient
1331 for SequentialProcessLoaderMachine<'_, C, D>
1332{
1333 fn handle_deferred_call(&self) {
1334 // We use deferred calls to start the operation in the async loop.
1335 match self.state.get() {
1336 Some(SequentialProcessLoaderMachineState::DiscoverProcessBinaries) => {
1337 self.load_and_check();
1338 }
1339 Some(SequentialProcessLoaderMachineState::LoadProcesses) => {
1340 let ret = self.load_process_objects();
1341 match ret {
1342 Ok(()) => {}
1343 Err(()) => {
1344 // If this failed for some reason, we still need to
1345 // signal that process loading has finished.
1346 self.get_current_client().map(|client| {
1347 client.process_loading_finished();
1348 });
1349 }
1350 }
1351 }
1352 None => {}
1353 }
1354 }
1355
1356 fn register(&'static self) {
1357 self.deferred_call.register(self);
1358 }
1359}
1360
1361impl<C: Chip, D: ProcessStandardDebug> crate::process_checker::ProcessCheckerMachineClient
1362 for SequentialProcessLoaderMachine<'_, C, D>
1363{
1364 fn done(
1365 &self,
1366 process_binary: ProcessBinary,
1367 result: Result<Option<AcceptedCredential>, crate::process_checker::ProcessCheckError>,
1368 ) {
1369 // Check if this process was approved by the checker.
1370 match result {
1371 Ok(optional_credential) => {
1372 if config::CONFIG.debug_load_processes {
1373 debug!(
1374 "Loading: Check succeeded for process {}",
1375 process_binary.header.get_package_name().unwrap_or("")
1376 );
1377 }
1378 // Save the checked process binary now that we know it is valid.
1379 match self.find_open_process_binary_slot() {
1380 Some(index) => {
1381 self.proc_binaries.map(|proc_binaries| {
1382 process_binary.credential.insert(optional_credential);
1383 proc_binaries[index] = Some(process_binary);
1384 });
1385 }
1386 None => {
1387 self.get_current_client().map(|client| {
1388 client.process_loaded(Err(ProcessLoadError::NoProcessSlot));
1389 });
1390 }
1391 }
1392 }
1393 Err(e) => {
1394 if config::CONFIG.debug_load_processes {
1395 debug!(
1396 "Loading: Process {} check failed {:?}",
1397 process_binary.header.get_package_name().unwrap_or(""),
1398 e
1399 );
1400 }
1401 // Signal error and call try next
1402 self.get_current_client().map(|client| {
1403 client.process_loaded(Err(ProcessLoadError::CheckError(e)));
1404 });
1405 }
1406 }
1407
1408 // Try to load the next process in flash.
1409 self.deferred_call.set();
1410 }
1411}