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