xref: /linux/arch/riscv/kernel/head.S (revision b7019ac550eb3916f34d79db583e9b7ea2524afa)
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Copyright (C) 2012 Regents of the University of California
4 */
5
6#include <asm/thread_info.h>
7#include <asm/asm-offsets.h>
8#include <asm/asm.h>
9#include <linux/init.h>
10#include <linux/linkage.h>
11#include <asm/thread_info.h>
12#include <asm/page.h>
13#include <asm/csr.h>
14
15__INIT
16ENTRY(_start)
17	/* Mask all interrupts */
18	csrw CSR_SIE, zero
19	csrw CSR_SIP, zero
20
21	/* Load the global pointer */
22.option push
23.option norelax
24	la gp, __global_pointer$
25.option pop
26
27	/*
28	 * Disable FPU to detect illegal usage of
29	 * floating point in kernel space
30	 */
31	li t0, SR_FS
32	csrc sstatus, t0
33
34	/* Pick one hart to run the main boot sequence */
35	la a3, hart_lottery
36	li a2, 1
37	amoadd.w a3, a2, (a3)
38	bnez a3, .Lsecondary_start
39
40	/* Clear BSS for flat non-ELF images */
41	la a3, __bss_start
42	la a4, __bss_stop
43	ble a4, a3, clear_bss_done
44clear_bss:
45	REG_S zero, (a3)
46	add a3, a3, RISCV_SZPTR
47	blt a3, a4, clear_bss
48clear_bss_done:
49
50	/* Save hart ID and DTB physical address */
51	mv s0, a0
52	mv s1, a1
53	la a2, boot_cpu_hartid
54	REG_S a0, (a2)
55
56	/* Initialize page tables and relocate to virtual addresses */
57	la sp, init_thread_union + THREAD_SIZE
58	call setup_vm
59	call relocate
60
61	/* Restore C environment */
62	la tp, init_task
63	sw zero, TASK_TI_CPU(tp)
64	la sp, init_thread_union + THREAD_SIZE
65
66	/* Start the kernel */
67	mv a0, s1
68	call parse_dtb
69	tail start_kernel
70
71relocate:
72	/* Relocate return address */
73	li a1, PAGE_OFFSET
74	la a0, _start
75	sub a1, a1, a0
76	add ra, ra, a1
77
78	/* Point stvec to virtual address of intruction after satp write */
79	la a0, 1f
80	add a0, a0, a1
81	csrw CSR_STVEC, a0
82
83	/* Compute satp for kernel page tables, but don't load it yet */
84	la a2, swapper_pg_dir
85	srl a2, a2, PAGE_SHIFT
86	li a1, SATP_MODE
87	or a2, a2, a1
88
89	/*
90	 * Load trampoline page directory, which will cause us to trap to
91	 * stvec if VA != PA, or simply fall through if VA == PA.  We need a
92	 * full fence here because setup_vm() just wrote these PTEs and we need
93	 * to ensure the new translations are in use.
94	 */
95	la a0, trampoline_pg_dir
96	srl a0, a0, PAGE_SHIFT
97	or a0, a0, a1
98	sfence.vma
99	csrw CSR_SATP, a0
100.align 2
1011:
102	/* Set trap vector to spin forever to help debug */
103	la a0, .Lsecondary_park
104	csrw CSR_STVEC, a0
105
106	/* Reload the global pointer */
107.option push
108.option norelax
109	la gp, __global_pointer$
110.option pop
111
112	/*
113	 * Switch to kernel page tables.  A full fence is necessary in order to
114	 * avoid using the trampoline translations, which are only correct for
115	 * the first superpage.  Fetching the fence is guarnteed to work
116	 * because that first superpage is translated the same way.
117	 */
118	csrw CSR_SATP, a2
119	sfence.vma
120
121	ret
122
123.Lsecondary_start:
124#ifdef CONFIG_SMP
125	li a1, CONFIG_NR_CPUS
126	bgeu a0, a1, .Lsecondary_park
127
128	/* Set trap vector to spin forever to help debug */
129	la a3, .Lsecondary_park
130	csrw CSR_STVEC, a3
131
132	slli a3, a0, LGREG
133	la a1, __cpu_up_stack_pointer
134	la a2, __cpu_up_task_pointer
135	add a1, a3, a1
136	add a2, a3, a2
137
138	/*
139	 * This hart didn't win the lottery, so we wait for the winning hart to
140	 * get far enough along the boot process that it should continue.
141	 */
142.Lwait_for_cpu_up:
143	/* FIXME: We should WFI to save some energy here. */
144	REG_L sp, (a1)
145	REG_L tp, (a2)
146	beqz sp, .Lwait_for_cpu_up
147	beqz tp, .Lwait_for_cpu_up
148	fence
149
150	/* Enable virtual memory and relocate to virtual address */
151	call relocate
152
153	tail smp_callin
154#endif
155
156.align 2
157.Lsecondary_park:
158	/* We lack SMP support or have too many harts, so park this hart */
159	wfi
160	j .Lsecondary_park
161END(_start)
162
163__PAGE_ALIGNED_BSS
164	/* Empty zero page */
165	.balign PAGE_SIZE
166