asm.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. use core::arch::global_asm;
  2. /// Parse cfg attributes inside a global_asm call.
  3. macro_rules! cfg_global_asm {
  4. {@inner, [$($x:tt)*], } => {
  5. global_asm!{$($x)*}
  6. };
  7. (@inner, [$($x:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => {
  8. #[cfg($meta)]
  9. cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
  10. #[cfg(not($meta))]
  11. cfg_global_asm!{@inner, [$($x)*], $($rest)*}
  12. };
  13. {@inner, [$($x:tt)*], $asm:literal, $($rest:tt)*} => {
  14. cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
  15. };
  16. {$($asms:tt)*} => {
  17. cfg_global_asm!{@inner, [], $($asms)*}
  18. };
  19. }
  20. // Provisional patch to avoid LLVM spurious errors when compiling in release mode.
  21. // This patch is somewhat hardcoded and relies on the fact that the rustc compiler
  22. // only supports a limited combination of ISA extensions. This patch should be
  23. // removed when LLVM fixes the issue. Alternatively, it must be updated when rustc
  24. // supports more ISA extension combinations.
  25. //
  26. // Related issues:
  27. // - https://github.com/rust-embedded/riscv/issues/175
  28. // - https://github.com/rust-lang/rust/issues/80608
  29. // - https://github.com/llvm/llvm-project/issues/61991
  30. cfg_global_asm!(
  31. "// Provisional patch to avoid LLVM spurious errors when compiling in release mode.",
  32. #[cfg(all(riscv32, riscvm))]
  33. ".attribute arch, \"rv32im\"",
  34. #[cfg(all(riscv64, riscvm, not(riscvg)))]
  35. ".attribute arch, \"rv64im\"",
  36. #[cfg(all(riscv64, riscvg))]
  37. ".attribute arch, \"rv64g\"",
  38. );
  39. // Entry point of all programs (_start). It initializes DWARF call frame information,
  40. // the stack pointer, the frame pointer (needed for closures to work in start_rust)
  41. // and the global pointer. Then it calls _start_rust.
  42. cfg_global_asm!(
  43. ".section .init, \"ax\"
  44. .global _start
  45. _start:",
  46. #[cfg(riscv32)]
  47. "lui ra, %hi(_abs_start)
  48. jr %lo(_abs_start)(ra)",
  49. #[cfg(riscv64)]
  50. ".option push
  51. .option norelax // to prevent an unsupported R_RISCV_ALIGN relocation from being generated
  52. 1:
  53. auipc ra, %pcrel_hi(1f)
  54. ld ra, %pcrel_lo(1b)(ra)
  55. jr ra
  56. .align 3
  57. 1:
  58. .dword _abs_start
  59. .option pop",
  60. "
  61. _abs_start:
  62. .option norelax
  63. .cfi_startproc
  64. .cfi_undefined ra",
  65. // Disable interrupts
  66. #[cfg(feature = "s-mode")]
  67. "csrw sie, 0
  68. csrw sip, 0",
  69. #[cfg(not(feature = "s-mode"))]
  70. "csrw mie, 0
  71. csrw mip, 0",
  72. // Set pre-init trap vector
  73. "la t0, _pre_init_trap",
  74. #[cfg(feature = "s-mode")]
  75. "csrw stvec, t0",
  76. #[cfg(not(feature = "s-mode"))]
  77. "csrw mtvec, t0",
  78. );
  79. // ZERO OUT GENERAL-PURPOSE REGISTERS
  80. riscv_rt_macros::loop_global_asm!(" li x{}, 0", 1, 10);
  81. // a0..a2 (x10..x12) skipped
  82. riscv_rt_macros::loop_global_asm!(" li x{}, 0", 13, 32);
  83. // INITIALIZE GLOBAL POINTER, STACK POINTER, AND FRAME POINTER
  84. cfg_global_asm!(
  85. ".option push
  86. .option norelax
  87. la gp, __global_pointer$
  88. .option pop",
  89. );
  90. #[cfg(not(feature = "single-hart"))]
  91. cfg_global_asm!(
  92. #[cfg(feature = "s-mode")]
  93. "mv t2, a0 // the hartid is passed as parameter by SMODE",
  94. #[cfg(not(feature = "s-mode"))]
  95. "csrr t2, mhartid",
  96. "lui t0, %hi(_max_hart_id)
  97. add t0, t0, %lo(_max_hart_id)
  98. bgtu t2, t0, abort
  99. lui t0, %hi(_hart_stack_size)
  100. add t0, t0, %lo(_hart_stack_size)",
  101. #[cfg(riscvm)]
  102. "mul t0, t2, t0",
  103. #[cfg(not(riscvm))]
  104. "beqz t2, 2f // Jump if single-hart
  105. mv t1, t2
  106. mv t3, t0
  107. 1:
  108. add t0, t0, t3
  109. addi t1, t1, -1
  110. bnez t1, 1b
  111. 2: ",
  112. );
  113. cfg_global_asm!(
  114. "la t1, _stack_start",
  115. #[cfg(not(feature = "single-hart"))]
  116. "sub t1, t1, t0",
  117. "andi sp, t1, -16 // align stack to 16-bytes
  118. add s0, sp, zero",
  119. );
  120. // STORE A0..A2 IN THE STACK, AS THEY WILL BE NEEDED LATER BY main
  121. cfg_global_asm!(
  122. #[cfg(riscv32)]
  123. "addi sp, sp, -4 * 3
  124. sw a0, 4 * 0(sp)
  125. sw a1, 4 * 1(sp)
  126. sw a2, 4 * 2(sp)",
  127. #[cfg(riscv64)]
  128. "addi sp, sp, -8 * 3
  129. sd a0, 8 * 0(sp)
  130. sd a1, 8 * 1(sp)
  131. sd a2, 8 * 2(sp)",
  132. );
  133. // SKIP RAM INITIALIZATION IF CURRENT HART IS NOT THE BOOT HART
  134. #[cfg(not(feature = "single-hart"))]
  135. cfg_global_asm!(
  136. #[cfg(not(feature = "s-mode"))]
  137. "csrr a0, mhartid",
  138. "call _mp_hook
  139. mv t0, a0
  140. beqz a0, 4f",
  141. );
  142. // IF CURRENT HART IS THE BOOT HART CALL __pre_init AND INITIALIZE RAM
  143. cfg_global_asm!(
  144. "call __pre_init
  145. // Copy .data from flash to RAM
  146. la t0, _sdata
  147. la t2, _edata
  148. la t1, _sidata
  149. bgeu t0, t2, 2f
  150. 1: ",
  151. #[cfg(target_arch = "riscv32")]
  152. "lw t3, 0(t1)
  153. addi t1, t1, 4
  154. sw t3, 0(t0)
  155. addi t0, t0, 4
  156. bltu t0, t2, 1b",
  157. #[cfg(target_arch = "riscv64")]
  158. "ld t3, 0(t1)
  159. addi t1, t1, 8
  160. sd t3, 0(t0)
  161. addi t0, t0, 8
  162. bltu t0, t2, 1b",
  163. "
  164. 2: // Zero out .bss
  165. la t0, _sbss
  166. la t2, _ebss
  167. bgeu t0, t2, 4f
  168. 3: ",
  169. #[cfg(target_arch = "riscv32")]
  170. "sw zero, 0(t0)
  171. addi t0, t0, 4
  172. bltu t0, t2, 3b",
  173. #[cfg(target_arch = "riscv64")]
  174. "sd zero, 0(t0)
  175. addi t0, t0, 8
  176. bltu t0, t2, 3b",
  177. "
  178. 4: // RAM initilized",
  179. );
  180. // INITIALIZE FLOATING POINT UNIT
  181. #[cfg(any(riscvf, riscvd))]
  182. cfg_global_asm!(
  183. "
  184. li t0, 0x4000 // bit 14 is FS most significant bit
  185. li t2, 0x2000 // bit 13 is FS least significant bit
  186. ",
  187. #[cfg(feature = "s-mode")]
  188. "csrrc x0, sstatus, t0
  189. csrrs x0, sstatus, t2",
  190. #[cfg(not(feature = "s-mode"))]
  191. "csrrc x0, mstatus, t0
  192. csrrs x0, mstatus, t2",
  193. "fscsr x0",
  194. );
  195. // ZERO OUT FLOATING POINT REGISTERS
  196. #[cfg(all(riscv32, riscvd))]
  197. riscv_rt_macros::loop_global_asm!(" fcvt.d.w f{}, x0", 32);
  198. #[cfg(all(riscv64, riscvd))]
  199. riscv_rt_macros::loop_global_asm!(" fmv.d.x f{}, x0", 32);
  200. #[cfg(all(riscvf, not(riscvd)))]
  201. riscv_rt_macros::loop_global_asm!(" fmv.w.x f{}, x0", 32);
  202. // SET UP INTERRUPTS, RESTORE a0..a2, AND JUMP TO MAIN RUST FUNCTION
  203. cfg_global_asm!(
  204. "call _setup_interrupts",
  205. #[cfg(riscv32)]
  206. "lw a0, 4 * 0(sp)
  207. lw a1, 4 * 1(sp)
  208. lw a2, 4 * 2(sp)
  209. addi sp, sp, 4 * 3",
  210. #[cfg(riscv64)]
  211. "ld a0, 8 * 0(sp)
  212. ld a1, 8 * 1(sp)
  213. ld a2, 8 * 2(sp)
  214. addi sp, sp, 8 * 3",
  215. "jal zero, main
  216. .cfi_endproc",
  217. );
  218. cfg_global_asm!(
  219. // Default implementation of `__pre_init` does nothing.
  220. // Users can override this function with the [`#[pre_init]`] macro.
  221. ".weak __pre_init
  222. __pre_init:
  223. ret",
  224. #[cfg(not(feature = "single-hart"))]
  225. // Default implementation of `_mp_hook` wakes hart 0 and busy-loops all the other harts.
  226. // Users can override this function by defining their own `_mp_hook`.
  227. // This function is only used when the `single-hart` feature is not enabled.
  228. ".weak _mp_hook
  229. _mp_hook:
  230. beqz a0, 2f // if hartid is 0, return true
  231. 1: wfi // Otherwise, wait for interrupt in a loop
  232. j 1b
  233. 2: li a0, 1
  234. ret",
  235. // Default implementation of `_setup_interrupts` sets the trap vector to `_start_trap`.
  236. // Trap mode is set to `Direct` by default.
  237. // Users can override this function by defining their own `_setup_interrupts`
  238. ".weak _setup_interrupts
  239. _setup_interrupts:
  240. la t0, _start_trap", // _start_trap is 16-byte aligned, so it corresponds to the Direct trap mode
  241. #[cfg(feature = "s-mode")]
  242. "csrw stvec, t0",
  243. #[cfg(not(feature = "s-mode"))]
  244. "csrw mtvec, t0",
  245. "ret",
  246. // Default implementation of `ExceptionHandler` is an infinite loop.
  247. // Users can override this function by defining their own `ExceptionHandler`
  248. ".weak ExceptionHandler
  249. ExceptionHandler:
  250. j ExceptionHandler",
  251. // Default implementation of `DefaultHandler` is an infinite loop.
  252. // Users can override this function by defining their own `DefaultHandler`
  253. ".weak DefaultHandler
  254. DefaultHandler:
  255. j DefaultHandler",
  256. // Default implementation of `_pre_init_trap` is an infinite loop.
  257. // Users can override this function by defining their own `_pre_init_trap`
  258. // If the execution reaches this point, it means that there is a bug in the boot code.
  259. ".section .init.trap, \"ax\"
  260. .weak _pre_init_trap
  261. _pre_init_trap:
  262. j _pre_init_trap",
  263. );
  264. /// Trap entry point (_start_trap). It saves caller saved registers, calls
  265. /// _start_trap_rust, restores caller saved registers and then returns.
  266. ///
  267. /// # Usage
  268. ///
  269. /// The macro takes 5 arguments:
  270. /// - `$STORE`: the instruction used to store a register in the stack (e.g. `sd` for riscv64)
  271. /// - `$LOAD`: the instruction used to load a register from the stack (e.g. `ld` for riscv64)
  272. /// - `$BYTES`: the number of bytes used to store a register (e.g. 8 for riscv64)
  273. /// - `$TRAP_SIZE`: the number of registers to store in the stack (e.g. 32 for all the user registers)
  274. /// - list of tuples of the form `($REG, $LOCATION)`, where:
  275. /// - `$REG`: the register to store/load
  276. /// - `$LOCATION`: the location in the stack where to store/load the register
  277. #[rustfmt::skip]
  278. macro_rules! trap_handler {
  279. ($STORE:ident, $LOAD:ident, $BYTES:literal, $TRAP_SIZE:literal, [$(($REG:ident, $LOCATION:literal)),*]) => {
  280. // ensure we do not break that sp is 16-byte aligned
  281. const _: () = assert!(($TRAP_SIZE * $BYTES) % 16 == 0);
  282. global_asm!(
  283. "
  284. .section .trap, \"ax\"
  285. .weak _start_trap
  286. _start_trap:",
  287. // save space for trap handler in stack
  288. concat!("addi sp, sp, -", stringify!($TRAP_SIZE * $BYTES)),
  289. // save registers in the desired order
  290. $(concat!(stringify!($STORE), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)*
  291. // call rust trap handler
  292. "add a0, sp, zero
  293. jal ra, _start_trap_rust",
  294. // restore registers in the desired order
  295. $(concat!(stringify!($LOAD), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)*
  296. // free stack
  297. concat!("addi sp, sp, ", stringify!($TRAP_SIZE * $BYTES)),
  298. );
  299. cfg_global_asm!(
  300. // return from trap
  301. #[cfg(feature = "s-mode")]
  302. "sret",
  303. #[cfg(not(feature = "s-mode"))]
  304. "mret",
  305. );
  306. };
  307. }
  308. #[rustfmt::skip]
  309. #[cfg(riscv32)]
  310. trap_handler!(
  311. sw, lw, 4, 16,
  312. [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7),
  313. (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)]
  314. );
  315. #[rustfmt::skip]
  316. #[cfg(riscv64)]
  317. trap_handler!(
  318. sd, ld, 8, 16,
  319. [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7),
  320. (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)]
  321. );
  322. #[rustfmt::skip]
  323. global_asm!(
  324. ".section .text.abort
  325. .weak abort
  326. abort: // make sure there is an abort symbol when linking
  327. j abort"
  328. );