kernel/process_loading.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Helper functions and machines for loading process binaries into in-memory
6//! Tock processes.
7//!
8//! Process loaders are responsible for parsing the binary formats of Tock
9//! processes, checking whether they are allowed to be loaded, and if so
10//! initializing a process structure to run it.
11//!
12//! This module provides multiple process loader options depending on which
13//! features a particular board requires.
14
15use core::cell::Cell;
16use core::fmt;
17
18use crate::capabilities::ProcessManagementCapability;
19use crate::config;
20use crate::debug;
21use crate::deferred_call::{DeferredCall, DeferredCallClient};
22use crate::kernel::Kernel;
23use crate::platform::chip::Chip;
24use crate::process::{Process, ShortId};
25use crate::process_binary::{ProcessBinary, ProcessBinaryError};
26use crate::process_checker::AcceptedCredential;
27use crate::process_checker::{AppIdPolicy, ProcessCheckError, ProcessCheckerMachine};
28use crate::process_policies::ProcessFaultPolicy;
29use crate::process_policies::ProcessStandardStoragePermissionsPolicy;
30use crate::process_standard::ProcessStandard;
31use crate::process_standard::{ProcessStandardDebug, ProcessStandardDebugFull};
32use crate::utilities::cells::{MapCell, OptionalCell};
33
34/// Errors that can occur when trying to load and create processes.
35pub enum ProcessLoadError {
36 /// Not enough memory to meet the amount requested by a process. Modify the
37 /// process to request less memory, flash fewer processes, or increase the
38 /// size of the region your board reserves for process memory.
39 NotEnoughMemory,
40
41 /// A process was loaded with a length in flash that the MPU does not
42 /// support. The fix is probably to correct the process size, but this could
43 /// also be caused by a bad MPU implementation.
44 MpuInvalidFlashLength,
45
46 /// The MPU configuration failed for some other, unspecified reason. This
47 /// could be of an internal resource exhaustion, or a mismatch between the
48 /// (current) MPU constraints and process requirements.
49 MpuConfigurationError,
50
51 /// A process specified a fixed memory address that it needs its memory
52 /// range to start at, and the kernel did not or could not give the process
53 /// a memory region starting at that address.
54 MemoryAddressMismatch {
55 actual_address: u32,
56 expected_address: u32,
57 },
58
59 /// There is nowhere in the `PROCESSES` array to store this process.
60 NoProcessSlot,
61
62 /// Process loading failed because parsing the binary failed.
63 BinaryError(ProcessBinaryError),
64
65 /// Process loading failed because checking the process failed.
66 CheckError(ProcessCheckError),
67
68 /// Process loading error due (likely) to a bug in the kernel. If you get
69 /// this error please open a bug report.
70 InternalError,
71}
72
73impl fmt::Debug for ProcessLoadError {
74 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 match self {
76 ProcessLoadError::NotEnoughMemory => {
77 write!(f, "Not able to provide RAM requested by app")
78 }
79
80 ProcessLoadError::MpuInvalidFlashLength => {
81 write!(f, "App flash length not supported by MPU")
82 }
83
84 ProcessLoadError::MpuConfigurationError => {
85 write!(f, "Configuring the MPU failed")
86 }
87
88 ProcessLoadError::MemoryAddressMismatch {
89 actual_address,
90 expected_address,
91 } => write!(
92 f,
93 "App memory does not match requested address Actual:{:#x}, Expected:{:#x}",
94 actual_address, expected_address
95 ),
96
97 ProcessLoadError::NoProcessSlot => {
98 write!(f, "Nowhere to store the loaded process")
99 }
100
101 ProcessLoadError::BinaryError(binary_error) => {
102 writeln!(f, "Error parsing process binary")?;
103 write!(f, "{:?}", binary_error)
104 }
105
106 ProcessLoadError::CheckError(check_error) => {
107 writeln!(f, "Error checking process")?;
108 write!(f, "{:?}", check_error)
109 }
110
111 ProcessLoadError::InternalError => write!(f, "Error in kernel. Likely a bug."),
112 }
113 }
114}
115
116////////////////////////////////////////////////////////////////////////////////
117// SYNCHRONOUS PROCESS LOADING
118////////////////////////////////////////////////////////////////////////////////
119
120/// Load processes into runnable process structures.
121///
122/// Load processes (stored as TBF objects in flash) into runnable process
123/// structures stored in the `procs` array and mark all successfully loaded
124/// processes as runnable. This method does not check the cryptographic
125/// credentials of TBF objects. Platforms for which code size is tight and do
126/// not need to check TBF credentials can call this method because it results in
127/// a smaller kernel, as it does not invoke the credential checking state
128/// machine.
129///
130/// This function is made `pub` so that board files can use it, but loading
131/// processes from slices of flash an memory is fundamentally unsafe. Therefore,
132/// we require the `ProcessManagementCapability` to call this function.
133// Mark inline always to reduce code size. Since this is only called in one
134// place (a board's main.rs), by inlining the load_*processes() functions, the
135// compiler can elide many checks which reduces code size appreciably. Note,
136// however, these functions require a rather large stack frame, which may be an
137// issue for boards small kernel stacks.
138#[inline(always)]
139pub fn load_processes<C: Chip>(
140 kernel: &'static Kernel,
141 chip: &'static C,
142 app_flash: &'static [u8],
143 app_memory: &'static mut [u8],
144 mut procs: &'static mut [Option<&'static dyn Process>],
145 fault_policy: &'static dyn ProcessFaultPolicy,
146 _capability_management: &dyn ProcessManagementCapability,
147) -> Result<(), ProcessLoadError> {
148 load_processes_from_flash::<C, ProcessStandardDebugFull>(
149 kernel,
150 chip,
151 app_flash,
152 app_memory,
153 &mut procs,
154 fault_policy,
155 )?;
156
157 if config::CONFIG.debug_process_credentials {
158 debug!("Checking: no checking, load and run all processes");
159 }
160 for proc in procs.iter() {
161 proc.map(|p| {
162 if config::CONFIG.debug_process_credentials {
163 debug!("Running {}", p.get_process_name());
164 }
165 });
166 }
167 Ok(())
168}
169
170/// Helper function to load processes from flash into an array of active
171/// processes. This is the default template for loading processes, but a board
172/// is able to create its own `load_processes()` function and use that instead.
173///
174/// Processes are found in flash starting from the given address and iterating
175/// through Tock Binary Format (TBF) headers. Processes are given memory out of
176/// the `app_memory` buffer until either the memory is exhausted or the
177/// allocated number of processes are created. This buffer is a non-static slice,
178/// ensuring that this code cannot hold onto the slice past the end of this function
179/// (instead, processes store a pointer and length), which necessary for later
180/// creation of `ProcessBuffer`s in this memory region to be sound.
181/// A reference to each process is stored in the provided `procs` array.
182/// How process faults are handled by the
183/// kernel must be provided and is assigned to every created process.
184///
185/// Returns `Ok(())` if process discovery went as expected. Returns a
186/// `ProcessLoadError` if something goes wrong during TBF parsing or process
187/// creation.
188#[inline(always)]
189fn load_processes_from_flash<C: Chip, D: ProcessStandardDebug + 'static>(
190 kernel: &'static Kernel,
191 chip: &'static C,
192 app_flash: &'static [u8],
193 app_memory: &'static mut [u8],
194 procs: &mut &'static mut [Option<&'static dyn Process>],
195 fault_policy: &'static dyn ProcessFaultPolicy,
196) -> Result<(), ProcessLoadError> {
197 if config::CONFIG.debug_load_processes {
198 debug!(
199 "Loading processes from flash={:#010X}-{:#010X} into sram={:#010X}-{:#010X}",
200 app_flash.as_ptr() as usize,
201 app_flash.as_ptr() as usize + app_flash.len() - 1,
202 app_memory.as_ptr() as usize,
203 app_memory.as_ptr() as usize + app_memory.len() - 1
204 );
205 }
206
207 let mut remaining_flash = app_flash;
208 let mut remaining_memory = app_memory;
209 // Try to discover up to `procs.len()` processes in flash.
210 let mut index = 0;
211 let num_procs = procs.len();
212 while index < num_procs {
213 let load_binary_result = discover_process_binary(remaining_flash);
214
215 match load_binary_result {
216 Ok((new_flash, process_binary)) => {
217 remaining_flash = new_flash;
218
219 let load_result = load_process::<C, D>(
220 kernel,
221 chip,
222 process_binary,
223 remaining_memory,
224 ShortId::LocallyUnique,
225 index,
226 fault_policy,
227 &(),
228 );
229 match load_result {
230 Ok((new_mem, proc)) => {
231 remaining_memory = new_mem;
232 match proc {
233 Some(p) => {
234 if config::CONFIG.debug_load_processes {
235 debug!("Loaded process {}", p.get_process_name())
236 }
237 procs[index] = proc;
238 index += 1;
239 }
240 None => {
241 if config::CONFIG.debug_load_processes {
242 debug!("No process loaded.");
243 }
244 }
245 }
246 }
247 Err((new_mem, err)) => {
248 remaining_memory = new_mem;
249 if config::CONFIG.debug_load_processes {
250 debug!("Processes load error: {:?}.", err);
251 }
252 }
253 }
254 }
255 Err((new_flash, err)) => {
256 remaining_flash = new_flash;
257 match err {
258 ProcessBinaryError::NotEnoughFlash | ProcessBinaryError::TbfHeaderNotFound => {
259 if config::CONFIG.debug_load_processes {
260 debug!("No more processes to load: {:?}.", err);
261 }
262 // No more processes to load.
263 break;
264 }
265
266 ProcessBinaryError::TbfHeaderParseFailure(_)
267 | ProcessBinaryError::IncompatibleKernelVersion { .. }
268 | ProcessBinaryError::IncorrectFlashAddress { .. }
269 | ProcessBinaryError::NotEnabledProcess
270 | ProcessBinaryError::Padding => {
271 if config::CONFIG.debug_load_processes {
272 debug!("Unable to use process binary: {:?}.", err);
273 }
274
275 // Skip this binary and move to the next one.
276 continue;
277 }
278 }
279 }
280 }
281 }
282 Ok(())
283}
284
285////////////////////////////////////////////////////////////////////////////////
286// HELPER FUNCTIONS
287////////////////////////////////////////////////////////////////////////////////
288
289/// Find a process binary stored at the beginning of `flash` and create a
290/// `ProcessBinary` object if the process is viable to run on this kernel.
291fn discover_process_binary(
292 flash: &'static [u8],
293) -> Result<(&'static [u8], ProcessBinary), (&'static [u8], ProcessBinaryError)> {
294 if config::CONFIG.debug_load_processes {
295 debug!(
296 "Looking for process binary in flash={:#010X}-{:#010X}",
297 flash.as_ptr() as usize,
298 flash.as_ptr() as usize + flash.len() - 1
299 );
300 }
301
302 // If this fails, not enough remaining flash to check for an app.
303 let test_header_slice = flash
304 .get(0..8)
305 .ok_or((flash, ProcessBinaryError::NotEnoughFlash))?;
306
307 // Pass the first eight bytes to tbfheader to parse out the length of
308 // the tbf header and app. We then use those values to see if we have
309 // enough flash remaining to parse the remainder of the header.
310 //
311 // Start by converting [u8] to [u8; 8].
312 let header = test_header_slice
313 .try_into()
314 .or(Err((flash, ProcessBinaryError::NotEnoughFlash)))?;
315
316 let (version, header_length, app_length) =
317 match tock_tbf::parse::parse_tbf_header_lengths(header) {
318 Ok((v, hl, el)) => (v, hl, el),
319 Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(app_length)) => {
320 // If we could not parse the header, then we want to skip over
321 // this app and look for the next one.
322 (0, 0, app_length)
323 }
324 Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => {
325 // Since Tock apps use a linked list, it is very possible the
326 // header we started to parse is intentionally invalid to signal
327 // the end of apps. This is ok and just means we have finished
328 // loading apps.
329 return Err((flash, ProcessBinaryError::TbfHeaderNotFound));
330 }
331 };
332
333 // Now we can get a slice which only encompasses the length of flash
334 // described by this tbf header. We will either parse this as an actual
335 // app, or skip over this region.
336 let app_flash = flash
337 .get(0..app_length as usize)
338 .ok_or((flash, ProcessBinaryError::NotEnoughFlash))?;
339
340 // Advance the flash slice for process discovery beyond this last entry.
341 // This will be the start of where we look for a new process since Tock
342 // processes are allocated back-to-back in flash.
343 let remaining_flash = flash
344 .get(app_flash.len()..)
345 .ok_or((flash, ProcessBinaryError::NotEnoughFlash))?;
346
347 let pb = ProcessBinary::create(app_flash, header_length as usize, version, true)
348 .map_err(|e| (remaining_flash, e))?;
349
350 Ok((remaining_flash, pb))
351}
352
353/// Load a process stored as a TBF process binary with `app_memory` as the RAM
354/// pool that its RAM should be allocated from. Returns `Ok` if the process
355/// object was created, `Err` with a relevant error if the process object could
356/// not be created.
357fn load_process<C: Chip, D: ProcessStandardDebug>(
358 kernel: &'static Kernel,
359 chip: &'static C,
360 process_binary: ProcessBinary,
361 app_memory: &'static mut [u8],
362 app_id: ShortId,
363 index: usize,
364 fault_policy: &'static dyn ProcessFaultPolicy,
365 storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy<C, D>,
366) -> Result<(&'static mut [u8], Option<&'static dyn Process>), (&'static mut [u8], ProcessLoadError)>
367{
368 if config::CONFIG.debug_load_processes {
369 debug!(
370 "Loading: process flash={:#010X}-{:#010X} ram={:#010X}-{:#010X}",
371 process_binary.flash.as_ptr() as usize,
372 process_binary.flash.as_ptr() as usize + process_binary.flash.len() - 1,
373 app_memory.as_ptr() as usize,
374 app_memory.as_ptr() as usize + app_memory.len() - 1
375 );
376 }
377
378 // Need to reassign remaining_memory in every iteration so the compiler
379 // knows it will not be re-borrowed.
380 // If we found an actual app header, try to create a `Process`
381 // object. We also need to shrink the amount of remaining memory
382 // based on whatever is assigned to the new process if one is
383 // created.
384
385 // Try to create a process object from that app slice. If we don't
386 // get a process and we didn't get a loading error (aka we got to
387 // this point), then the app is a disabled process or just padding.
388 let (process_option, unused_memory) = unsafe {
389 ProcessStandard::<C, D>::create(
390 kernel,
391 chip,
392 process_binary,
393 app_memory,
394 fault_policy,
395 storage_policy,
396 app_id,
397 index,
398 )
399 .map_err(|(e, memory)| (memory, e))?
400 };
401
402 process_option.map(|process| {
403 if config::CONFIG.debug_load_processes {
404 debug!(
405 "Loading: {} [{}] flash={:#010X}-{:#010X} ram={:#010X}-{:#010X}",
406 process.get_process_name(),
407 index,
408 process.get_addresses().flash_start,
409 process.get_addresses().flash_end,
410 process.get_addresses().sram_start,
411 process.get_addresses().sram_end - 1,
412 );
413 }
414 });
415
416 Ok((unused_memory, process_option))
417}
418
419////////////////////////////////////////////////////////////////////////////////
420// ASYNCHRONOUS PROCESS LOADING
421////////////////////////////////////////////////////////////////////////////////
422
423/// Client for asynchronous process loading.
424///
425/// This supports a client that is notified after trying to load each process in
426/// flash. Also there is a callback for after all processes have been
427/// discovered.
428pub trait ProcessLoadingAsyncClient {
429 /// A process was successfully found in flash, checked, and loaded into a
430 /// `ProcessStandard` object.
431 fn process_loaded(&self, result: Result<(), ProcessLoadError>);
432
433 /// There are no more processes in flash to be loaded.
434 fn process_loading_finished(&self);
435}
436
437/// Asynchronous process loading.
438///
439/// Machines which implement this trait perform asynchronous process loading and
440/// signal completion through `ProcessLoadingAsyncClient`.
441///
442/// Various process loaders may exist. This includes a loader from a MCU's
443/// integrated flash, or a loader from an external flash chip.
444pub trait ProcessLoadingAsync<'a> {
445 /// Set the client to receive callbacks about process loading and when
446 /// process loading has finished.
447 fn set_client(&self, client: &'a dyn ProcessLoadingAsyncClient);
448
449 /// Set the credential checking policy for the loader.
450 fn set_policy(&self, policy: &'a dyn AppIdPolicy);
451
452 /// Start the process loading operation.
453 fn start(&self);
454}
455
456/// Operating mode of the loader.
457#[derive(Clone, Copy)]
458enum SequentialProcessLoaderMachineState {
459 /// Phase of discovering `ProcessBinary` objects in flash.
460 DiscoverProcessBinaries,
461 /// Phase of loading `ProcessBinary`s into `Process`s.
462 LoadProcesses,
463}
464
465/// A machine for loading processes stored sequentially in a region of flash.
466///
467/// Load processes (stored as TBF objects in flash) into runnable process
468/// structures stored in the `procs` array. This machine scans the footers in
469/// the TBF for cryptographic credentials for binary integrity, passing them to
470/// the checker to decide whether the process has sufficient credentials to run.
471pub struct SequentialProcessLoaderMachine<'a, C: Chip + 'static, D: ProcessStandardDebug + 'static>
472{
473 /// Client to notify as processes are loaded and process loading finishes.
474 client: OptionalCell<&'a dyn ProcessLoadingAsyncClient>,
475 /// Machine to use to check process credentials.
476 checker: &'static ProcessCheckerMachine,
477 /// Array of stored process references for loaded processes.
478 procs: MapCell<&'static mut [Option<&'static dyn Process>]>,
479 /// Array to store `ProcessBinary`s after checking credentials.
480 proc_binaries: MapCell<&'static mut [Option<ProcessBinary>]>,
481 /// Flash memory region to load processes from.
482 flash: Cell<&'static [u8]>,
483 /// Memory available to assign to applications.
484 app_memory: Cell<&'static mut [u8]>,
485 /// Mechanism for generating async callbacks.
486 deferred_call: DeferredCall,
487 /// Reference to the kernel object for creating Processes.
488 kernel: &'static Kernel,
489 /// Reference to the Chip object for creating Processes.
490 chip: &'static C,
491 /// The policy to use when determining ShortIds and process uniqueness.
492 policy: OptionalCell<&'a dyn AppIdPolicy>,
493 /// The fault policy to assign to each created Process.
494 fault_policy: &'static dyn ProcessFaultPolicy,
495 /// The storage permissions policy to assign to each created Process.
496 storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy<C, D>,
497 /// Current mode of the loading machine.
498 state: OptionalCell<SequentialProcessLoaderMachineState>,
499}
500
501impl<C: Chip, D: ProcessStandardDebug> SequentialProcessLoaderMachine<'_, C, D> {
502 /// This function is made `pub` so that board files can use it, but loading
503 /// processes from slices of flash an memory is fundamentally unsafe.
504 /// Therefore, we require the `ProcessManagementCapability` to call this
505 /// function.
506 pub fn new(
507 checker: &'static ProcessCheckerMachine,
508 procs: &'static mut [Option<&'static dyn Process>],
509 proc_binaries: &'static mut [Option<ProcessBinary>],
510 kernel: &'static Kernel,
511 chip: &'static C,
512 flash: &'static [u8],
513 app_memory: &'static mut [u8],
514 fault_policy: &'static dyn ProcessFaultPolicy,
515 storage_policy: &'static dyn ProcessStandardStoragePermissionsPolicy<C, D>,
516 policy: &'static dyn AppIdPolicy,
517 _capability_management: &dyn ProcessManagementCapability,
518 ) -> Self {
519 Self {
520 deferred_call: DeferredCall::new(),
521 checker,
522 client: OptionalCell::empty(),
523 procs: MapCell::new(procs),
524 proc_binaries: MapCell::new(proc_binaries),
525 kernel,
526 chip,
527 flash: Cell::new(flash),
528 app_memory: Cell::new(app_memory),
529 policy: OptionalCell::new(policy),
530 fault_policy,
531 storage_policy,
532 state: OptionalCell::empty(),
533 }
534 }
535
536 /// Find a slot in the `PROCESSES` array to store this process.
537 fn find_open_process_slot(&self) -> Option<usize> {
538 self.procs.map_or(None, |procs| {
539 for (i, p) in procs.iter().enumerate() {
540 if p.is_none() {
541 return Some(i);
542 }
543 }
544 None
545 })
546 }
547
548 /// Find a slot in the `PROCESS_BINARIES` array to store this process.
549 fn find_open_process_binary_slot(&self) -> Option<usize> {
550 self.proc_binaries.map_or(None, |proc_bins| {
551 for (i, p) in proc_bins.iter().enumerate() {
552 if p.is_none() {
553 return Some(i);
554 }
555 }
556 None
557 })
558 }
559
560 fn load_and_check(&self) {
561 let ret = self.discover_process_binary();
562 match ret {
563 Ok(pb) => match self.checker.check(pb) {
564 Ok(()) => {}
565 Err(e) => {
566 self.client.map(|client| {
567 client.process_loaded(Err(ProcessLoadError::CheckError(e)));
568 });
569 }
570 },
571 Err(ProcessBinaryError::NotEnoughFlash)
572 | Err(ProcessBinaryError::TbfHeaderNotFound) => {
573 // These two errors occur when there are no more app binaries in
574 // flash. Now we can move to actually loading process binaries
575 // into full processes.
576
577 self.state
578 .set(SequentialProcessLoaderMachineState::LoadProcesses);
579 self.deferred_call.set();
580 }
581 Err(e) => {
582 if config::CONFIG.debug_load_processes {
583 debug!("Loading: unable to create ProcessBinary: {:?}", e);
584 }
585
586 // Other process binary errors indicate the process is not
587 // compatible. Signal error and try the next item in flash.
588 self.client.map(|client| {
589 client.process_loaded(Err(ProcessLoadError::BinaryError(e)));
590 });
591 self.deferred_call.set();
592 }
593 }
594 }
595
596 /// Try to parse a process binary from flash.
597 ///
598 /// Returns the process binary object or an error if a valid process
599 /// binary could not be extracted.
600 fn discover_process_binary(&self) -> Result<ProcessBinary, ProcessBinaryError> {
601 let flash = self.flash.get();
602
603 if config::CONFIG.debug_load_processes {
604 debug!(
605 "Looking for process binary in flash={:#010X}-{:#010X}",
606 flash.as_ptr() as usize,
607 flash.as_ptr() as usize + flash.len() - 1
608 );
609 }
610
611 // If this fails, not enough remaining flash to check for an app.
612 let test_header_slice = flash.get(0..8).ok_or(ProcessBinaryError::NotEnoughFlash)?;
613
614 // Pass the first eight bytes to tbfheader to parse out the length of
615 // the tbf header and app. We then use those values to see if we have
616 // enough flash remaining to parse the remainder of the header.
617 //
618 // Start by converting [u8] to [u8; 8].
619 let header = test_header_slice
620 .try_into()
621 .or(Err(ProcessBinaryError::NotEnoughFlash))?;
622
623 let (version, header_length, app_length) =
624 match tock_tbf::parse::parse_tbf_header_lengths(header) {
625 Ok((v, hl, el)) => (v, hl, el),
626 Err(tock_tbf::types::InitialTbfParseError::InvalidHeader(app_length)) => {
627 // If we could not parse the header, then we want to skip over
628 // this app and look for the next one.
629 (0, 0, app_length)
630 }
631 Err(tock_tbf::types::InitialTbfParseError::UnableToParse) => {
632 // Since Tock apps use a linked list, it is very possible the
633 // header we started to parse is intentionally invalid to signal
634 // the end of apps. This is ok and just means we have finished
635 // loading apps.
636 return Err(ProcessBinaryError::TbfHeaderNotFound);
637 }
638 };
639
640 // Now we can get a slice which only encompasses the length of flash
641 // described by this tbf header. We will either parse this as an actual
642 // app, or skip over this region.
643 let app_flash = flash
644 .get(0..app_length as usize)
645 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
646
647 // Advance the flash slice for process discovery beyond this last entry.
648 // This will be the start of where we look for a new process since Tock
649 // processes are allocated back-to-back in flash.
650 let remaining_flash = flash
651 .get(app_flash.len()..)
652 .ok_or(ProcessBinaryError::NotEnoughFlash)?;
653 self.flash.set(remaining_flash);
654
655 let pb = ProcessBinary::create(app_flash, header_length as usize, version, true)?;
656
657 Ok(pb)
658 }
659
660 /// Create process objects from the discovered process binaries.
661 ///
662 /// This verifies that the discovered processes are valid to run.
663 fn load_process_objects(&self) -> Result<(), ()> {
664 let proc_binaries = self.proc_binaries.take().ok_or(())?;
665 let proc_binaries_len = proc_binaries.len();
666
667 // Iterate all process binary entries.
668 for i in 0..proc_binaries_len {
669 // We are either going to load this process binary or discard it, so
670 // we can use `take()` here.
671 if let Some(process_binary) = proc_binaries[i].take() {
672 // We assume the process can be loaded. This is not the case
673 // if there is a conflicting process.
674 let mut ok_to_load = true;
675
676 // Start by iterating all other process binaries and seeing
677 // if any are in conflict (same AppID with newer version).
678 for proc_bin in proc_binaries.iter() {
679 match proc_bin {
680 Some(other_process_binary) => {
681 let blocked = self
682 .is_blocked_from_loading_by(&process_binary, other_process_binary);
683
684 if blocked {
685 ok_to_load = false;
686 break;
687 }
688 }
689 None => {}
690 }
691 }
692
693 // Go to next ProcessBinary if we cannot load this process.
694 if !ok_to_load {
695 continue;
696 }
697
698 // Now scan the already loaded processes and make sure this
699 // doesn't conflict with any of those. Since those processes
700 // are already loaded, we just need to check if this process
701 // binary has the same AppID as an already loaded process.
702 self.procs.map(|procs| {
703 for proc in procs.iter() {
704 match proc {
705 Some(p) => {
706 let blocked =
707 self.is_blocked_from_loading_by_process(&process_binary, *p);
708
709 if blocked {
710 ok_to_load = false;
711 break;
712 }
713 }
714 None => {}
715 }
716 }
717 });
718
719 if !ok_to_load {
720 continue;
721 }
722
723 // If we get here it is ok to load the process.
724 match self.find_open_process_slot() {
725 Some(index) => {
726 // Calculate the ShortId for this new process.
727 let short_app_id = self.policy.map_or(ShortId::LocallyUnique, |policy| {
728 policy.to_short_id(&process_binary)
729 });
730
731 // Try to create a `Process` object.
732 let load_result = load_process(
733 self.kernel,
734 self.chip,
735 process_binary,
736 self.app_memory.take(),
737 short_app_id,
738 index,
739 self.fault_policy,
740 self.storage_policy,
741 );
742 match load_result {
743 Ok((new_mem, proc)) => {
744 self.app_memory.set(new_mem);
745 match proc {
746 Some(p) => {
747 if config::CONFIG.debug_load_processes {
748 debug!(
749 "Loading: Loaded process {}",
750 p.get_process_name()
751 )
752 }
753
754 // Store the `ProcessStandard` object in the `PROCESSES`
755 // array.
756 self.procs.map(|procs| {
757 procs[index] = proc;
758 });
759 // Notify the client the process was loaded
760 // successfully.
761 self.client.map(|client| {
762 client.process_loaded(Ok(()));
763 });
764 }
765 None => {
766 if config::CONFIG.debug_load_processes {
767 debug!("No process loaded.");
768 }
769 }
770 }
771 }
772 Err((new_mem, err)) => {
773 self.app_memory.set(new_mem);
774 if config::CONFIG.debug_load_processes {
775 debug!("Could not load process: {:?}.", err);
776 }
777
778 self.client.map(|client| {
779 client.process_loaded(Err(err));
780 });
781 }
782 }
783 }
784 None => {
785 // Nowhere to store the process.
786 self.client.map(|client| {
787 client.process_loaded(Err(ProcessLoadError::NoProcessSlot));
788 });
789 }
790 }
791 }
792 }
793 self.proc_binaries.put(proc_binaries);
794
795 // We have iterated all discovered `ProcessBinary`s and loaded what we
796 // could so now we can signal that process loading is finished.
797 self.client.map(|client| {
798 client.process_loading_finished();
799 });
800
801 self.state.clear();
802 Ok(())
803 }
804
805 /// Check if `pb1` is blocked from running by `pb2`.
806 ///
807 /// `pb2` blocks `pb1` if:
808 ///
809 /// - They both have the same AppID or they both have the same ShortId, and
810 /// - `pb2` has a higher version number.
811 fn is_blocked_from_loading_by(&self, pb1: &ProcessBinary, pb2: &ProcessBinary) -> bool {
812 let same_app_id = self
813 .policy
814 .map_or(false, |policy| !policy.different_identifier(pb1, pb2));
815 let same_short_app_id = self.policy.map_or(false, |policy| {
816 policy.to_short_id(pb1) == policy.to_short_id(pb2)
817 });
818 let other_newer = pb2.header.get_binary_version() > pb1.header.get_binary_version();
819
820 let blocks = (same_app_id || same_short_app_id) && other_newer;
821
822 if config::CONFIG.debug_process_credentials {
823 debug!(
824 "Loading: ProcessBinary {}({:#02x}) does{} block {}({:#02x})",
825 pb2.header.get_package_name().unwrap_or(""),
826 pb2.flash.as_ptr() as usize,
827 if blocks { " not" } else { "" },
828 pb1.header.get_package_name().unwrap_or(""),
829 pb1.flash.as_ptr() as usize,
830 );
831 }
832
833 blocks
834 }
835
836 /// Check if `pb` is blocked from running by `process`.
837 ///
838 /// `process` blocks `pb` if:
839 ///
840 /// - They both have the same AppID, or
841 /// - They both have the same ShortId
842 ///
843 /// Since `process` is already loaded, we only have to enforce the AppID and
844 /// ShortId uniqueness guarantees.
845 fn is_blocked_from_loading_by_process(
846 &self,
847 pb: &ProcessBinary,
848 process: &dyn Process,
849 ) -> bool {
850 let same_app_id = self.policy.map_or(false, |policy| {
851 !policy.different_identifier_process(pb, process)
852 });
853 let same_short_app_id = self.policy.map_or(false, |policy| {
854 policy.to_short_id(pb) == process.short_app_id()
855 });
856
857 let blocks = same_app_id || same_short_app_id;
858
859 if config::CONFIG.debug_process_credentials {
860 debug!(
861 "Loading: Process {}({:#02x}) does{} block {}({:#02x})",
862 process.get_process_name(),
863 process.get_addresses().flash_start,
864 if blocks { " not" } else { "" },
865 pb.header.get_package_name().unwrap_or(""),
866 pb.flash.as_ptr() as usize,
867 );
868 }
869
870 blocks
871 }
872}
873
874impl<'a, C: Chip, D: ProcessStandardDebug> ProcessLoadingAsync<'a>
875 for SequentialProcessLoaderMachine<'a, C, D>
876{
877 fn set_client(&self, client: &'a dyn ProcessLoadingAsyncClient) {
878 self.client.set(client);
879 }
880
881 fn set_policy(&self, policy: &'a dyn AppIdPolicy) {
882 self.policy.replace(policy);
883 }
884
885 fn start(&self) {
886 self.state
887 .set(SequentialProcessLoaderMachineState::DiscoverProcessBinaries);
888 // Start an asynchronous flow so we can issue a callback on error.
889 self.deferred_call.set();
890 }
891}
892
893impl<C: Chip, D: ProcessStandardDebug> DeferredCallClient
894 for SequentialProcessLoaderMachine<'_, C, D>
895{
896 fn handle_deferred_call(&self) {
897 // We use deferred calls to start the operation in the async loop.
898 match self.state.get() {
899 Some(SequentialProcessLoaderMachineState::DiscoverProcessBinaries) => {
900 self.load_and_check();
901 }
902 Some(SequentialProcessLoaderMachineState::LoadProcesses) => {
903 let ret = self.load_process_objects();
904 match ret {
905 Ok(()) => {}
906 Err(()) => {
907 // If this failed for some reason, we still need to
908 // signal that process loading has finished.
909 self.client.map(|client| {
910 client.process_loading_finished();
911 });
912 }
913 }
914 }
915 None => {}
916 }
917 }
918
919 fn register(&'static self) {
920 self.deferred_call.register(self);
921 }
922}
923
924impl<C: Chip, D: ProcessStandardDebug> crate::process_checker::ProcessCheckerMachineClient
925 for SequentialProcessLoaderMachine<'_, C, D>
926{
927 fn done(
928 &self,
929 process_binary: ProcessBinary,
930 result: Result<Option<AcceptedCredential>, crate::process_checker::ProcessCheckError>,
931 ) {
932 // Check if this process was approved by the checker.
933 match result {
934 Ok(optional_credential) => {
935 if config::CONFIG.debug_load_processes {
936 debug!(
937 "Loading: Check succeeded for process {}",
938 process_binary.header.get_package_name().unwrap_or("")
939 );
940 }
941 // Save the checked process binary now that we know it is valid.
942 match self.find_open_process_binary_slot() {
943 Some(index) => {
944 self.proc_binaries.map(|proc_binaries| {
945 process_binary.credential.insert(optional_credential);
946 proc_binaries[index] = Some(process_binary);
947 });
948 }
949 None => {
950 self.client.map(|client| {
951 client.process_loaded(Err(ProcessLoadError::NoProcessSlot));
952 });
953 }
954 }
955 }
956 Err(e) => {
957 if config::CONFIG.debug_load_processes {
958 debug!(
959 "Loading: Process {} check failed {:?}",
960 process_binary.header.get_package_name().unwrap_or(""),
961 e
962 );
963 }
964 // Signal error and call try next
965 self.client.map(|client| {
966 client.process_loaded(Err(ProcessLoadError::CheckError(e)));
967 });
968 }
969 }
970
971 // Try to load the next process in flash.
972 self.deferred_call.set();
973 }
974}