1
1
// TODO: Implement `libtock_unittest`, which is referenced in the comment on
2
2
// `RawSyscalls`.
3
3
4
+ use crate :: Register ;
5
+
4
6
/// `RawSyscalls` allows a fake Tock kernel to be injected into components for
5
7
/// unit testing. It is implemented by `libtock_runtime::TockSyscalls` and
6
8
/// `libtock_unittest::fake::Kernel`. **Components should not use `RawSyscalls`
19
21
// Theoretically, RawSyscalls could consist of a single raw system call. To
20
22
// start, something like this should work:
21
23
//
22
- // unsafe fn syscall<const CLASS: usize>([usize; 4]) -> [usize; 4];
23
- //
24
- // However, this will not work with Miri's -Zmiri-track-raw-pointers flag, as it
25
- // causes pointers passed to the kernel via the Allow system calls to be
26
- // untagged. In order to work with -Zmiri-track-raw-pointers, we need to pass
27
- // pointers for the register values. Rust's closest analogue to C's void pointer
28
- // is *mut () or *const (); we use *mut () because it is shorter:
24
+ // unsafe fn syscall<const CLASS: usize>([Reg; 4]) -> [Reg; 4];
29
25
//
30
- // unsafe fn syscall<const CLASS: usize>([*mut (); 4]) -> [*mut (); 4];
26
+ // Note: Reg is an abbreviation of Register.
31
27
//
32
28
// Using a single system call has a major inefficiency. The single raw system
33
29
// call would need to clobber every register that any system call can clobber.
34
30
// Yield has a far longer clobber list than most system calls, so this would be
35
31
// inefficient for the majority of system calls. As a result, we can split yield
36
32
// out into its own function, giving the following API:
37
33
//
38
- // unsafe fn yield([*mut () ; 4]) -> [*mut () ; 4];
39
- // unsafe fn syscall<const CLASS: usize>([*mut () ; 4]) -> [*mut () ; 4];
34
+ // unsafe fn yield([Reg ; 4]) -> [Reg ; 4];
35
+ // unsafe fn syscall<const CLASS: usize>([Reg ; 4]) -> [Reg ; 4];
40
36
//
41
37
// There is one significant inefficiency remaining. Many system calls, such as
42
38
// memop's "get RAM start address" operation, do not need to set all four
43
39
// arguments. The compiler cannot optimize away this inefficiency, so to remove
44
40
// it we need to split the system calls up based on the number of arguments they
45
41
// take:
46
42
//
47
- // unsafe fn yield0([*mut () ; 0]) -> [*mut () ; 4];
48
- // unsafe fn yield1([*mut () ; 1]) -> [*mut () ; 4];
49
- // unsafe fn yield2([*mut () ; 2]) -> [*mut () ; 4];
50
- // unsafe fn yield3([*mut () ; 3]) -> [*mut () ; 4];
51
- // unsafe fn yield4([*mut () ; 4]) -> [*mut () ; 4];
52
- // unsafe fn syscall0<const CLASS: usize>([*mut () ; 0]) -> [*mut () ; 4];
53
- // unsafe fn syscall1<const CLASS: usize>([*mut () ; 1]) -> [*mut () ; 4];
54
- // unsafe fn syscall2<const CLASS: usize>([*mut () ; 2]) -> [*mut () ; 4];
55
- // unsafe fn syscall3<const CLASS: usize>([*mut () ; 3]) -> [*mut () ; 4];
56
- // unsafe fn syscall4<const CLASS: usize>([*mut () ; 4]) -> [*mut () ; 4];
43
+ // unsafe fn yield0([Reg ; 0]) -> [Reg ; 4];
44
+ // unsafe fn yield1([Reg ; 1]) -> [Reg ; 4];
45
+ // unsafe fn yield2([Reg ; 2]) -> [Reg ; 4];
46
+ // unsafe fn yield3([Reg ; 3]) -> [Reg ; 4];
47
+ // unsafe fn yield4([Reg ; 4]) -> [Reg ; 4];
48
+ // unsafe fn syscall0<const CLASS: usize>([Reg ; 0]) -> [Reg ; 4];
49
+ // unsafe fn syscall1<const CLASS: usize>([Reg ; 1]) -> [Reg ; 4];
50
+ // unsafe fn syscall2<const CLASS: usize>([Reg ; 2]) -> [Reg ; 4];
51
+ // unsafe fn syscall3<const CLASS: usize>([Reg ; 3]) -> [Reg ; 4];
52
+ // unsafe fn syscall4<const CLASS: usize>([Reg ; 4]) -> [Reg ; 4];
57
53
//
58
54
// However, not all of these are used! If we remove the system calls that are
59
55
// unused, we are left with the following:
60
56
//
61
- // unsafe fn yield1([*mut () ; 1]) -> [*mut () ; 4];
62
- // unsafe fn yield2([*mut () ; 2]) -> [*mut () ; 4];
63
- // unsafe fn syscall1<const CLASS: usize>([*mut () ; 1]) -> [*mut () ; 4];
64
- // unsafe fn syscall2<const CLASS: usize>([*mut () ; 2]) -> [*mut () ; 4];
65
- // unsafe fn syscall4<const CLASS: usize>([*mut () ; 4]) -> [*mut () ; 4];
57
+ // unsafe fn yield1([Reg ; 1]) -> [Reg ; 4];
58
+ // unsafe fn yield2([Reg ; 2]) -> [Reg ; 4];
59
+ // unsafe fn syscall1<const CLASS: usize>([Reg ; 1]) -> [Reg ; 4];
60
+ // unsafe fn syscall2<const CLASS: usize>([Reg ; 2]) -> [Reg ; 4];
61
+ // unsafe fn syscall4<const CLASS: usize>([Reg ; 4]) -> [Reg ; 4];
66
62
//
67
63
// These system calls are refined further individually, which is documented on
68
64
// a per-function basis.
@@ -86,7 +82,7 @@ pub unsafe trait RawSyscalls {
86
82
/// # Safety
87
83
/// yield1 may only be used for yield operations that do not return a value.
88
84
/// It is exactly as safe as the underlying system call.
89
- unsafe fn yield1 ( _: [ * mut ( ) ; 1 ] ) ;
85
+ unsafe fn yield1 ( _: [ Register ; 1 ] ) ;
90
86
91
87
// yield2 can only be used to call `yield-no-wait`. `yield-no-wait` does not
92
88
// return any values, so to simplify the assembly we omit return arguments.
@@ -106,7 +102,7 @@ pub unsafe trait RawSyscalls {
106
102
/// # Safety
107
103
/// yield2 may only be used for yield operations that do not return a value.
108
104
/// It has the same safety invariants as the underlying system call.
109
- unsafe fn yield2 ( _: [ * mut ( ) ; 2 ] ) ;
105
+ unsafe fn yield2 ( _: [ Register ; 2 ] ) ;
110
106
111
107
// syscall1 is only used to invoke Memop operations. Because there are no
112
108
// Memop commands that set r2 or r3, raw_syscall1 only needs to return r0
@@ -135,7 +131,7 @@ pub unsafe trait RawSyscalls {
135
131
/// This directly makes a system call. It can only be used for core kernel
136
132
/// system calls that accept 1 argument and only overwrite r0 and r1 on
137
133
/// return. It is unsafe any time the underlying system call is unsafe.
138
- unsafe fn syscall1 < const CLASS : usize > ( _: [ * mut ( ) ; 1 ] ) -> [ * mut ( ) ; 2 ] ;
134
+ unsafe fn syscall1 < const CLASS : usize > ( _: [ Register ; 1 ] ) -> [ Register ; 2 ] ;
139
135
140
136
// syscall2 is used to invoke Exit as well as Memop operations that take an
141
137
// argument. Memop does not currently use more than 2 registers for its
@@ -160,7 +156,7 @@ pub unsafe trait RawSyscalls {
160
156
/// `syscall2` directly makes a system call. It can only be used for core
161
157
/// kernel system calls that accept 2 arguments and only overwrite r0 and r1
162
158
/// on return. It is unsafe any time the underlying system call is unsafe.
163
- unsafe fn syscall2 < const CLASS : usize > ( _: [ * mut ( ) ; 2 ] ) -> [ * mut ( ) ; 2 ] ;
159
+ unsafe fn syscall2 < const CLASS : usize > ( _: [ Register ; 2 ] ) -> [ Register ; 2 ] ;
164
160
165
161
// syscall4 should:
166
162
// 1. Call the syscall class specified by CLASS.
@@ -183,5 +179,5 @@ pub unsafe trait RawSyscalls {
183
179
/// `syscall4` must NOT be used to invoke yield. Otherwise, it has the same
184
180
/// safety invariants as the underlying system call, which varies depending
185
181
/// on the system call class.
186
- unsafe fn syscall4 < const CLASS : usize > ( _: [ * mut ( ) ; 4 ] ) -> [ * mut ( ) ; 4 ] ;
182
+ unsafe fn syscall4 < const CLASS : usize > ( _: [ Register ; 4 ] ) -> [ Register ; 4 ] ;
187
183
}
0 commit comments