-
Notifications
You must be signed in to change notification settings - Fork 109
/
Copy pathentry_point.rs
367 lines (337 loc) · 14.4 KB
/
entry_point.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
use crate::memop;
use crate::syscalls;
use crate::ALLOCATOR;
use core::intrinsics;
use core::ptr;
// _start and rust_start are the first two procedures executed when a Tock
// application starts. _start is invoked directly by the Tock kernel; it
// performs stack setup then calls rust_start. rust_start performs data
// relocation and sets up the heap before calling the rustc-generated main.
// rust_start and _start are tightly coupled.
//
// The memory layout is controlled by the linker script.
//
// When the kernel gives control to us, we get r0-r3 values that is as follows.
//
// +--------------+ <- (r2) mem.len()
// | Grant |
// +--------------+
// | Unused |
// S +--------------+ <- (r3) app_heap_break
// R | Heap | (hardcoded to mem_start + 3072 in
// A +--------------| Processs::create which could be lesser than
// M | .bss | mem_start + stack + .data + .bss)
// +--------------|
// | .data |
// +--------------+
// | Stack |
// +--------------+ <- (r1) mem_start
//
// +--------------+
// | .text |
// F +--------------+
// L | .crt0_header |
// A +--------------+ <- (r0) app_start
// S | Protected |
// H | Region |
// +--------------+
//
// We want to organize the memory as follows.
//
// +--------------+ <- app_heap_break
// | Heap |
// +--------------| <- heap_start
// | .bss |
// +--------------|
// | .data |
// +--------------+ <- stack_start (stacktop)
// | Stack |
// | (grows down) |
// +--------------+ <- mem_start
//
// app_heap_break and mem_start are given to us by the kernel. The stack size is
// determined using pointer app_start, and is used with mem_start to compute
// stack_start (stacktop). The placement of .data and .bss are given to us by
// the linker script; the heap is located between the end of .bss and
// app_heap_break. This requires that .bss is the last (highest-address) section
// placed by the linker script.
/// Tock programs' entry point. Called by the kernel at program start. Sets up
/// the stack then calls rust_start() for the remainder of setup.
#[cfg(target_arch = "arm")]
#[doc(hidden)]
#[no_mangle]
#[naked]
#[link_section = ".start"]
pub unsafe extern "C" fn _start(
app_start: usize,
mem_start: usize,
_memory_len: usize,
app_heap_break: usize,
) -> ! {
asm!("
// Because ROPI-RWPI support in LLVM/rustc is incomplete, Rust
// applications must be statically linked. An offset between the
// location the program is linked at and its actual location in flash
// would cause references in .data and .rodata to point to the wrong
// data. To mitigate this, this section checks that .text (and .start)
// are loaded at the correct location. If the application was linked and
// loaded correctly, the location of the first instruction (read using
// the Program Counter) will match the intended location of .start. We
// don't have an easy way to signal an error, so for now we just yield
// if the location is wrong.
sub r4, pc, #4 // r4 = pc
ldr r5, =.start // r5 = address of .start
cmp r4, r5
beq .Lstack_init // Jump to stack initialization if pc was correct
movw r0, #8 // LowLevelDebug driver number
movw r1, #1 // LowLevelDebug 'print status code' command
movw r2, #2 // LowLevelDebug relocation failed status code
svc 2 // command() syscall
.Lyield_loop:
svc 0 // yield() syscall (in infinite loop)
b .Lyield_loop
.Lstack_init:
// Compute the stacktop (stack_start). The stacktop is computed as
// stack_size + mem_start plus padding to align the stack to a multiple
// of 8 bytes. The 8 byte alignment is to follow ARM AAPCS:
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html
ldr r4, [r0, #36] // r4 = app_start->stack_size
add r4, r4, r1 // r4 = app_start->stack_size + mem_start
add r4, #7 // r4 = app_start->stack_size + mem_start + 7
bic r4, r4, #7 // r4 = (app_start->stack_size + mem_start + 7) & ~0x7
mov sp, r4 // sp = r4
// We need to pass app_start, stacktop and app_heap_break to rust_start.
// Temporarily store them in r6, r7 and r8
mov r6, r0
mov r7, sp
// Debug support, tell the kernel the stack location
//
// memop(10, stacktop)
// r7 contains stacktop
mov r0, #10
mov r1, r7
svc 4
// Debug support, tell the kernel the heap_start location
mov r0, r6
ldr r4, [r0, #24] // r4 = app_start->bss_start
ldr r5, [r0, #28] // r5 = app_start->bss_size
add r4, r4, r5 // r4 = bss_start + bss_size
//
// memop(11, r4)
mov r0, #11
mov r1, r4
svc 4
// Store heap_start (and soon to be app_heap_break) in r8
mov r8, r4
// There is a possibility that stack + .data + .bss is greater than
// 3072. Therefore setup the initial app_heap_break to heap_start (that
// is zero initial heap) and let rust_start determine where the actual
// app_heap_break should go.
//
// Also, because app_heap_break is where the unprivileged MPU region
// ends, in case mem_start + stack + .data + .bss is greater than
// initial app_heap_break (mem_start + 3072), we will get a memory fault
// in rust_start when initializing .data and .bss. Setting
// app_heap_break to heap_start avoids that.
// memop(0, r8)
mov r0, #0
mov r1, r8
svc 4
// NOTE: If there is a hard-fault before this point, then
// process_detail_fmt in kernel/src/process.rs panics which
// will result in us losing the PC of the instruction
// generating the hard-fault. Therefore any code before
// this point is critical code
// Setup parameters needed by rust_start
// r6 (app_start), r7 (stacktop), r8 (app_heap_break)
mov r0, r6
mov r1, r7
mov r2, r8
// Call rust_start
bl rust_start"
: // No output operands
: "{r0}"(app_start), "{r1}"(mem_start), "{r3}"(app_heap_break) // Input operands
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r12",
"cc", "memory" // Clobbers
: "volatile" // Options
);
intrinsics::unreachable();
}
/// Tock programs' entry point. Called by the kernel at program start. Sets up
/// the stack then calls rust_start() for the remainder of setup.
#[cfg(target_arch = "riscv32")]
#[doc(hidden)]
#[naked]
#[no_mangle]
#[link_section = ".start"]
// The args for this function are:
// app_start: usize,
// mem_start: usize,
// memory_len: usize,
// app_heap_break: usize,
// Due to Rust issue: https://github.com/rust-lang/rust/issues/42779 we can't have
// args to the function
pub unsafe extern "C" fn _start() -> ! {
asm!(
// Compute the stack top.
//
// struct hdr* myhdr = (struct hdr*) app_start;
// uint32_t stacktop = (((uint32_t) mem_start + myhdr->stack_size + 7) & 0xfffffff8);
"lw t0, 36(a0) // t0 = myhdr->stack_size
addi t0, t0, 7 // t0 = myhdr->stack_size + 7
add t0, t0, a1 // t0 = mem_start + myhdr->stack_size + 7
li t1, 7 // t1 = 7
not t1, t1 // t1 = ~0x7
and t0, t0, t1 // t0 = (mem_start + myhdr->stack_size + 7) & ~0x7
//
// Compute the app data size and where initial app brk should go.
// This includes the GOT, data, and BSS sections. However, we can't be sure
// the linker puts them back-to-back, but we do assume that BSS is last
// (i.e. myhdr->got_start < myhdr->bss_start && myhdr->data_start <
// myhdr->bss_start). With all of that true, then the size is equivalent
// to the end of the BSS section.
//
// uint32_t appdata_size = myhdr->bss_start + myhdr->bss_size;
lw t1, 24(a0) // t1 = myhdr->bss_start
lw t2, 28(a0) // t2 = myhdr->bss_size
lw t3, 4(a0) // t3 = myhdr->got_start
add t1, t1, t2 // t1 = bss_start + bss_size
//
// Move arguments we need to keep over to callee-saved locations.
mv s0, a0 // s0 = void* app_start
mv s1, t0 // s1 = stack_top
mv s2, a3 // s2 = app_heap_break
//
// Now we may want to move the stack pointer. If the kernel set the
// `app_heap_break` larger than we need (and we are going to call `brk()`
// to reduce it) then our stack pointer will fit and we can move it now.
// Otherwise after the first syscall (the memop to set the brk), the return
// will use a stack that is outside of the process accessible memory.
//
add t2, t0, t1 // t2 = stacktop + appdata_size
bgt t2, a3, skip_set_sp // Compare `app_heap_break` with new brk.
// If our current `app_heap_break` is larger
// then we need to move the stack pointer
// before we call the `brk` syscall.
mv sp, t0 // Update the stack pointer
skip_set_sp: // Back to regularly scheduled programming.
// Call `brk` to set to requested memory
// memop(0, stacktop + appdata_size);
li a0, 4 // a0 = 4 // memop syscall
li a1, 0 // a1 = 0
mv a2, t2 // a2 = stacktop + appdata_size
ecall // memop
//
// Debug support, tell the kernel the stack location
//
// memop(10, stacktop);
li a0, 4 // a0 = 4 // memop syscall
li a1, 10 // a1 = 10
mv a2, s1 // a2 = stacktop
ecall // memop
//
// Debug support, tell the kernel the heap location
//
// memop(11, stacktop + appdata_size);
li a0, 4 // a0 = 4 // memop syscall
li a1, 11 // a1 = 10
mv a2, t2 // a2 = stacktop + appdata_size
ecall // memop
//
// Setup initial stack pointer for normal execution
// Call into the rest of startup. This should never return.
mv sp, s1 // sp = stacktop
mv a0, s0 // first arg is app_start
mv s0, sp // Set the frame pointer to sp.
mv a1, s1 // second arg is stacktop
mv a2, s2 // third arg is app_heap_break
jal rust_start"
: // No output operands
:
: "memory", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "ra" // Clobbers
: "volatile" // Options
);
intrinsics::unreachable();
}
/// Ensure an abort symbol exists.
#[cfg(target_arch = "riscv32")]
#[link_section = ".start"]
#[export_name = "abort"]
pub extern "C" fn abort() {
unsafe {
asm! ("
// Simply go back to the start as if we had just booted.
j _start
"
:
:
:
: "volatile");
}
}
/// The header encoded at the beginning of .text by the linker script. It is
/// accessed by rust_start() using its app_start parameter.
#[repr(C)]
struct LayoutHeader {
got_sym_start: usize,
got_start: usize,
got_size: usize,
data_sym_start: usize,
data_start: usize,
data_size: usize,
bss_start: usize,
bss_size: usize,
reldata_start: usize,
stack_size: usize,
}
/// Rust setup, called by _start. Uses the extern "C" calling convention so that
/// the assembly in _start knows how to call it (the Rust ABI is not defined).
/// Sets up the data segment (including relocations) and the heap, then calls
/// into the rustc-generated main(). This cannot use mutable global variables or
/// global references to globals until it is done setting up the data segment.
#[no_mangle]
pub unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_break: usize) -> ! {
extern "C" {
// This function is created internally by `rustc`. See
// `src/lang_items.rs` for more details.
fn main(argc: isize, argv: *const *const u8) -> isize;
}
// Copy .data into its final location in RAM (determined by the linker
// script -- should be immediately above the stack).
let layout_header: &LayoutHeader = core::mem::transmute(app_start);
let data_flash_start_addr = app_start + layout_header.data_sym_start;
intrinsics::copy_nonoverlapping(
data_flash_start_addr as *const u8,
stacktop as *mut u8,
layout_header.data_size,
);
// Zero .bss (specified by the linker script).
let bss_end = layout_header.bss_start + layout_header.bss_size; // 1 past the end of .bss
for i in layout_header.bss_start..bss_end {
core::ptr::write(i as *mut u8, 0);
}
// TODO: Wait for rustc to have working ROPI-RWPI relocation support, then
// implement dynamic relocations here. At the moment, rustc does not have
// working ROPI-RWPI support, and it is not clear what that support would
// look like at the LLVM level. Once we know what the relocation strategy
// looks like we can write the dynamic linker.
// Initialize the heap. Unlike libtock-c's newlib allocator, which can use
// `sbrk` system call to dynamically request heap memory from the kernel, we
// need to tell `linked_list_allocator` where the heap starts and ends.
//
// Heap size is set using `elf2tab` with `--app-heap` option, which is
// currently at 1024. If you change the `elf2tab` heap size, make sure to
// make the corresponding change here.
const HEAP_SIZE: usize = 1024;
// we could have also bss_end for app_heap_start
let app_heap_start = app_heap_break;
let app_heap_end = app_heap_break + HEAP_SIZE;
// Tell the kernel the new app heap break.
memop::set_brk(app_heap_end as *const u8);
ALLOCATOR.lock().init(app_heap_start, HEAP_SIZE);
main(0, ptr::null());
loop {
syscalls::yieldk();
}
}