Skip to main content

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