xref: /DragonOS/kernel/src/arch/riscv64/asm/head.S (revision f2022a8a1cc4a8e2a85e9061e036e9c491a2fa00)
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 0x8000000000000000ULL
21#define SATP_MODE_48 0x9000000000000000ULL
22#define SATP_MODE_57 0xa000000000000000ULL
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	call __initial_reloacate_enable_mmu
88
89.option push
90.option norelax
91
92	la a0, BSP_IDLE_STACK_SPACE
93	mv sp, a0
94	li t0, 32752	// 预留16字节防止越界
95	add sp, sp, t0
96.option pop
97	/*
98	 * Disable FPU & VECTOR to detect illegal usage of
99	 * floating point or vector in kernel space
100	 */
101	li t0, SR_FS_VS
102	csrc CSR_STATUS, t0
103
104	/* Call the kernel */
105	la a0, __initial_hartid_ptr
106	ld a0, 0(a0)
107	la a1, __initial_fdt_ptr
108	ld a1, 0(a1)
109
110
111	// 跳转到kernel_main
112	call kernel_main
113	nop
114	wfi
115
116
117__initial_reloacate_enable_mmu:
118	// 计算起始物理地址与内核高虚拟地址的偏移量
119	la t0, __initial_start_load_paddr
120	ld t0, 0(t0)
121
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, 1f
132	add a2, a2, t1
133	csrw CSR_TVEC, a2
134	// enable MMU
135	la a2, __initial_pgtable
136	srli a2, a2, 12
137	la a0, __initial_satp_mode
138	ld a0, 0(a0)
139	or a2, a2, a0
140
141
142	sfence.vma
143	csrw satp, a2
144
1451:
146	la a0, __initial_Lsecondary_park
147	add a0, a0, t1
148	csrw CSR_TVEC, a0
149
150	csrw satp, a2
151	sfence.vma
152
153	ret
154
155// 映射物理地址到虚拟地址(2M页,1G大小)
156// 参数:
157//   a0: 物理地址
158//   a1: 虚拟地址
159initial_map_256M_phys_addr:
160	// 检查物理地址是否对齐到2M
161	li t0, 0x1fffff
162	and t0, t0, a0
163	bnez t0, __initial_map_1g_phys_failed
164
165	// 检查虚拟地址是否对齐到2M
166	li t0, 0x1fffff
167	and t0, t0, a1
168	bnez t0, __initial_map_1g_phys_failed
169
170	// 把起始虚拟地址存储到t2中
171	mv t2, a1
172	// 按照2M对齐
173	li t1, -0x200000
174	and t2, t2, t1
175
176	// 计算L0页表项的索引
177	srli t2, t2, 30
178	andi t2, t2, 511
179
180
181	// 填写第一个L0页表项
182	la t4, __initial_pgtable
183	slli t5, t2, 3 // t5 = t2 * 8
184	add t4, t4, t5 // t4 = t4 + t5, t4指向L0页表项
185
186	// 提取L1页表的地址
187	la t5, __initial_l1_pgtable
188	srli t5, t5, 12
189	slli t5, t5, 10
190	ori t5, t5, 0x1 // 设置L1页表项属性,V = 1
191	// 设置L0页表项的值
192	sd t5, 0(t4)
193
194	// 计算是否需要填写第二个L1页表项(判断是否超过第一个L1页表的范围)
195	addi t3, t2, 128
196	li t5, 512
197	blt t3, t5, __initial_set_l1_pgtable
198	// 填写第二个L1页表
199	la t3, __initial_l1_pgtable
200	li t5, 4096
201	add t3, t3, t5
202	srli t3, t3, 12
203	slli t3, t3, 10
204	ori t3, t3, 0x1 // 设置L1页表项属性,V = 1
205	// 设置L0页表项的值
206	sd t3, 8(t4)
207
208__initial_set_l1_pgtable:	// 开始填写L1页表
209
210	// 获取起始物理地址
211	mv t6, a0
212	// 获取L1页表的地址
213	la t0, __initial_l1_pgtable
214
215	// 计算起始L1页表项的索引
216	mv t3, a1
217	srli t3, t3, 21
218	andi t3, t3, 511
219
220	slli t3, t3, 3 // t3 = t3 * 8
221	add t0, t0, t3 // t0 = t0 + t3
222
223	// 加载计数器
224	li t5, 0
225__initial_set_l1_pgtable_loop:
226
227	mv t3, t6
228	srli t3, t3, 12 // t3 = t6 >> 12 (page frame number)
229	li t1, 0x3FFFFFFFFFFFFF
230	and t3, t3, t1 // t3 = t3 & 0x3FFFFFFFFFFFFF
231	slli t3, t3, 10 // t3 = t3 << 10
232	ori t3, t3, 0xEF // 设置L1页表项属性,set R/W/X/V/A/D/G = 1
233	// 设置L1页表项的值
234	sd t3, 0(t0)
235
236	// 增加 页表项指针
237	addi t0, t0, 8
238	// 增加 t6 的值(2M)
239	li t2, 0x200000
240	add t6, t6, t2
241
242	// 增加计数器
243	addi t5, t5, 1
244	// 判断计数器是否超过128
245	li t2, 128
246	blt t5, t2, __initial_set_l1_pgtable_loop
247
248
249	// 填写完成
250	ret
251
252
253
254
255__initial_map_1g_phys_failed:
256	// 地址没有对齐到2M
257	wfi
258	la a0, __initial_map_1g_phys_failed
259	// 跳转
260	jr a0
261
262
263
264// 映射物理地址到虚拟地址(恒等映射)
265// 参数:
266//   a0: 物理地址
267initial_map_1g_identical:
268	mv a1, a0
269	// 把_start向下对齐到1GB
270	li t0, -0x40000000
271	// 计算起始物理地址,存放在t0中
272	and t0, t0, a0
273
274
275	// 把起始虚拟地址存储到t2中
276	mv t2, a1
277	// 按照1g对齐
278	li t1, -0x40000000
279	and t2, t2, t1
280
281
282	// 右移30位,得到L0页表项的索引
283	srli t2, t2, 30
284	// 与511进行与运算,得到L0页表项的索引
285	andi t2, t2, 511
286
287
288	// 填写页表项
289	la t4, __initial_pgtable
290	slli t3, t2, 3 // t3 = t2 * 8
291	add t4, t4, t3 // t4 = t4 + t3
292
293
294	mv t3, t0
295	srli t3, t3, 12 // t3 = t0 >> 12 (page frame number)
296	slli t3, t3, 10 // t3 = t3 << 10
297	ori t3, t3, 0xEF // set R/W/X/V/A/D/G = 1
298
299	// 计算delta的pfn
300	li t2, 0x40000000
301	srli t2, t2, 12
302	// 把delta pfn移位到页表项的第10位的位置
303	slli t2, t2, 10
304	li t1, 2
305
306__loop_set_8g:
307
308
309	sd t3, 0(t4)
310
311	// 增加 t4 的值
312	addi t4, t4, 8
313	// 增加1G的pfn
314	add t3, t3, t2
315
316
317	addi t1, t1, -1
318	bnez t1, __loop_set_8g
319
320	ret
321
322
323// 用于清空页表的空间
324// 参数:
325//   a0: page table address
326__initial_clear_pgtable:
327	mv t0, a0
328	li t1, 512
329	li t2, 0 // 用于存储 0
330
331__initial_clear_pgtable_loop:
332
333	sd t2, 0(t0) // 将 0 存储到当前word
334	addi t0, t0, 8 // 增加 t0 的值
335	addi t1, t1, -1 // 减少剩余的word数
336	bnez t1, __initial_clear_pgtable_loop
337
338	ret
339
340.align 2
341__initial_Lsecondary_park:
342	/* We lack SMP support or have too many harts, so park this hart */
343	wfi
344	j __initial_Lsecondary_park
345
346// 全局变量,存储平坦设备树的地址和hartid
347.global __initial_fdt_ptr
348__initial_fdt_ptr:
349	.quad 0
350
351.global __initial_hartid_ptr
352__initial_hartid_ptr:
353	.quad 0
354
355// _start标签在启动时被加载到的物理地址
356.global __initial_start_load_paddr
357__initial_start_load_paddr:
358	.quad 0
359
360__initial_kernel_main_vaddr:
361	.quad 0
362
363
364
365.global __initial_satp_mode
366__initial_satp_mode:
367	.quad SATP_MODE_39
368
369// 初始页表的空间(sv39模式的L0页表)
370.section .initial_pgtable_section
371.global __initial_pgtable
372__initial_pgtable:
373	.skip 4096
374
375.global __initial_l1_pgtable
376__initial_l1_pgtable:
377	.skip 8192
378
379