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}