asm.rs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. #[cfg(all(riscv32, riscvm))]
  32. ".attribute arch, \"rv32im\"",
  33. #[cfg(all(riscv64, riscvm, not(riscvg)))]
  34. ".attribute arch, \"rv64im\"",
  35. #[cfg(all(riscv64, riscvg))]
  36. ".attribute arch, \"rv64g\"",
  37. );
  38. // Entry point of all programs (_start). It initializes DWARF call frame information,
  39. // the stack pointer, the frame pointer (needed for closures to work in start_rust)
  40. // and the global pointer. Then it calls _start_rust.
  41. cfg_global_asm!(
  42. ".section .init, \"ax\"
  43. .global _start
  44. _start:",
  45. #[cfg(riscv32)]
  46. "lui ra, %hi(_abs_start)
  47. jr %lo(_abs_start)(ra)",
  48. #[cfg(riscv64)]
  49. ".option push
  50. .option norelax // to prevent an unsupported R_RISCV_ALIGN relocation from being generated
  51. 1:
  52. auipc ra, %pcrel_hi(1f)
  53. ld ra, %pcrel_lo(1b)(ra)
  54. jr ra
  55. .align 3
  56. 1:
  57. .dword _abs_start
  58. .option pop",
  59. "
  60. _abs_start:
  61. .option norelax
  62. .cfi_startproc
  63. .cfi_undefined ra",
  64. #[cfg(feature = "s-mode")]
  65. "csrw sie, 0
  66. csrw sip, 0",
  67. #[cfg(not(feature = "s-mode"))]
  68. "csrw mie, 0
  69. csrw mip, 0",
  70. "li x1, 0
  71. li x2, 0
  72. li x3, 0
  73. li x4, 0
  74. li x5, 0
  75. li x6, 0
  76. li x7, 0
  77. li x8, 0
  78. li x9, 0
  79. // a0..a2 (x10..x12) skipped
  80. li x13, 0
  81. li x14, 0
  82. li x15, 0
  83. li x16, 0
  84. li x17, 0
  85. li x18, 0
  86. li x19, 0
  87. li x20, 0
  88. li x21, 0
  89. li x22, 0
  90. li x23, 0
  91. li x24, 0
  92. li x25, 0
  93. li x26, 0
  94. li x27, 0
  95. li x28, 0
  96. li x29, 0
  97. li x30, 0
  98. li x31, 0
  99. .option push
  100. .option norelax
  101. la gp, __global_pointer$
  102. .option pop
  103. // Allocate stacks",
  104. #[cfg(all(not(feature = "single-hart"), feature = "s-mode"))]
  105. "mv t2, a0 // the hartid is passed as parameter by SMODE",
  106. #[cfg(all(not(feature = "single-hart"), not(feature = "s-mode")))]
  107. "csrr t2, mhartid",
  108. #[cfg(not(feature = "single-hart"))]
  109. "lui t0, %hi(_max_hart_id)
  110. add t0, t0, %lo(_max_hart_id)
  111. bgtu t2, t0, abort
  112. lui t0, %hi(_hart_stack_size)
  113. add t0, t0, %lo(_hart_stack_size)",
  114. #[cfg(all(not(feature = "single-hart"), riscvm))]
  115. "mul t0, t2, t0",
  116. #[cfg(all(not(feature = "single-hart"), not(riscvm)))]
  117. "beqz t2, 2f // Jump if single-hart
  118. mv t1, t2
  119. mv t3, t0
  120. 1:
  121. add t0, t0, t3
  122. addi t1, t1, -1
  123. bnez t1, 1b
  124. 2: ",
  125. "la t1, _stack_start",
  126. #[cfg(not(feature = "single-hart"))]
  127. "sub t1, t1, t0",
  128. "andi sp, t1, -16 // Force 16-byte alignment
  129. // Set frame pointer
  130. add s0, sp, zero
  131. jal zero, _start_rust
  132. .cfi_endproc",
  133. );
  134. /// Trap entry point (_start_trap). It saves caller saved registers, calls
  135. /// _start_trap_rust, restores caller saved registers and then returns.
  136. ///
  137. /// # Usage
  138. ///
  139. /// The macro takes 5 arguments:
  140. /// - `$STORE`: the instruction used to store a register in the stack (e.g. `sd` for riscv64)
  141. /// - `$LOAD`: the instruction used to load a register from the stack (e.g. `ld` for riscv64)
  142. /// - `$BYTES`: the number of bytes used to store a register (e.g. 8 for riscv64)
  143. /// - `$TRAP_SIZE`: the number of registers to store in the stack (e.g. 32 for all the user registers)
  144. /// - list of tuples of the form `($REG, $LOCATION)`, where:
  145. /// - `$REG`: the register to store/load
  146. /// - `$LOCATION`: the location in the stack where to store/load the register
  147. #[rustfmt::skip]
  148. macro_rules! trap_handler {
  149. ($STORE:ident, $LOAD:ident, $BYTES:literal, $TRAP_SIZE:literal, [$(($REG:ident, $LOCATION:literal)),*]) => {
  150. // ensure we do not break that sp is 16-byte aligned
  151. const _: () = assert!(($TRAP_SIZE * $BYTES) % 16 == 0);
  152. global_asm!(
  153. "
  154. .section .trap, \"ax\"
  155. .global default_start_trap
  156. default_start_trap:",
  157. // save space for trap handler in stack
  158. concat!("addi sp, sp, -", stringify!($TRAP_SIZE * $BYTES)),
  159. // save registers in the desired order
  160. $(concat!(stringify!($STORE), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)*
  161. // call rust trap handler
  162. "add a0, sp, zero
  163. jal ra, _start_trap_rust",
  164. // restore registers in the desired order
  165. $(concat!(stringify!($LOAD), " ", stringify!($REG), ", ", stringify!($LOCATION * $BYTES), "(sp)"),)*
  166. // free stack
  167. concat!("addi sp, sp, ", stringify!($TRAP_SIZE * $BYTES)),
  168. );
  169. cfg_global_asm!(
  170. // return from trap
  171. #[cfg(feature = "s-mode")]
  172. "sret",
  173. #[cfg(not(feature = "s-mode"))]
  174. "mret",
  175. );
  176. };
  177. }
  178. #[rustfmt::skip]
  179. #[cfg(riscv32)]
  180. trap_handler!(
  181. sw, lw, 4, 16,
  182. [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7),
  183. (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)]
  184. );
  185. #[rustfmt::skip]
  186. #[cfg(riscv64)]
  187. trap_handler!(
  188. sd, ld, 8, 16,
  189. [(ra, 0), (t0, 1), (t1, 2), (t2, 3), (t3, 4), (t4, 5), (t5, 6), (t6, 7),
  190. (a0, 8), (a1, 9), (a2, 10), (a3, 11), (a4, 12), (a5, 13), (a6, 14), (a7, 15)]
  191. );
  192. // Make sure there is an abort when linking
  193. global_asm!(
  194. ".section .text.abort
  195. .globl abort
  196. abort:
  197. j abort"
  198. );