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