xref: /DragonOS/kernel/src/arch/riscv64/asm/head.S (revision 91e9d4ab55ef960f57a1b6287bc523ca4341f67a)
1#include "common/asm.h"
2
3.section .bootstrap
4
5#define CSR_SSTATUS		0x100
6#define CSR_SIE			0x104
7#define CSR_STVEC		0x105
8#define CSR_SIP			0x144
9
10# define CSR_TVEC	CSR_STVEC
11
12# define CSR_STATUS	CSR_SSTATUS
13#define CSR_IE CSR_SIE
14#define CSR_IP CSR_SIP
15
16#define SR_FS 0x00006000
17#define SR_VS 0x00000600
18#define SR_FS_VS	(SR_FS | SR_VS) /* Vector and Floating-Point Unit */
19
20#define SATP_MODE_39 0x8000000000000000UL
21#define SATP_MODE_48 0x9000000000000000UL
22#define SATP_MODE_57 0xa000000000000000UL
23
24#define PAGE_OFFSET 0xffffffc000000000
25#define KERNEL_LINK_OFFSET 0x1000000
26#define KERNEL_VIRT_START (PAGE_OFFSET + KERNEL_LINK_OFFSET)
27
28
29// 内核入口(从DragonStub跳转到这里)
30// 参数:
31//   a0: hartid (核心ID)
32//   a1: fdt (平坦设备树)
33.global _start
34.type _start, @function
35ENTRY(_start)
36	/* Mask all interrupts */
37	csrw CSR_IE, zero
38	csrw CSR_IP, zero
39
40
41	// 暂存hartid
42	la t0, __initial_hartid_ptr
43	sd a0, 0(t0)
44	// 暂存平坦设备树地址
45	la t0, __initial_fdt_ptr
46	sd a1, 0(t0)
47
48
49	// 暂存_start标签被DragonStub加载到的物理地址
50	auipc t0, 0
51	li t1, -4095
52	and t0, t0, t1
53	la t1, __initial_start_load_paddr
54	sd t0, 0(t1)
55
56	// 清空页表的空间
57	la a0, __initial_pgtable
58	call __initial_clear_pgtable
59	la a0, __initial_l1_pgtable
60	call __initial_clear_pgtable
61	la a0, __initial_l1_pgtable
62	li a1, 4096
63	add a0, a0, a1
64	call __initial_clear_pgtable
65
66
67
68	// 设置页表,把内核当前所在的物理地址映射到链接时的内核虚拟空间
69	la a0, __initial_start_load_paddr
70	ld a0, 0(a0)
71
72	// 偏移量0xffffffc000000000,计算起始的L0页表项
73	// 因为内核链接地址还有16M的空间,所以这里加上0x1000000
74	li a1, KERNEL_VIRT_START
75
76	// 映射物理地址到虚拟地址
77	call initial_map_256M_phys_addr
78
79	// 增加恒等映射
80	la a0, __initial_start_load_paddr
81	ld a0, 0(a0)
82
83	mv a1, a0
84	call initial_map_1g_identical
85
86__init_set_pgtable_loop_end:
87
88	call __initial_reloacate_enable_mmu
89
90.option push
91.option norelax
92
93	la a0, BSP_IDLE_STACK_SPACE
94	mv sp, a0
95	li t0, 32768
96	add sp, sp, t0
97.option pop
98	/*
99	 * Disable FPU & VECTOR to detect illegal usage of
100	 * floating point or vector in kernel space
101	 */
102	li t0, SR_FS_VS
103	csrc CSR_STATUS, t0
104
105	/* Call the kernel */
106	la a0, __initial_hartid_ptr
107	ld a0, 0(a0)
108	la a1, __initial_fdt_ptr
109	ld a1, 0(a1)
110
111
112	// 跳转到kernel_main
113	call kernel_main
114	nop
115	wfi
116
117
118__initial_reloacate_enable_mmu:
119	// 计算起始物理地址与内核高虚拟地址的偏移量
120	la t0, __initial_start_load_paddr
121	ld t0, 0(t0)
122
123	li t1, KERNEL_VIRT_START
124	sub t1, t1, t0
125
126	// 重定位返回地址
127	add ra, ra, t1
128
129	/* Point stvec to virtual address of intruction after satp write */
130	/* Set trap vector to spin forever to help debug */
131	la a2, __initial_Lsecondary_park
132	add a2, a2, t1
133	csrw CSR_TVEC, a2
134
135	// enable MMU
136	la a2, __initial_pgtable
137	srli a2, a2, 12
138	la a0, __initial_satp_mode
139	ld a0, 0(a0)
140	or a2, a2, a0
141	sfence.vma
142	csrw satp, a2
143
144	ret
145
146// 映射物理地址到虚拟地址(2M页,1G大小)
147// 参数:
148//   a0: 物理地址
149//   a1: 虚拟地址
150initial_map_256M_phys_addr:
151	// 检查物理地址是否对齐到2M
152	li t0, 0x1fffff
153	and t0, t0, a0
154	bnez t0, __initial_map_1g_phys_failed
155
156	// 检查虚拟地址是否对齐到2M
157	li t0, 0x1fffff
158	and t0, t0, a1
159	bnez t0, __initial_map_1g_phys_failed
160
161	// 把起始虚拟地址存储到t2中
162	mv t2, a1
163	// 按照2M对齐
164	li t1, -0x200000
165	and t2, t2, t1
166
167	// 计算L0页表项的索引
168	srl t2, t2, 30
169	andi t2, t2, 511
170
171
172	// 填写第一个L0页表项
173	la t4, __initial_pgtable
174	slli t5, t2, 3 // t5 = t2 * 8
175	add t4, t4, t5 // t4 = t4 + t5, t4指向L0页表项
176
177	// 提取L1页表的地址
178	la t5, __initial_l1_pgtable
179	srli t5, t5, 12
180	slli t5, t5, 10
181	ori t5, t5, 0x1 // 设置L1页表项属性,V = 1
182	// 设置L0页表项的值
183	sd t5, 0(t4)
184
185	// 计算是否需要填写第二个L1页表项(判断是否超过第一个L1页表的范围)
186	addi t3, t2, 128
187	li t5, 512
188	blt t3, t5, __initial_set_l1_pgtable
189	// 填写第二个L1页表
190	la t3, __initial_l1_pgtable
191	li t5, 4096
192	add t3, t3, t5
193	srli t3, t3, 12
194	slli t3, t3, 10
195	ori t3, t3, 0x1 // 设置L1页表项属性,V = 1
196	// 设置L0页表项的值
197	sd t3, 8(t4)
198
199__initial_set_l1_pgtable:	// 开始填写L1页表
200
201	// 获取起始物理地址
202	mv t6, a0
203	// 获取L1页表的地址
204	la t0, __initial_l1_pgtable
205
206	// 计算起始L1页表项的索引
207	mv t3, a1
208	srli t3, t3, 21
209	andi t3, t3, 511
210
211	slli t3, t3, 3 // t3 = t3 * 8
212	add t0, t0, t3 // t0 = t0 + t3
213
214	// 加载计数器
215	li t5, 0
216__initial_set_l1_pgtable_loop:
217
218	mv t3, t6
219	srli t3, t3, 12 // t3 = t6 >> 12 (page frame number)
220	li t1, 0x3FFFFFFFFFFFFF
221	and t3, t3, t1 // t3 = t3 & 0x3FFFFFFFFFFFFF
222	slli t3, t3, 10 // t3 = t3 << 10
223	ori t3, t3, 0xf // 设置L1页表项属性,R/W/X/V = 1
224	// 设置L1页表项的值
225	sd t3, 0(t0)
226
227	// 增加 页表项指针
228	addi t0, t0, 8
229	// 增加 t6 的值(2M)
230	li t2, 0x200000
231	add t6, t6, t2
232
233	// 增加计数器
234	addi t5, t5, 1
235	// 判断计数器是否超过128
236	li t2, 128
237	blt t5, t2, __initial_set_l1_pgtable_loop
238
239
240	// 填写完成
241	ret
242
243
244
245
246__initial_map_1g_phys_failed:
247	// 地址没有对齐到2M
248	wfi
249	la a0, __initial_map_1g_phys_failed
250	// 跳转
251	jr a0
252
253
254
255// 映射物理地址到虚拟地址(恒等映射)
256// 参数:
257//   a0: 物理地址
258initial_map_1g_identical:
259	mv a1, a0
260	// 把_start向下对齐到1GB
261	li t0, -0x40000000
262	// 计算起始物理地址,存放在t0中
263	and t0, t0, a0
264
265
266	// 把起始虚拟地址存储到t2中
267	mv t2, a1
268	// 按照1g对齐
269	li t1, -0x40000000
270	and t2, t2, t1
271
272	// 右移30位,得到L0页表项的索引
273	srl t2, t2, 30
274	// 与511进行与运算,得到L0页表项的索引
275	andi t2, t2, 511
276
277
278	// 填写页表项
279	// li t2, 0xf // 页表项属性, R/W/X/V = 1
280	la t4, __initial_pgtable
281	slli t3, t2, 3 // t3 = t2 * 8
282	add t4, t4, t3 // t4 = t4 + t3
283
284	mv t3, t0
285	srli t3, t3, 12 // t3 = t0 >> 12 (page frame number)
286	slli t3, t3, 10 // t3 = t3 << 10
287	ori t3, t3, 0xf // set R/W/X/V = 1
288	// 设置t0地址在L0页表中的值
289	sd t3, 0(t4)
290
291	// 增加 t4 的值
292	addi t4, t4, 8
293	// 增加 t3 的值(1G)
294	li t2, 0x40000000
295	add t3, t3, t2
296	sd t3, 0(t4)
297
298	ret
299
300// 用于清空页表的空间
301// 参数:
302//   a0: page table address
303__initial_clear_pgtable:
304	mv t0, a0
305	li t1, 512
306	li t2, 0 // 用于存储 0
307
308__initial_clear_pgtable_loop:
309
310	sd t2, 0(t0) // 将 0 存储到当前word
311	addi t0, t0, 8 // 增加 t0 的值
312	addi t1, t1, -1 // 减少剩余的word数
313	bnez t1, __initial_clear_pgtable_loop
314
315	ret
316
317.align 2
318__initial_Lsecondary_park:
319	/* We lack SMP support or have too many harts, so park this hart */
320	wfi
321	j __initial_Lsecondary_park
322
323// 全局变量,存储平坦设备树的地址和hartid
324.global __initial_fdt_ptr
325__initial_fdt_ptr:
326	.quad 0
327
328.global __initial_hartid_ptr
329__initial_hartid_ptr:
330	.quad 0
331
332// _start标签在启动时被加载到的物理地址
333__initial_start_load_paddr:
334	.quad 0
335
336__initial_kernel_main_vaddr:
337	.quad 0
338
339
340
341.global __initial_satp_mode
342__initial_satp_mode:
343	.quad SATP_MODE_39
344
345// 初始页表的空间(sv39模式的L0页表)
346.section .initial_pgtable_section
347__initial_pgtable:
348	.skip 4096
349
350__initial_l1_pgtable:
351	.skip 8192
352
353