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}