kernel/
process.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Types for Tock-compatible processes.

use core::fmt;
use core::fmt::Write;
use core::num::NonZeroU32;
use core::ptr::NonNull;
use core::str;

use crate::capabilities;
use crate::errorcode::ErrorCode;
use crate::ipc;
use crate::kernel::Kernel;
use crate::platform::mpu::{self};
use crate::processbuffer::{ReadOnlyProcessBuffer, ReadWriteProcessBuffer};
use crate::storage_permissions;
use crate::syscall::{self, Syscall, SyscallReturn};
use crate::upcall::UpcallId;
use crate::utilities::capability_ptr::CapabilityPtr;
use tock_tbf::types::CommandPermissions;

// Export all process related types via `kernel::process::`.
pub use crate::process_binary::ProcessBinary;
pub use crate::process_checker::AcceptedCredential;
pub use crate::process_checker::{ProcessCheckerMachine, ProcessCheckerMachineClient};
pub use crate::process_loading::load_processes;
pub use crate::process_loading::ProcessLoadError;
pub use crate::process_loading::SequentialProcessLoaderMachine;
pub use crate::process_loading::{ProcessLoadingAsync, ProcessLoadingAsyncClient};
pub use crate::process_policies::{ProcessFaultPolicy, ProcessStandardStoragePermissionsPolicy};
pub use crate::process_printer::{ProcessPrinter, ProcessPrinterContext};
pub use crate::process_standard::ProcessStandard;
pub use crate::process_standard::{ProcessStandardDebug, ProcessStandardDebugFull};

/// Userspace process identifier.
///
/// This is an opaque type that can be used to represent a running process on
/// the board without requiring an actual reference to a `Process` object.
/// Having this `ProcessId` reference type is useful for managing ownership and
/// type issues in Rust, but more importantly `ProcessId` serves as a tool for
/// capsules to hold pointers to applications.
///
/// Since `ProcessId` implements `Copy`, having an `ProcessId` does _not_ ensure
/// that the process the `ProcessId` refers to is still valid. The process may
/// have been removed, terminated, or restarted as a new process. Therefore, all
/// uses of `ProcessId` in the kernel must check that the `ProcessId` is still
/// valid. This check happens automatically when `.index()` is called, as noted
/// by the return type: `Option<usize>`. `.index()` will return the index of the
/// process in the processes array, but if the process no longer exists then
/// `None` is returned.
///
/// Outside of the kernel crate, holders of an `ProcessId` may want to use
/// `.id()` to retrieve a simple identifier for the process that can be
/// communicated over a UART bus or syscall interface. This call is guaranteed
/// to return a suitable identifier for the `ProcessId`, but does not check that
/// the corresponding application still exists.
///
/// This type also provides capsules an interface for interacting with processes
/// since they otherwise would have no reference to a `Process`. Very limited
/// operations are available through this interface since capsules should not
/// need to know the details of any given process. However, certain information
/// makes certain capsules possible to implement. For example, capsules can use
/// the `get_editable_flash_range()` function so they can safely allow an app to
/// modify its own flash.
#[derive(Clone, Copy)]
pub struct ProcessId {
    /// Reference to the main kernel struct. This is needed for checking on
    /// certain properties of the referred app (like its editable bounds), but
    /// also for checking that the index is valid.
    pub(crate) kernel: &'static Kernel,

    /// The index in the kernel.PROCESSES[] array where this app's state is
    /// stored. This makes for fast lookup of the process and helps with
    /// implementing IPC.
    ///
    /// This value is crate visible to enable optimizations in sched.rs. Other
    /// users should call `.index()` instead.
    pub(crate) index: usize,

    /// The unique identifier for this process. This can be used to refer to the
    /// process in situations where a single number is required, for instance
    /// when referring to specific applications across the syscall interface.
    ///
    /// The combination of (index, identifier) is used to check if the app this
    /// `ProcessId` refers to is still valid. If the stored identifier in the
    /// process at the given index does not match the value saved here, then the
    /// process moved or otherwise ended, and this `ProcessId` is no longer
    /// valid.
    identifier: usize,
}

impl PartialEq for ProcessId {
    fn eq(&self, other: &ProcessId) -> bool {
        self.identifier == other.identifier
    }
}

impl Eq for ProcessId {}

impl fmt::Debug for ProcessId {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        // We handle alignment and width.
        if let Some(width) = formatter.width() {
            match formatter.align() {
                Some(fmt::Alignment::Left) => {
                    write!(formatter, "{:<width$}", self.identifier, width = width)
                }
                Some(fmt::Alignment::Right) => {
                    write!(formatter, "{:width$}", self.identifier, width = width)
                }
                Some(fmt::Alignment::Center) => {
                    write!(formatter, "{:^width$}", self.identifier, width = width)
                }
                None => write!(formatter, "{:width$}", self.identifier, width = width),
            }
        } else {
            // Otherwise just do default.
            write!(formatter, "{}", self.identifier)
        }
    }
}

impl ProcessId {
    /// Create a new `ProcessId` object based on the app identifier and its
    /// index in the processes array.
    pub(crate) fn new(kernel: &'static Kernel, identifier: usize, index: usize) -> ProcessId {
        ProcessId {
            kernel,
            index,
            identifier,
        }
    }

    /// Create a new `ProcessId` object based on the app identifier and its
    /// index in the processes array.
    ///
    /// This constructor is public but protected with a capability so that
    /// external implementations of `Process` can use it.
    pub fn new_external(
        kernel: &'static Kernel,
        identifier: usize,
        index: usize,
        _capability: &dyn capabilities::ExternalProcessCapability,
    ) -> ProcessId {
        ProcessId {
            kernel,
            index,
            identifier,
        }
    }

    /// Get the location of this app in the processes array.
    ///
    /// This will return `Some(index)` if the identifier stored in this
    /// `ProcessId` matches the app saved at the known index. If the identifier
    /// does not match then `None` will be returned.
    pub(crate) fn index(&self) -> Option<usize> {
        // Do a lookup to make sure that the index we have is correct.
        if self.kernel.processid_is_valid(self) {
            Some(self.index)
        } else {
            None
        }
    }

    /// Get a `usize` unique identifier for the app this `ProcessId` refers to.
    ///
    /// This function should not generally be used, instead code should just use
    /// the `ProcessId` object itself to refer to various apps on the system.
    /// However, getting just a `usize` identifier is particularly useful when
    /// referring to a specific app with things outside of the kernel, say for
    /// userspace (e.g. IPC) or tockloader (e.g. for debugging) where a concrete
    /// number is required.
    ///
    /// Note, this will always return the saved unique identifier for the app
    /// originally referred to, even if that app no longer exists. For example,
    /// the app may have restarted, or may have been ended or removed by the
    /// kernel. Therefore, calling `id()` is _not_ a valid way to check that an
    /// application still exists.
    pub fn id(&self) -> usize {
        self.identifier
    }

    /// Get the `ShortId` for this application this process is an execution of.
    ///
    /// The `ShortId` is an identifier for the _application_, not the particular
    /// execution (i.e. the currently running process). This makes `ShortId`
    /// distinct from `ProcessId`.
    ///
    /// This function is a helper function as capsules typically use `ProcessId`
    /// as a handle to the running process and corresponding app.
    pub fn short_app_id(&self) -> ShortId {
        self.kernel
            .process_map_or(ShortId::LocallyUnique, *self, |process| {
                process.short_app_id()
            })
    }

    /// Returns the full address of the start and end of the flash region that
    /// the app owns and can write to. This includes the app's code and data and
    /// any padding at the end of the app. It does not include the TBF header,
    /// or any space that the kernel is using for any potential bookkeeping.
    pub fn get_editable_flash_range(&self) -> (usize, usize) {
        self.kernel.process_map_or((0, 0), *self, |process| {
            let addresses = process.get_addresses();
            (addresses.flash_non_protected_start, addresses.flash_end)
        })
    }

    /// Get the storage permissions for the process. These permissions indicate
    /// what the process is allowed to read and write. Returns `None` if the
    /// process has no storage permissions.
    pub fn get_storage_permissions(&self) -> Option<storage_permissions::StoragePermissions> {
        self.kernel.process_map_or(None, *self, |process| {
            Some(process.get_storage_permissions())
        })
    }
}

/// A compressed form of an Application Identifier.
///
/// ShortIds are useful for more efficient operations with app identifiers
/// within the kernel. They are guaranteed to be unique among all running
/// processes on the same board. However, as they are only 32 bits they are not
/// globally unique.
///
/// ShortIds are persistent across restarts of the same app (whereas ProcessIDs
/// are not).
///
/// As ShortIds must be unique for each app on a board, and since not every
/// platform may have a use for ShortIds, the definition of a ShortId provides a
/// convenient mechanism for meeting the uniqueness requirement without actually
/// requiring assigning unique discrete values to each app. This is done with
/// the `LocallyUnique` variant which is an abstract ID that is guaranteed to be
/// unique (i.e. an equality comparison with any other ShortId will always
/// return `false`). Platforms which have a use for an actual number for a
/// `ShortId` should use the `Fixed(NonZeroU32)` variant. Note, for type space
/// efficiency, we disallow using the number 0 as a fixed ShortId.
///
/// ShortIds are assigned to the app as part of the credential checking process.
/// Specifically, an implementation of the `process_checker::Compress` trait
/// assigns ShortIds.
#[derive(Clone, Copy)]
pub enum ShortId {
    /// An abstract `ShortId` that is always guaranteed to be unique. As this is
    /// not an actual discrete value, it cannot be used for anything other than
    /// meeting the uniqueness requirement.
    LocallyUnique,
    /// A 32 bit number `ShortId`. This fixed value is guaranteed to be unique
    /// among all running processes as the kernel will not start two processes
    /// with the same ShortId.
    Fixed(core::num::NonZeroU32),
}

impl PartialEq for ShortId {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (ShortId::Fixed(a), ShortId::Fixed(b)) => a == b,
            _ => false,
        }
    }
}
impl Eq for ShortId {}

impl core::convert::From<Option<core::num::NonZeroU32>> for ShortId {
    fn from(id: Option<core::num::NonZeroU32>) -> ShortId {
        match id {
            Some(fixed) => ShortId::Fixed(fixed),
            None => ShortId::LocallyUnique,
        }
    }
}

impl core::fmt::Display for ShortId {
    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> fmt::Result {
        match *self {
            ShortId::LocallyUnique => {
                write!(fmt, "Unique")
            }
            ShortId::Fixed(id) => {
                write!(fmt, "0x{:<8x} ", id)
            }
        }
    }
}

/// Enum used to inform scheduler why a process stopped executing (aka why
/// `do_process()` returned).
///
/// This is publicly exported to allow for schedulers implemented outside of the
/// kernel crate.
#[derive(PartialEq, Eq)]
pub enum StoppedExecutingReason {
    /// The process returned because it is no longer ready to run.
    NoWorkLeft,

    /// The process faulted, and the board restart policy was configured such
    /// that it was not restarted and there was not a kernel panic.
    StoppedFaulted,

    /// The kernel stopped the process.
    Stopped,

    /// The process was preempted because its timeslice expired.
    TimesliceExpired,

    /// The process returned because it was preempted by the kernel. This can
    /// mean that kernel work became ready (most likely because an interrupt
    /// fired and the kernel thread needs to execute the bottom half of the
    /// interrupt), or because the scheduler no longer wants to execute that
    /// process.
    KernelPreemption,
}

/// The version of a binary.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct BinaryVersion(NonZeroU32);

impl BinaryVersion {
    /// Creates a new binary version.
    pub fn new(value: NonZeroU32) -> Self {
        Self(value)
    }
}

/// This trait represents a generic process that the Tock scheduler can
/// schedule.
pub trait Process {
    /// Returns the process's identifier.
    fn processid(&self) -> ProcessId;

    /// Returns the [`ShortId`] generated by the application binary checker at
    /// loading.
    fn short_app_id(&self) -> ShortId;

    /// Returns the version number of the binary in this process, as specified
    /// in a TBF Program Header. If the binary has no version assigned this
    /// returns [`None`].
    fn binary_version(&self) -> Option<BinaryVersion>;

    /// Return the credential which the credential checker approved if the
    /// credential checker approved a credential. If the process was allowed to
    /// run without credentials, return `None`.
    fn get_credential(&self) -> Option<AcceptedCredential>;

    /// Returns how many times this process has been restarted.
    fn get_restart_count(&self) -> usize;

    /// Get the name of the process. Used for IPC.
    fn get_process_name(&self) -> &'static str;

    /// Return if there are any Tasks (upcalls/IPC requests) enqueued for the
    /// process.
    fn has_tasks(&self) -> bool;

    /// Returns the number of pending tasks. If 0 then `dequeue_task()` will
    /// return `None` when called.
    fn pending_tasks(&self) -> usize;

    /// Queue a [`Task`] for the process. This will be added to a per-process
    /// buffer and executed by the scheduler. [`Task`]s are some function the
    /// process should run, for example a upcall or an IPC call.
    ///
    /// This function returns:
    /// - `Ok(())` if the [`Task`] was successfully enqueued.
    /// - [`Err(ErrorCode::NODEVICE)`] if the process is no longer alive.
    /// - [`Err(ErrorCode::NOMEM)`] if the task could not be enqueued because
    ///   there is insufficient space in the internal task queue.
    ///
    /// Other return values must be treated as kernel-internal errors.
    fn enqueue_task(&self, task: Task) -> Result<(), ErrorCode>;

    /// Remove the scheduled operation from the front of the queue and return it
    /// to be handled by the scheduler.
    ///
    /// If there are no [`Task`]s in the queue for this process this will return
    /// [`None`].
    fn dequeue_task(&self) -> Option<Task>;

    /// Search the work queue for the first pending operation with the given
    /// `upcall_id` and if one exists remove it from the queue.process
    ///
    /// ## Returns
    ///
    /// Returns the associated [`Task`] if one was found, otherwise returns
    /// [`None`].
    fn remove_upcall(&self, upcall_id: UpcallId) -> Option<Task>;

    /// Remove all scheduled upcalls with the given `upcall_id` from the task
    /// queue.
    fn remove_pending_upcalls(&self, upcall_id: UpcallId);

    /// Returns the current state the process is in.
    fn get_state(&self) -> State;

    /// Returns whether this process is ready to execute.
    ///
    /// A process is ready if it has work to do or if it was interrupted while
    /// executing and can continue to execute.
    ///
    /// ## Returns
    ///
    /// `true` if the process is ready and `false` otherwise.
    fn ready(&self) -> bool;

    /// Returns whether the process is running or not.
    ///
    /// A process is considered running if it is active and has active stack
    /// frames. A running process can be executed.
    ///
    /// A process that is not running cannot be executed. The process may have
    /// faulted or it may have completed.
    ///
    /// ## Returns
    ///
    /// `true` if the process is running and `false` otherwise.
    fn is_running(&self) -> bool;

    /// Move this process from the running state to the yielded state.
    ///
    /// This will fail (i.e. not do anything) if the process was not previously
    /// running.
    fn set_yielded_state(&self);

    /// Move this process from the running state to the yielded-for state.
    ///
    /// This will fail (i.e. not do anything) if the process was not previously
    /// running.
    fn set_yielded_for_state(&self, upcall_id: UpcallId);

    /// Move this process from running or yielded state into the stopped state.
    ///
    /// This will fail (i.e. not do anything) if the process was not either
    /// running or yielded.
    fn stop(&self);

    /// Move this stopped process back into its original state.
    ///
    /// This transitions a process from
    /// [`Stopped`](State::Stopped) to [`Running`](State::Running), [`Yielded`](State::Running) or
    /// [`YieldedFor`](State::YieldedFor).
    ///
    /// This will fail (i.e. not do anything) if the process was not stopped.
    fn resume(&self);

    /// Put this process in the fault state.
    ///
    /// The kernel will use the process's fault policy to decide what action to
    /// take in regards to the faulted process.
    fn set_fault_state(&self);

    /// Start a terminated process. This function can only be called on a
    /// terminated process.
    ///
    /// The caller MUST verify this process is unique before calling this
    /// function. This requires a capability to call to ensure that the caller
    /// have verified that this process is unique before trying to start it.
    fn start(&self, cap: &dyn crate::capabilities::ProcessStartCapability);

    /// Terminates and attempts to restart the process. The process and current
    /// application always terminate. The kernel may, based on its own policy,
    /// restart the application using the same process, reuse the process for
    /// another application, or simply terminate the process and application.
    ///
    /// This function can be called when the process is in any state except for
    /// [`Terminated`](State::Terminated). It attempts to reset all process
    /// state and re-initialize it so that it can be reused.
    ///
    /// Restarting an application can fail for three general reasons:
    ///
    /// 1. The process is already terminated. Use [`Process::start()`] instead.
    ///
    /// 2. The kernel chooses not to restart the application, based on its
    ///    policy.
    ///
    /// 3. The kernel decides to restart the application but fails to do so
    ///    because some state can no long be configured for the process. For
    ///    example, the syscall state for the process fails to initialize.
    ///
    /// After `try_restart()` runs, the process will either be queued to run the
    /// same application's `_start` function, terminated, or queued to run a
    /// different application's `_start` function.
    ///
    /// As the process will be terminated before being restarted, this function
    /// accepts an optional `completion_code`. If the process provided a
    /// completion code (e.g. via the exit syscall), then this should be called
    /// with `Some(u32)`. If the kernel is trying to restart the process and the
    /// process did not provide a completion code, then this should be called
    /// with `None`.
    fn try_restart(&self, completion_code: Option<u32>);

    /// Stop and clear a process's state and put it into the
    /// [`Terminated`](State::Terminated) state.
    ///
    /// This will end the process, but does not reset it such that it could be
    /// restarted and run again. This function instead frees grants and any
    /// queued tasks for this process, but leaves the debug information about
    /// the process and other state intact.
    ///
    /// When a process is terminated, an optional `completion_code` should be
    /// stored for the process. If the process provided the completion code
    /// (e.g. via the exit syscall), then this function should be called with a
    /// completion code of `Some(u32)`. If the kernel is terminating the process
    /// and therefore has no completion code from the process, it should provide
    /// `None`.
    fn terminate(&self, completion_code: Option<u32>);

    /// Get the completion code if the process has previously terminated.
    ///
    /// ## Returns
    ///
    /// If the process has never terminated then there has been no opportunity
    /// for a completion code to be set, and this will return `None`.
    ///
    /// If the process has previously terminated this will return `Some()`. If
    /// the last time the process terminated it did not provide a completion
    /// code (e.g. the process faulted), then this will return `Some(None)`. If
    /// the last time the process terminated it did provide a completion code,
    /// this will return `Some(Some(completion_code))`.
    fn get_completion_code(&self) -> Option<Option<u32>>;

    // memop operations

    /// Change the location of the program break to `new_break` and reallocate
    /// the MPU region covering program memory.
    ///
    /// ## Returns
    ///
    /// On success, return the previous break address with authority that
    /// has RW permissions from the start of process RAM to the new break.
    ///
    /// On error, return:
    /// - [`Error::InactiveApp`] if the process is not running and adjusting the
    ///   memory pointers is not valid.
    /// - [`Error::OutOfMemory`] if the requested break would overlap with the
    ///   grant region or if the newly requested memory cannot be protected with
    ///   the MPU.
    /// - [`Error::AddressOutOfBounds`] if the requested break is beyond the
    ///   process's memory region.
    /// - [`Error::KernelError`] if there was an internal kernel error. This is
    ///   a bug.
    fn brk(&self, new_break: *const u8) -> Result<CapabilityPtr, Error>;

    /// Change the location of the program break by `increment` bytes,
    /// reallocate the MPU region covering program memory, and return the
    /// previous break address.
    ///
    /// ## Returns
    ///
    /// On success, return the previous break address with authority that
    /// has RW permissions from the start of process RAM to the new break.
    ///
    /// On error, return:
    /// - [`Error::InactiveApp`] if the process is not running and adjusting the
    ///   sbrk is not valid.
    /// - [`Error::OutOfMemory`] if the requested break would overlap with the
    ///   grant region or if the newly requested memory cannot be protected with
    ///   the MPU.
    /// - [`Error::AddressOutOfBounds`] if the requested break is beyond the
    ///   process's memory region.
    /// - [`Error::KernelError`] if there was an internal kernel error. This is
    ///   a bug.
    fn sbrk(&self, increment: isize) -> Result<CapabilityPtr, Error>;

    /// How many writeable flash regions defined in the TBF header for this
    /// process.
    ///
    /// ## Returns
    ///
    /// The number of writeable flash regions defined for this process.
    fn number_writeable_flash_regions(&self) -> usize;

    /// Get the offset from the beginning of flash and the size of the defined
    /// writeable flash region.
    ///
    /// ## Returns
    ///
    /// A tuple containing the a `usize` of the offset from the beginning of the
    /// process's flash region where the writeable region starts and a `usize` of
    /// the size of the region in bytes.
    fn get_writeable_flash_region(&self, region_index: usize) -> (usize, usize);

    /// Debug function to update the kernel on where the stack starts for this
    /// process. Processes are not required to call this through the memop
    /// system call, but it aids in debugging the process.
    fn update_stack_start_pointer(&self, stack_pointer: *const u8);

    /// Debug function to update the kernel on where the process heap starts.
    /// Also optional.
    fn update_heap_start_pointer(&self, heap_pointer: *const u8);

    /// Creates a [`ReadWriteProcessBuffer`] from the given offset and size in
    /// process memory.
    ///
    /// ## Returns
    ///
    /// In case of success, this method returns the created
    /// [`ReadWriteProcessBuffer`].
    ///
    /// In case of an error, an appropriate `ErrorCode` is returned:
    ///
    /// - If the memory is not contained in the process-accessible memory space
    ///   / `buf_start_addr` and `size` are not a valid read-write buffer (any
    ///   byte in the range is not read/write accessible to the process):
    ///   [`ErrorCode::INVAL`].
    /// - If the process is not active: [`ErrorCode::FAIL`].
    /// - For all other errors: [`ErrorCode::FAIL`].
    fn build_readwrite_process_buffer(
        &self,
        buf_start_addr: *mut u8,
        size: usize,
    ) -> Result<ReadWriteProcessBuffer, ErrorCode>;

    /// Creates a [`ReadOnlyProcessBuffer`] from the given offset and size in
    /// process memory.
    ///
    /// ## Returns
    ///
    /// In case of success, this method returns the created
    /// [`ReadOnlyProcessBuffer`].
    ///
    /// In case of an error, an appropriate ErrorCode is returned:
    ///
    /// - If the memory is not contained in the process-accessible memory space
    ///   / `buf_start_addr` and `size` are not a valid read-only buffer (any
    ///   byte in the range is not read-accessible to the process):
    ///   [`ErrorCode::INVAL`].
    /// - If the process is not active: [`ErrorCode::FAIL`].
    /// - For all other errors: [`ErrorCode::FAIL`].
    fn build_readonly_process_buffer(
        &self,
        buf_start_addr: *const u8,
        size: usize,
    ) -> Result<ReadOnlyProcessBuffer, ErrorCode>;

    /// Set a single byte within the process address space at `addr` to `value`.
    /// Return true if `addr` is within the RAM bounds currently exposed to the
    /// process (thereby writable by the process itself) and the value was set,
    /// false otherwise.
    ///
    /// ### Safety
    ///
    /// This function verifies that the byte to be written is in the process's
    /// accessible memory. However, to avoid undefined behavior the caller needs
    /// to ensure that no other references exist to the process's memory before
    /// calling this function.
    unsafe fn set_byte(&self, addr: *mut u8, value: u8) -> bool;

    /// Return the permissions for this process for a given `driver_num`.
    ///
    /// The returned `CommandPermissions` will indicate if any permissions for
    /// individual command numbers are specified. If there are permissions set
    /// they are returned as a 64 bit bitmask for sequential command numbers.
    /// The offset indicates the multiple of 64 command numbers to get
    /// permissions for.
    fn get_command_permissions(&self, driver_num: usize, offset: usize) -> CommandPermissions;

    /// Get the storage permissions for the process.
    ///
    /// Returns `None` if the process has no storage permissions.
    fn get_storage_permissions(&self) -> storage_permissions::StoragePermissions;

    // mpu

    /// Configure the MPU to use the process's allocated regions.
    ///
    /// It is not valid to call this function when the process is inactive (i.e.
    /// the process will not run again).
    fn setup_mpu(&self);

    /// Allocate a new MPU region for the process that is at least
    /// `min_region_size` bytes and lies within the specified stretch of
    /// unallocated memory.
    ///
    /// It is not valid to call this function when the process is inactive (i.e.
    /// the process will not run again).
    fn add_mpu_region(
        &self,
        unallocated_memory_start: *const u8,
        unallocated_memory_size: usize,
        min_region_size: usize,
    ) -> Option<mpu::Region>;

    /// Removes an MPU region from the process that has been previously added
    /// with `add_mpu_region`.
    ///
    /// It is not valid to call this function when the process is inactive (i.e.
    /// the process will not run again).
    fn remove_mpu_region(&self, region: mpu::Region) -> Result<(), ErrorCode>;

    // grants

    /// Allocate memory from the grant region and store the reference in the
    /// proper grant pointer index.
    ///
    /// This function must check that doing the allocation does not cause the
    /// kernel memory break to go below the top of the process accessible memory
    /// region allowed by the MPU. Note, this can be different from the actual
    /// app_brk, as MPU alignment and size constraints may result in the MPU
    /// enforced region differing from the app_brk.
    ///
    /// This will return `Err(())` and fail if:
    /// - The process is inactive, or
    /// - There is not enough available memory to do the allocation, or
    /// - The grant_num is invalid, or
    /// - The grant_num already has an allocated grant.
    fn allocate_grant(
        &self,
        grant_num: usize,
        driver_num: usize,
        size: usize,
        align: usize,
    ) -> Result<(), ()>;

    /// Check if a given grant for this process has been allocated.
    ///
    /// Returns `None` if the process is not active. Otherwise, returns `true`
    /// if the grant has been allocated, `false` otherwise.
    fn grant_is_allocated(&self, grant_num: usize) -> Option<bool>;

    /// Allocate memory from the grant region that is `size` bytes long and
    /// aligned to `align` bytes. This is used for creating custom grants which
    /// are not recorded in the grant pointer array, but are useful for capsules
    /// which need additional process-specific dynamically allocated memory.
    ///
    /// If successful, return a Ok() with an identifier that can be used with
    /// `enter_custom_grant()` to get access to the memory and the pointer to
    /// the memory which must be used to initialize the memory.
    fn allocate_custom_grant(
        &self,
        size: usize,
        align: usize,
    ) -> Result<(ProcessCustomGrantIdentifier, NonNull<u8>), ()>;

    /// Enter the grant based on `grant_num` for this process.
    ///
    /// Entering a grant means getting access to the actual memory for the
    /// object stored as the grant.
    ///
    /// This will return an `Err` if the process is inactive of the `grant_num`
    /// is invalid, if the grant has not been allocated, or if the grant is
    /// already entered. If this returns `Ok()` then the pointer points to the
    /// previously allocated memory for this grant.
    fn enter_grant(&self, grant_num: usize) -> Result<NonNull<u8>, Error>;

    /// Enter a custom grant based on the `identifier`.
    ///
    /// This retrieves a pointer to the previously allocated custom grant based
    /// on the identifier returned when the custom grant was allocated.
    ///
    /// This returns an error if the custom grant is no longer accessible, or if
    /// the process is inactive.
    fn enter_custom_grant(
        &self,
        identifier: ProcessCustomGrantIdentifier,
    ) -> Result<*mut u8, Error>;

    /// Opposite of `enter_grant()`. Used to signal that the grant is no longer
    /// entered.
    ///
    /// If `grant_num` is valid, this function cannot fail. If `grant_num` is
    /// invalid, this function will do nothing. If the process is inactive then
    /// grants are invalid and are not entered or not entered, and this function
    /// will do nothing.
    ///
    /// ### Safety
    ///
    /// The caller must ensure that no references to the memory inside the grant
    /// exist after calling `leave_grant()`. Otherwise, it would be possible to
    /// effectively enter the grant twice (once using the existing reference,
    /// once with a new call to `enter_grant()`) which breaks the memory safety
    /// requirements of grants.
    unsafe fn leave_grant(&self, grant_num: usize);

    /// Return the count of the number of allocated grant pointers if the
    /// process is active. This does not count custom grants. This is used to
    /// determine if a new grant has been allocated after a call to
    /// `SyscallDriver::allocate_grant()`.
    ///
    /// Useful for debugging/inspecting the system.
    fn grant_allocated_count(&self) -> Option<usize>;

    /// Get the grant number (grant_num) associated with a given driver number
    /// if there is a grant associated with that driver_num.
    fn lookup_grant_from_driver_num(&self, driver_num: usize) -> Result<usize, Error>;

    // subscribe

    /// Verify that an upcall function pointer is within process-accessible
    /// memory.
    ///
    /// Returns `true` if the upcall function pointer is valid for this process,
    /// and `false` otherwise.
    // `upcall_fn` can eventually be a better type:
    // <https://github.com/tock/tock/issues/4134>
    fn is_valid_upcall_function_pointer(&self, upcall_fn: *const ()) -> bool;

    // functions for processes that are architecture specific

    /// Set the return value the process should see when it begins executing
    /// again after the syscall.
    ///
    /// It is not valid to call this function when the process is inactive (i.e.
    /// the process will not run again).
    ///
    /// This can fail, if the UKB implementation cannot correctly set the return
    /// value. An example of how this might occur:
    ///
    /// 1. The UKB implementation uses the process's stack to transfer values
    ///    between kernelspace and userspace.
    /// 2. The process calls memop.brk and reduces its accessible memory region
    ///    below its current stack.
    /// 3. The UKB implementation can no longer set the return value on the
    ///    stack since the process no longer has access to its stack.
    ///
    /// If it fails, the process will be put into the faulted state.
    fn set_syscall_return_value(&self, return_value: SyscallReturn);

    /// Set the function that is to be executed when the process is resumed.
    ///
    /// It is not valid to call this function when the process is inactive (i.e.
    /// the process will not run again).
    fn set_process_function(&self, callback: FunctionCall);

    /// Context switch to a specific process.
    ///
    /// This will return `None` if the process is inactive and cannot be
    /// switched to.
    fn switch_to(&self) -> Option<syscall::ContextSwitchReason>;

    /// Return process state information related to the location in memory of
    /// various process data structures.
    fn get_addresses(&self) -> ProcessAddresses;

    /// Return process state information related to the size in memory of
    /// various process data structures.
    fn get_sizes(&self) -> ProcessSizes;

    /// Write stored state as a binary blob into the `out` slice. Returns the
    /// number of bytes written to `out` on success.
    ///
    /// Returns `ErrorCode::SIZE` if `out` is too short to hold the stored state
    /// binary representation. Returns `ErrorCode::FAIL` on an internal error.
    fn get_stored_state(&self, out: &mut [u8]) -> Result<usize, ErrorCode>;

    /// Print out the full state of the process: its memory map, its context,
    /// and the state of the memory protection unit (MPU).
    fn print_full_process(&self, writer: &mut dyn Write);

    // debug

    /// Returns how many syscalls this app has called.
    fn debug_syscall_count(&self) -> usize;

    /// Returns how many upcalls for this process have been dropped.
    fn debug_dropped_upcall_count(&self) -> usize;

    /// Returns how many times this process has exceeded its timeslice.
    fn debug_timeslice_expiration_count(&self) -> usize;

    /// Increment the number of times the process has exceeded its timeslice.
    fn debug_timeslice_expired(&self);

    /// Increment the number of times the process called a syscall and record
    /// the last syscall that was called.
    fn debug_syscall_called(&self, last_syscall: Syscall);

    /// Return the last syscall the process called. Returns `None` if the
    /// process has not called any syscalls or the information is unknown.
    fn debug_syscall_last(&self) -> Option<Syscall>;
}

/// Opaque identifier for custom grants allocated dynamically from a process's
/// grant region.
///
/// This type allows Process to provide a handle to a custom grant within a
/// process's memory that `ProcessGrant` can use to access the custom grant
/// memory later.
///
/// We use this type rather than a direct pointer so that any attempt to access
/// can ensure the process still exists and is valid, and that the custom grant
/// has not been freed.
///
/// The fields of this struct are private so only Process can create this
/// identifier.
#[derive(Copy, Clone)]
pub struct ProcessCustomGrantIdentifier {
    pub(crate) offset: usize,
}

/// Error types related to processes.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
    /// The process has been removed and no longer exists. For example, the
    /// kernel could stop a process and re-claim its resources.
    NoSuchApp,
    /// The process does not have enough memory to complete the requested
    /// operation.
    OutOfMemory,
    /// The provided memory address is not accessible or not valid for the
    /// process.
    AddressOutOfBounds,
    /// The process is inactive (likely in a fault or exit state) and the
    /// attempted operation is therefore invalid.
    InactiveApp,
    /// This likely indicates a bug in the kernel and that some state is
    /// inconsistent in the kernel.
    KernelError,
    /// Indicates some process data, such as a Grant, is already borrowed.
    AlreadyInUse,
}

impl From<Error> for Result<(), ErrorCode> {
    fn from(err: Error) -> Result<(), ErrorCode> {
        match err {
            Error::OutOfMemory => Err(ErrorCode::NOMEM),
            Error::AddressOutOfBounds => Err(ErrorCode::INVAL),
            Error::NoSuchApp => Err(ErrorCode::INVAL),
            Error::InactiveApp => Err(ErrorCode::FAIL),
            Error::KernelError => Err(ErrorCode::FAIL),
            Error::AlreadyInUse => Err(ErrorCode::FAIL),
        }
    }
}

impl From<Error> for ErrorCode {
    fn from(err: Error) -> ErrorCode {
        match err {
            Error::OutOfMemory => ErrorCode::NOMEM,
            Error::AddressOutOfBounds => ErrorCode::INVAL,
            Error::NoSuchApp => ErrorCode::INVAL,
            Error::InactiveApp => ErrorCode::FAIL,
            Error::KernelError => ErrorCode::FAIL,
            Error::AlreadyInUse => ErrorCode::FAIL,
        }
    }
}

/// States a process can be in.
///
/// This is public so external implementations of `Process` can re-use these
/// process states.
///
/// While a process is running, it transitions between the `Running`, `Yielded`,
/// `YieldedFor`, and `Stopped` states. If an error occurs (e.g., a memory
/// access error), the kernel faults it and either leaves it in the `Faulted`
/// state, restarts it, or takes some other action defined by the kernel fault
/// policy. If the process issues an `exit-terminate` system call, it enters the
/// `Terminated` state. If it issues an `exit-restart` system call, it
/// terminates then tries to back to a runnable state.
///
/// When a process faults, it enters the `Faulted` state. To be restarted, it
/// must first transition to the `Terminated` state, which means that all of its
/// state has been cleaned up.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum State {
    /// Process expects to be running code. The process may not be currently
    /// scheduled by the scheduler, but the process has work to do if it is
    /// scheduled.
    Running,

    /// Process stopped executing and returned to the kernel because it called
    /// the `yield` syscall. This likely means it is waiting for some event to
    /// occur, but it could also mean it has finished and doesn't need to be
    /// scheduled again.
    Yielded,

    /// Process stopped executing and returned to the kernel because it called
    /// the `WaitFor` variant of the `yield` syscall. The process should not be
    /// scheduled until the specified driver attempts to execute the specified
    /// upcall.
    YieldedFor(UpcallId),

    /// The process is stopped and the previous state the process was in when it
    /// was stopped. This is used if the kernel forcibly stops a process. This
    /// state indicates to the kernel not to schedule the process, but if the
    /// process is to be resumed later it should be put back in its previous
    /// state so it will execute correctly.
    Stopped(StoppedState),

    /// The process ran, faulted while running, and is no longer runnable. For a
    /// faulted process to be made runnable, it must first be terminated (to
    /// clean up its state).
    Faulted,

    /// The process is not running: it exited with the `exit-terminate` system
    /// call or was terminated for some other reason (e.g., by the process
    /// console). Processes in the `Terminated` state can be run again.
    Terminated,
}

/// States a process could previously have been in when stopped.
///
/// This is public so external implementations of `Process` can re-use these
/// process stopped states.
///
/// These are recorded so the process can be returned to its previous state when
/// it is resumed.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum StoppedState {
    /// The process was in the running state when it was stopped.
    Running,
    /// The process was in the yielded state when it was stopped.
    Yielded,
    /// The process was in the yielded for state when it was stopped with a
    /// particular upcall it was waiting for.
    YieldedFor(UpcallId),
}

/// The action the kernel should take when a process encounters a fault.
///
/// When an exception occurs during a process's execution (a common example is a
/// process trying to access memory outside of its allowed regions) the system
/// will trap back to the kernel, and the kernel has to decide what to do with
/// the process at that point.
///
/// The actions are separate from the policy on deciding which action to take. A
/// separate process-specific policy should determine which action to take.
#[derive(Copy, Clone)]
pub enum FaultAction {
    /// Generate a `panic!()` call and crash the entire system. This is useful
    /// for debugging applications as the error is displayed immediately after
    /// it occurs.
    Panic,

    /// Attempt to cleanup and restart the process which caused the fault. This
    /// resets the process's memory to how it was when the process was started
    /// and schedules the process to run again from its init function.
    Restart,

    /// Stop the process by no longer scheduling it to run.
    Stop,
}

/// Tasks that can be enqueued for a process.
///
/// This is public for external implementations of `Process`.
#[derive(Copy, Clone)]
pub enum Task {
    /// Function pointer in the process to execute. Generally this is a upcall
    /// from a capsule.
    FunctionCall(FunctionCall),
    /// Data to return to the process. This is used to resume a suspended
    /// process without invoking any callbacks in userspace (e.g., in response
    /// to a YieldFor).
    ReturnValue(ReturnArguments),
    /// An IPC operation that needs additional setup to configure memory access.
    IPC((ProcessId, ipc::IPCUpcallType)),
}

/// Enumeration to identify whether a function call for a process comes directly
/// from the kernel or from a upcall subscribed through a `Driver`
/// implementation.
///
/// An example of a kernel function is the application entry point.
#[derive(Copy, Clone, Debug)]
pub enum FunctionCallSource {
    /// For functions coming directly from the kernel, such as `init_fn`.
    Kernel,
    /// For functions coming from capsules or any implementation of `Driver`.
    Driver(UpcallId),
}

/// Struct that defines a upcall that can be passed to a process. The upcall
/// takes four arguments that are `Driver` and upcall specific, so they are
/// represented generically here.
///
/// Likely these four arguments will get passed as the first four register
/// values, but this is architecture-dependent.
///
/// A `FunctionCall` also identifies the upcall that scheduled it, if any, so
/// that it can be unscheduled when the process unsubscribes from this upcall.
#[derive(Copy, Clone, Debug)]
pub struct FunctionCall {
    /// Whether the kernel called this directly or this is an upcall.
    pub source: FunctionCallSource,
    /// The first argument to the function.
    pub argument0: usize,
    /// The second argument to the function.
    pub argument1: usize,
    /// The third argument to the function.
    pub argument2: usize,
    /// The userdata provided by the process via `subscribe`
    pub argument3: CapabilityPtr,
    /// The PC of the function to execute.
    pub pc: CapabilityPtr,
}

/// This is similar to `FunctionCall` but for the special case of the Null
/// Upcall for a subscribe.
///
/// Because there is no function pointer in a Null Upcall we can only
/// return these values to userspace. This is used to pass around
/// upcall parameters when there is no associated upcall to actually
/// call or userdata.
#[derive(Copy, Clone, Debug)]
pub struct ReturnArguments {
    /// Which upcall generates this event.
    pub upcall_id: UpcallId,
    /// The first argument to return.
    pub argument0: usize,
    /// The second argument to return.
    pub argument1: usize,
    /// The third argument to return.
    pub argument2: usize,
}

/// Collection of process state information related to the memory addresses of
/// different elements of the process.
pub struct ProcessAddresses {
    /// The address of the beginning of the process's region in nonvolatile
    /// memory.
    pub flash_start: usize,
    /// The address of the beginning of the region the process has access to in
    /// nonvolatile memory. This is after the TBF header and any other memory
    /// the kernel has reserved for its own use.
    pub flash_non_protected_start: usize,
    /// The address immediately after the end of part of the process binary that
    /// is covered by integrity; the integrity region is [flash_start -
    /// flash_integrity_end). Footers are stored in the flash after
    /// flash_integrity_end.
    pub flash_integrity_end: *const u8,
    /// The address immediately after the end of the region allocated for this
    /// process in nonvolatile memory.
    pub flash_end: usize,
    /// The address of the beginning of the process's allocated region in
    /// memory.
    pub sram_start: usize,
    /// The address of the application break. This is the address immediately
    /// after the end of the memory the process has access to.
    pub sram_app_brk: usize,
    /// The lowest address of any allocated grant. This is the start of the
    /// region the kernel is using for its own internal state on behalf of this
    /// process.
    pub sram_grant_start: usize,
    /// The address immediately after the end of the region allocated for this
    /// process in memory.
    pub sram_end: usize,

    /// The address of the start of the process's heap, if known. Note, managing
    /// this is completely up to the process, and the kernel relies on the
    /// process explicitly notifying it of this address. Therefore, its possible
    /// the kernel does not know the start address, or its start address could
    /// be incorrect.
    pub sram_heap_start: Option<usize>,
    /// The address of the top (or start) of the process's stack, if known.
    /// Note, managing the stack is completely up to the process, and the kernel
    /// relies on the process explicitly notifying it of where it started its
    /// stack. Therefore, its possible the kernel does not know the start
    /// address, or its start address could be incorrect.
    pub sram_stack_top: Option<usize>,
    /// The lowest address the kernel has seen the stack pointer. Note, the
    /// stack is entirely managed by the process, and the process could
    /// intentionally obscure this address from the kernel. Also, the stack may
    /// have reached a lower address, this is only the lowest address seen when
    /// the process calls a syscall.
    pub sram_stack_bottom: Option<usize>,
}

/// Collection of process state related to the size in memory of various process
/// structures.
pub struct ProcessSizes {
    /// The number of bytes used for the grant pointer table.
    pub grant_pointers: usize,
    /// The number of bytes used for the pending upcall queue.
    pub upcall_list: usize,
    /// The number of bytes used for the process control block (i.e. the
    /// `ProcessX` struct).
    pub process_control_block: usize,
}