hyperlight_guest/
interrupt_entry.rs

1/*
2Copyright 2024 The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17// Note: this code takes reference from
18// https://github.com/nanvix/nanvix/blob/dev/src/kernel/src/hal/arch/x86/hooks.S
19
20use core::arch::global_asm;
21
22use crate::interrupt_handlers::hl_exception_handler;
23
24extern "sysv64" {
25    // Exception handlers
26    pub(crate) fn _do_excp0();
27    pub(crate) fn _do_excp1();
28    pub(crate) fn _do_excp2();
29    pub(crate) fn _do_excp3();
30    pub(crate) fn _do_excp4();
31    pub(crate) fn _do_excp5();
32    pub(crate) fn _do_excp6();
33    pub(crate) fn _do_excp7();
34    pub(crate) fn _do_excp8();
35    pub(crate) fn _do_excp9();
36    pub(crate) fn _do_excp10();
37    pub(crate) fn _do_excp11();
38    pub(crate) fn _do_excp12();
39    pub(crate) fn _do_excp13();
40    pub(crate) fn _do_excp14();
41    pub(crate) fn _do_excp15();
42    pub(crate) fn _do_excp16();
43    pub(crate) fn _do_excp17();
44    pub(crate) fn _do_excp18();
45    pub(crate) fn _do_excp19();
46    pub(crate) fn _do_excp20();
47    pub(crate) fn _do_excp30();
48}
49
50// Defines `context_save` and `context_restore`
51macro_rules! context_save {
52    () => {
53        concat!(
54            // Save general-purpose registers
55            "    push rax\n",
56            "    push rbx\n",
57            "    push rcx\n",
58            "    push rdx\n",
59            "    push rsi\n",
60            "    push rdi\n",
61            "    push rbp\n",
62            "    push r8\n",
63            "    push r9\n",
64            "    push r10\n",
65            "    push r11\n",
66            "    push r12\n",
67            "    push r13\n",
68            "    push r14\n",
69            "    push r15\n",
70            // Save segment registers
71            "    mov rax, ds\n",
72            "    push rax\n",
73            "    mov rax, es\n",
74            "    push rax\n",
75            "    mov rax, fs\n",
76            "    push rax\n",
77            "    mov rax, gs\n",
78            "    push rax\n",
79        )
80    };
81}
82
83macro_rules! context_restore {
84    () => {
85        concat!(
86            // Restore segment registers
87            "    pop rax\n",
88            "    mov gs, rax\n",
89            "    pop rax\n",
90            "    mov fs, rax\n",
91            "    pop rax\n",
92            "    mov es, rax\n",
93            "    pop rax\n",
94            "    mov ds, rax\n",
95            // Restore general-purpose registers
96            "    pop r15\n",
97            "    pop r14\n",
98            "    pop r13\n",
99            "    pop r12\n",
100            "    pop r11\n",
101            "    pop r10\n",
102            "    pop r9\n",
103            "    pop r8\n",
104            "    pop rbp\n",
105            "    pop rdi\n",
106            "    pop rsi\n",
107            "    pop rdx\n",
108            "    pop rcx\n",
109            "    pop rbx\n",
110            "    pop rax\n",
111        )
112    };
113}
114
115// Generates exception handlers
116macro_rules! generate_exceptions {
117    () => {
118        concat!(
119            // Common exception handler
120            ".global _do_excp_common\n",
121            "_do_excp_common:\n",
122            // In SysV ABI, the first argument is passed in rdi
123            // rdi is the stack pointer.
124            "    mov rdi, rsp\n",
125            "    call {hl_exception_handler}\n",
126            context_restore!(),
127            "    iretq\n", // iretq is used to return from exception in x86_64
128            generate_excp!(0, pusherrcode),
129            generate_excp!(1, pusherrcode),
130            generate_excp!(2, pusherrcode),
131            generate_excp!(3, pusherrcode),
132            generate_excp!(4, pusherrcode),
133            generate_excp!(5, pusherrcode),
134            generate_excp!(6, pusherrcode),
135            generate_excp!(7, pusherrcode),
136            generate_excp!(8),
137            generate_excp!(9, pusherrcode),
138            generate_excp!(10),
139            generate_excp!(11),
140            generate_excp!(12),
141            generate_excp!(13),
142            generate_excp!(14, pagefault),
143            generate_excp!(15, pusherrcode),
144            generate_excp!(16, pusherrcode),
145            generate_excp!(17),
146            generate_excp!(18, pusherrcode),
147            generate_excp!(19, pusherrcode),
148            generate_excp!(20, pusherrcode),
149            generate_excp!(30),
150        )
151    };
152}
153
154// Macro to generate exception handlers
155// that satisfy the `extern`s at the top of the file.
156//
157// - Example output from this macro for generate_excp!(0) call:
158// ```assembly
159// .global _do_excp0
160// _do_excp0:
161//     context_save!()
162//     mov rsi, 0
163//     mov rdx, 0
164//     jmp _do_excp_common
165// ```
166macro_rules! generate_excp {
167    ($num:expr) => {
168        concat!(
169            ".global _do_excp",
170            stringify!($num),
171            "\n",
172            "_do_excp",
173            stringify!($num),
174            ":\n",
175            context_save!(),
176            // In SysV ABI, the second argument is passed in rsi
177            // rsi is the exception number.
178            "    mov rsi, ",
179            stringify!($num),
180            "\n",
181            // In SysV ABI, the third argument is passed in rdx
182            // rdx is only used for pagefault exception and
183            // contains the address that caused the pagefault.
184            "    mov rdx, 0\n",
185            "    jmp _do_excp_common\n"
186        )
187    };
188    ($num:expr, pusherrcode) => {
189        concat!(
190            ".global _do_excp",
191            stringify!($num),
192            "\n",
193            "_do_excp",
194            stringify!($num),
195            ":\n",
196            // Some exceptions push an error code onto the stack.
197            // For the ones that don't, we push a 0 to keep the
198            // stack aligned.
199            "   push 0\n",
200            context_save!(),
201            // In SysV ABI, the second argument is passed in rsi
202            // rsi is the exception number.
203            "    mov rsi, ",
204            stringify!($num),
205            "\n",
206            // In SysV ABI, the third argument is passed in rdx
207            // rdx is only used for pagefault exception and
208            // contains the address that caused the pagefault.
209            "    mov rdx, 0\n",
210            "    jmp _do_excp_common\n"
211        )
212    };
213    ($num:expr, pagefault) => {
214        concat!(
215            ".global _do_excp",
216            stringify!($num),
217            "\n",
218            "_do_excp",
219            stringify!($num),
220            ":\n",
221            context_save!(),
222            "    mov rsi, ",
223            stringify!($num),
224            "\n",
225            // In a page fault exception, the cr2 register
226            // contains the address that caused the page fault.
227            "    mov rdx, cr2\n",
228            "    jmp _do_excp_common\n"
229        )
230    };
231}
232
233// Output the assembly code
234global_asm!(
235    generate_exceptions!(),
236    hl_exception_handler = sym hl_exception_handler,
237);