trap.rs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. mod trap_context;
  2. use super::context::TaskContext;
  3. use core::arch::{asm, global_asm, naked_asm};
  4. use eonix_hal_traits::{
  5. context::RawTaskContext,
  6. trap::{IrqState as IrqStateTrait, TrapReturn},
  7. };
  8. pub use trap_context::TrapContext;
  9. unsafe extern "C" {
  10. fn _default_trap_handler(trap_context: &mut TrapContext);
  11. pub fn trap_stubs_start();
  12. fn _raw_trap_return();
  13. }
  14. #[eonix_percpu::define_percpu]
  15. static TRAP_HANDLER: unsafe extern "C" fn() = default_trap_handler;
  16. #[eonix_percpu::define_percpu]
  17. static CAPTURER_CONTEXT: TaskContext = TaskContext::new();
  18. /// This value will never be used.
  19. static mut DIRTY_TRAP_CONTEXT: TaskContext = TaskContext::new();
  20. /// State of the interrupt flag.
  21. pub struct IrqState(u64);
  22. global_asm!(
  23. r"
  24. .set RAX, 0x00
  25. .set RBX, 0x08
  26. .set RCX, 0x10
  27. .set RDX, 0x18
  28. .set RDI, 0x20
  29. .set RSI, 0x28
  30. .set R8, 0x30
  31. .set R9, 0x38
  32. .set R10, 0x40
  33. .set R11, 0x48
  34. .set R12, 0x50
  35. .set R13, 0x58
  36. .set R14, 0x60
  37. .set R15, 0x68
  38. .set RBP, 0x70
  39. .set INT_NO, 0x78
  40. .set ERRCODE, 0x80
  41. .set RIP, 0x88
  42. .set CS, 0x90
  43. .set FLAGS, 0x98
  44. .set RSP, 0xa0
  45. .set SS, 0xa8
  46. .macro cfi_all_same_value
  47. .cfi_same_value %rax
  48. .cfi_same_value %rbx
  49. .cfi_same_value %rcx
  50. .cfi_same_value %rdx
  51. .cfi_same_value %rdi
  52. .cfi_same_value %rsi
  53. .cfi_same_value %r8
  54. .cfi_same_value %r9
  55. .cfi_same_value %r10
  56. .cfi_same_value %r11
  57. .cfi_same_value %r12
  58. .cfi_same_value %r13
  59. .cfi_same_value %r14
  60. .cfi_same_value %r15
  61. .cfi_same_value %rbp
  62. .endm
  63. .globl {trap_stubs_start}
  64. {trap_stubs_start}:
  65. .altmacro
  66. .macro build_isr_no_err name
  67. .align 8
  68. .globl ISR\name
  69. .type ISR\name @function
  70. ISR\name:
  71. .cfi_startproc
  72. .cfi_signal_frame
  73. .cfi_def_cfa_offset 0x08
  74. .cfi_offset %rsp, 0x10
  75. cfi_all_same_value
  76. push %rbp # push placeholder for error code
  77. .cfi_def_cfa_offset 0x10
  78. call _raw_trap_entry
  79. .cfi_endproc
  80. .endm
  81. .altmacro
  82. .macro build_isr_err name
  83. .align 8
  84. .globl ISR\name
  85. .type ISR\name @function
  86. ISR\name:
  87. .cfi_startproc
  88. .cfi_signal_frame
  89. .cfi_def_cfa_offset 0x10
  90. .cfi_offset %rsp, 0x10
  91. cfi_all_same_value
  92. call _raw_trap_entry
  93. .cfi_endproc
  94. .endm
  95. build_isr_no_err 0
  96. build_isr_no_err 1
  97. build_isr_no_err 2
  98. build_isr_no_err 3
  99. build_isr_no_err 4
  100. build_isr_no_err 5
  101. build_isr_no_err 6
  102. build_isr_no_err 7
  103. build_isr_err 8
  104. build_isr_no_err 9
  105. build_isr_err 10
  106. build_isr_err 11
  107. build_isr_err 12
  108. build_isr_err 13
  109. build_isr_err 14
  110. build_isr_no_err 15
  111. build_isr_no_err 16
  112. build_isr_err 17
  113. build_isr_no_err 18
  114. build_isr_no_err 19
  115. build_isr_no_err 20
  116. build_isr_err 21
  117. build_isr_no_err 22
  118. build_isr_no_err 23
  119. build_isr_no_err 24
  120. build_isr_no_err 25
  121. build_isr_no_err 26
  122. build_isr_no_err 27
  123. build_isr_no_err 28
  124. build_isr_err 29
  125. build_isr_err 30
  126. build_isr_no_err 31
  127. .set i, 32
  128. .rept 0x80+1
  129. build_isr_no_err %i
  130. .set i, i+1
  131. .endr
  132. .globl _raw_trap_entry
  133. .type _raw_trap_entry @function
  134. _raw_trap_entry:
  135. .cfi_startproc
  136. .cfi_signal_frame
  137. .cfi_def_cfa %rsp, 0x18
  138. .cfi_offset %rsp, 0x10
  139. cfi_all_same_value
  140. sub $0x78, %rsp
  141. .cfi_def_cfa_offset CS
  142. mov %rax, RAX(%rsp)
  143. .cfi_rel_offset %rax, RAX
  144. mov %rbx, RBX(%rsp)
  145. .cfi_rel_offset %rbx, RBX
  146. mov %rcx, RCX(%rsp)
  147. .cfi_rel_offset %rcx, RCX
  148. mov %rdx, RDX(%rsp)
  149. .cfi_rel_offset %rdx, RDX
  150. mov %rdi, RDI(%rsp)
  151. .cfi_rel_offset %rdi, RDI
  152. mov %rsi, RSI(%rsp)
  153. .cfi_rel_offset %rsi, RSI
  154. mov %r8, R8(%rsp)
  155. .cfi_rel_offset %r8, R8
  156. mov %r9, R9(%rsp)
  157. .cfi_rel_offset %r9, R9
  158. mov %r10, R10(%rsp)
  159. .cfi_rel_offset %r10, R10
  160. mov %r11, R11(%rsp)
  161. .cfi_rel_offset %r11, R11
  162. mov %r12, R12(%rsp)
  163. .cfi_rel_offset %r12, R12
  164. mov %r13, R13(%rsp)
  165. .cfi_rel_offset %r13, R13
  166. mov %r14, R14(%rsp)
  167. .cfi_rel_offset %r14, R14
  168. mov %r15, R15(%rsp)
  169. .cfi_rel_offset %r15, R15
  170. mov %rbp, RBP(%rsp)
  171. .cfi_rel_offset %rbp, RBP
  172. mov INT_NO(%rsp), %rcx
  173. sub ${trap_stubs_start}, %rcx
  174. shr $3, %rcx
  175. mov %rcx, INT_NO(%rsp)
  176. cmpq $0x08, CS(%rsp)
  177. je 2f
  178. swapgs
  179. 2:
  180. mov %gs:0, %rcx
  181. add ${handler}, %rcx
  182. mov (%rcx), %rcx
  183. jmp *%rcx
  184. .cfi_endproc
  185. _raw_trap_return:
  186. .cfi_startproc
  187. .cfi_def_cfa %rsp, CS
  188. .cfi_rel_offset %rax, RAX
  189. .cfi_rel_offset %rbx, RBX
  190. .cfi_rel_offset %rcx, RCX
  191. .cfi_rel_offset %rdx, RDX
  192. .cfi_rel_offset %rdi, RDI
  193. .cfi_rel_offset %rsi, RSI
  194. .cfi_rel_offset %r8, R8
  195. .cfi_rel_offset %r9, R9
  196. .cfi_rel_offset %r10, R10
  197. .cfi_rel_offset %r11, R11
  198. .cfi_rel_offset %r12, R12
  199. .cfi_rel_offset %r13, R13
  200. .cfi_rel_offset %r14, R14
  201. .cfi_rel_offset %r15, R15
  202. .cfi_rel_offset %rbp, RBP
  203. .cfi_rel_offset %rsp, RSP
  204. mov RAX(%rsp), %rax
  205. .cfi_restore %rax
  206. mov RBX(%rsp), %rbx
  207. .cfi_restore %rbx
  208. mov RCX(%rsp), %rcx
  209. .cfi_restore %rcx
  210. mov RDX(%rsp), %rdx
  211. .cfi_restore %rdx
  212. mov RDI(%rsp), %rdi
  213. .cfi_restore %rdi
  214. mov RSI(%rsp), %rsi
  215. .cfi_restore %rsi
  216. mov R8(%rsp), %r8
  217. .cfi_restore %r8
  218. mov R9(%rsp), %r9
  219. .cfi_restore %r9
  220. mov R10(%rsp), %r10
  221. .cfi_restore %r10
  222. mov R11(%rsp), %r11
  223. .cfi_restore %r11
  224. mov R12(%rsp), %r12
  225. .cfi_restore %r12
  226. mov R13(%rsp), %r13
  227. .cfi_restore %r13
  228. mov R14(%rsp), %r14
  229. .cfi_restore %r14
  230. mov R15(%rsp), %r15
  231. .cfi_restore %r15
  232. mov RBP(%rsp), %rbp
  233. .cfi_restore %rbp
  234. cmpq $0x08, CS(%rsp)
  235. je 2f
  236. swapgs
  237. 2:
  238. lea RIP(%rsp), %rsp
  239. .cfi_def_cfa %rsp, 0x08
  240. .cfi_offset %rsp, 0x10
  241. iretq
  242. .cfi_endproc
  243. ",
  244. trap_stubs_start = sym trap_stubs_start,
  245. handler = sym _percpu_inner_TRAP_HANDLER,
  246. options(att_syntax),
  247. );
  248. /// Default handler handles the trap on the current stack and returns
  249. /// to the context before interrut.
  250. #[unsafe(naked)]
  251. unsafe extern "C" fn default_trap_handler() {
  252. naked_asm!(
  253. ".cfi_startproc",
  254. "mov %rsp, %rbx",
  255. ".cfi_def_cfa_register %rbx",
  256. "",
  257. "and $~0xf, %rsp",
  258. "",
  259. "mov %rbx, %rdi",
  260. "call {handle_trap}",
  261. "",
  262. "mov %rbx, %rsp",
  263. ".cfi_def_cfa_register %rsp",
  264. "",
  265. "jmp {trap_return}",
  266. ".cfi_endproc",
  267. handle_trap = sym _default_trap_handler,
  268. trap_return = sym _raw_trap_return,
  269. options(att_syntax),
  270. );
  271. }
  272. #[unsafe(naked)]
  273. unsafe extern "C" fn captured_trap_handler() {
  274. naked_asm!(
  275. "mov ${from_context}, %rdi",
  276. "mov %gs:0, %rsi",
  277. "add ${to_context}, %rsi",
  278. "",
  279. "mov %rdi, %rsp", // We need a temporary stack to use `switch()`.
  280. "",
  281. "jmp {switch}",
  282. from_context = sym DIRTY_TRAP_CONTEXT,
  283. to_context = sym _percpu_inner_CAPTURER_CONTEXT,
  284. switch = sym TaskContext::switch,
  285. options(att_syntax),
  286. );
  287. }
  288. #[unsafe(naked)]
  289. unsafe extern "C" fn captured_trap_return(trap_context: usize) -> ! {
  290. naked_asm!(
  291. "jmp {trap_return}",
  292. trap_return = sym _raw_trap_return,
  293. options(att_syntax),
  294. );
  295. }
  296. impl TrapReturn for TrapContext {
  297. type TaskContext = TaskContext;
  298. unsafe fn trap_return(&mut self, task_ctx: &mut Self::TaskContext) {
  299. let irq_states = disable_irqs_save();
  300. let old_handler = TRAP_HANDLER.swap(captured_trap_handler);
  301. task_ctx.set_program_counter(captured_trap_return as _);
  302. task_ctx.set_stack_pointer(&raw mut *self as usize);
  303. task_ctx.set_interrupt_enabled(false);
  304. unsafe {
  305. TaskContext::switch(CAPTURER_CONTEXT.as_mut(), &mut *task_ctx);
  306. }
  307. TRAP_HANDLER.set(old_handler);
  308. irq_states.restore();
  309. }
  310. }
  311. impl IrqStateTrait for IrqState {
  312. fn restore(self) {
  313. let Self(state) = self;
  314. unsafe {
  315. asm!(
  316. "push {state}",
  317. "popf",
  318. state = in(reg) state,
  319. options(att_syntax, nomem)
  320. );
  321. }
  322. }
  323. }
  324. pub fn enable_irqs() {
  325. unsafe {
  326. asm!("sti", options(att_syntax, nomem, nostack));
  327. }
  328. }
  329. pub fn disable_irqs() {
  330. unsafe {
  331. asm!("cli", options(att_syntax, nomem, nostack));
  332. }
  333. }
  334. pub fn disable_irqs_save() -> IrqState {
  335. let state: u64;
  336. unsafe {
  337. asm!(
  338. "pushf",
  339. "pop {state}",
  340. "cli",
  341. state = out(reg) state,
  342. options(att_syntax, nomem)
  343. );
  344. }
  345. IrqState(state)
  346. }