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
|
.extern hart0_boot
.extern hart0_early_boot
.section .text.start
.global _start
.type _start, STT_FUNC
_start:
## Have harts other than hart0 spin until hart0 wakes them up. As a
## side effect, load every hart's thread pointer register with MHARTID.
csrr tp, mhartid
bnez tp, wait_for_hart0
### Set up trap handling.
## Set MENVCFG.STCE, to enable the Sstc extension, which will allow us
## to delegate timer interrupts to Supervisor mode.
li t0, 1<<63
csrs menvcfg, t0
## Set MCOUNTEREN.{CY, TM, IR} to 1, enabling Supervisor mode accesses
## to the cycle, time, stimecmp, and instret MSRs.
li t0, 0b111
csrs mcounteren, t0
## Set MIE.{SSIE, STIE, SEIE} to 1, enabling software, timer, and
## exception traps in Supervisor mode.
li t0, 0b1000100010
csrs mie, t0
## Delegate all exceptions to Supervisor mode.
li t0, 0xffff
csrw medeleg, t0
csrw mideleg, t0
### Go from Machine mode to Supervisor mode by fictitiously returning from a
### trap.
## First, set MSTATUS.MPP to Supervisor, so that when we execute mret,
## the privilege level will lower to Supervisor.
li t0, 0b11 << 11
csrc mstatus, t0
li t0, 0b01 << 11
csrs mstatus, t0
## Next, set MEPC to the address of hart0_full_boot, so that when we
## execute mret, execution will continue there.
la t0, hart0_full_boot
csrw mepc, t0
## Set SATP.MODE to Bare, and clear the rest of the bits. This disables
## paging in Supervisor mode.
csrw satp, zero
## Set PMP0CFG.{R, W, X} to 1, PMP0CFG.A to TOR, and PMP0ADDR to the
## maximum value. This allows supervisor mode to access all of physical
## memory.
li t0, (0b01 << 3) | 0b111
csrw pmpcfg0, t0
li t0, -1
csrw pmpaddr0, t0
## Since we adjusted PMP settings, we need to issue an SFENCE.VMA.
sfence.vma
## Jump to supervisor mode.
mret
.size _start, . - _start
.section .text
.type hart0_full_boot, STT_FUNC
hart0_full_boot:
## Set up hart0's stack, leaving room for the EarlyBootAddrs and CPULocals.
la tp, hart0_initial_stack_top - (4 * 8)
addi sp, tp, -(9 * 8)
## Write a canary to the bottom of hart0's stack.
li t0, 0xdead0bad0defaced
la t1, hart0_initial_stack
sd t0, (t1)
## Store the DeviceTree and the appropriate addresses into the
## EarlyBootAddrs.
sd a1, (0 * 8)(sp)
la t0, kernel_start
sd t0, (1 * 8)(sp)
la t0, kernel_end
sd t0, (2 * 8)(sp)
la t0, kernel_rx_end
sd t0, (3 * 8)(sp)
la t0, kernel_ro_end
sd t0, (4 * 8)(sp)
la t0, kernel_rw_end
sd t0, (5 * 8)(sp)
sd t1, (6 * 8)(sp) # This is still hart0_initial_stack from above.
la t0, hart0_initial_stack_top
sd t0, (7 * 8)(sp)
la t0, trampoline_start
sd t0, (8 * 8)(sp)
## Call hart0_early_boot, passing it the DeviceTree we received and
## getting back the address of a stack to switch to.
mv a0, sp
call hart0_early_boot
## Switch to the returned stack.
mv sp, a0
## Load the canary back from hart0's stack.
la t0, hart0_initial_stack
ld a0, (t0)
## Jump to hart0_boot.
j hart0_boot
.size hart0_full_boot, . - hart0_full_boot
.section .text
.type wait_for_hart0, STT_FUNC
wait_for_hart0:
## TODO
wfi
j wait_for_hart0
.size wait_for_hart0, . - wait_for_hart0
.section .trampoline_page
trap_handler:
## TODO
wfi
j trap_handler
|