kernel/arch/riscv64/trap/
exception.rs

1use core::arch::asm;
2use core::panic;
3
4use crate::abi::syscall_dispatcher;
5use crate::arch::trap::print_traplog;
6use crate::arch::{Trapframe, get_cpu};
7use crate::println;
8use crate::sched::scheduler::get_scheduler;
9use crate::task::mytask;
10
11fn log_fatal_page_fault_context(
12    trapframe: &Trapframe,
13    cause: usize,
14    vaddr: usize,
15    task_id: usize,
16    task_name: &str,
17    asid: u16,
18) {
19    use crate::arch::vm::{get_root_pagetable_ptr, is_asid_used};
20
21    let cpu_id = get_cpu().get_cpuid();
22    let epc = trapframe.epc as usize;
23    // RISC-V integer registers: x1=ra, x2=sp, x8=s0/fp
24    let ra = trapframe.regs.reg[1] as usize;
25    let sp = trapframe.regs.reg[2] as usize;
26    let fp = trapframe.regs.reg[8] as usize;
27
28    let asid_used = is_asid_used(asid);
29    let root_pt = get_root_pagetable_ptr(asid).unwrap_or(core::ptr::null_mut());
30
31    println!(
32        "[Trap] fatal page fault map failed: cpu={} cause={} task_id={} name={} asid={} asid_used={} root_pt={:p}",
33        cpu_id, cause, task_id, task_name, asid, asid_used, root_pt
34    );
35    println!(
36        "[Trap] epc={:#x} vaddr={:#x} ra={:#x} sp={:#x} fp={:#x}",
37        epc, vaddr, ra, sp, fp
38    );
39}
40
41pub fn arch_exception_handler(trapframe: &mut Trapframe, cause: usize) {
42    match cause {
43        /* Illegal instruction (used for lazy FP/Vector enable) */
44        2 => {
45            let task = get_scheduler()
46                .get_current_task(get_cpu().get_cpuid())
47                .unwrap();
48
49            let user_fpu_allowed = crate::arch::user_fpu_enabled();
50            let user_vec_allowed = crate::arch::user_vector_enabled();
51
52            // Read stval (may contain the faulting instruction word; can also be 0).
53            let mut inst: usize;
54            let sstatus: usize;
55            unsafe {
56                asm!(
57                    "csrr {0}, stval",
58                    "csrr {1}, sstatus",
59                    out(reg) inst,
60                    out(reg) sstatus,
61                );
62            }
63
64            // If stval doesn't contain the instruction, try to fetch it from the task's
65            // mapped user code via the VM translation.
66            if inst == 0 {
67                if let Some(paddr) = task.vm_manager.translate_vaddr(trapframe.epc as usize) {
68                    inst = crate::arch::instruction::Instruction::fetch(paddr).raw as usize;
69                }
70            }
71
72            // Helpers for determining whether we should treat this as a lazy enable trap.
73            let fs_off = (sstatus & 0x6000) == 0;
74            let vs_off = (sstatus & 0x600) == 0;
75
76            let raw32 = inst as u32;
77            let is_32bit = (raw32 & 0b11) == 0b11;
78
79            // Vector instructions are always 32-bit.
80            let is_vector_insn = if is_32bit {
81                let opcode = raw32 & 0x7f;
82                if opcode == 0x57 {
83                    true
84                } else if opcode == 0x73 {
85                    // SYSTEM/CSR access: treat v* CSRs as vector-related.
86                    let csr = (raw32 >> 20) & 0xfff;
87                    // vstart..vcsr, vl..vlenb
88                    (csr >= 0x008 && csr <= 0x00a) || (csr >= 0xc20 && csr <= 0xc22)
89                } else {
90                    false
91                }
92            } else {
93                false
94            };
95
96            let is_fpu_insn = if is_32bit {
97                let opcode = raw32 & 0x7f;
98                let funct3 = (raw32 >> 12) & 0x7;
99                match opcode {
100                    // FP arithmetic and conversion ops.
101                    0x53 | 0x43 | 0x47 | 0x4b | 0x4f => true,
102                    // FP loads/stores.
103                    0x07 | 0x27 => matches!(funct3, 0b010 | 0b011 | 0b100),
104                    _ => false,
105                }
106            } else {
107                // Minimal support for common compressed FP loads/stores.
108                let raw16 = (raw32 & 0xffff) as u16;
109                let quadrant = raw16 & 0b11;
110                let funct3 = (raw16 >> 13) & 0x7;
111
112                // C.FLD/C.FSD (quadrant 0), C.FLDSP/C.FSDSP (quadrant 2)
113                (quadrant == 0b00 || quadrant == 0b10) && matches!(funct3, 0b001 | 0b101)
114            };
115
116            // Handle lazy enable without relying purely on instruction decoding.
117            // This avoids panicking if stval is 0 or if we cannot classify the instruction.
118            #[cfg(feature = "user-vector")]
119            if user_vec_allowed && vs_off && is_vector_insn {
120                task.vcpu.lock().vector_used = true;
121                crate::arch::riscv64::fpu::enable_vector();
122                if task.vcpu.lock().vector.is_none() {
123                    task.vcpu.lock().vector = Some(alloc::boxed::Box::new(
124                        crate::arch::riscv64::fpu::VectorContext::new(),
125                    ));
126                }
127                unsafe { task.vcpu.lock().vector.as_ref().unwrap().restore() };
128                crate::arch::riscv64::fpu::mark_vector_clean();
129                return;
130            }
131
132            #[cfg(feature = "user-fpu")]
133            if user_fpu_allowed && fs_off && is_fpu_insn {
134                task.vcpu.lock().fpu_used = true;
135                crate::arch::riscv64::fpu::enable_fpu();
136                unsafe { task.vcpu.lock().fpu.restore() };
137                crate::arch::riscv64::fpu::mark_fpu_clean();
138                return;
139            }
140
141            // Fallback: if the relevant extension is disabled, enable it and restore a
142            // safe initial context (prevents leaking previous task state).
143            #[cfg(feature = "user-fpu")]
144            if user_fpu_allowed && fs_off {
145                task.vcpu.lock().fpu_used = true;
146                crate::arch::riscv64::fpu::enable_fpu();
147                unsafe { task.vcpu.lock().fpu.restore() };
148                crate::arch::riscv64::fpu::mark_fpu_clean();
149                return;
150            }
151
152            #[cfg(feature = "user-vector")]
153            if user_vec_allowed && vs_off {
154                task.vcpu.lock().vector_used = true;
155                crate::arch::riscv64::fpu::enable_vector();
156                if task.vcpu.lock().vector.is_none() {
157                    task.vcpu.lock().vector = Some(alloc::boxed::Box::new(
158                        crate::arch::riscv64::fpu::VectorContext::new(),
159                    ));
160                }
161                unsafe { task.vcpu.lock().vector.as_ref().unwrap().restore() };
162                crate::arch::riscv64::fpu::mark_vector_clean();
163                return;
164            }
165
166            print_traplog(trapframe);
167            panic!(
168                "Unhandled illegal instruction: inst={:#x} epc={:#x}",
169                raw32, trapframe.epc
170            );
171        }
172        /* Environment call from U-mode */
173        8 => {
174            /* Execute SystemCall */
175            match syscall_dispatcher(trapframe) {
176                Ok(ret) => {
177                    trapframe.set_return_value(ret);
178                }
179                Err(msg) => {
180                    // panic!("Syscall error: {}", msg);
181                    println!("Syscall error: {}", msg);
182                    trapframe.set_return_value(usize::MAX); // Set error code: -1
183                    trapframe.increment_pc_next(mytask().unwrap());
184                }
185            }
186        }
187        /* Instruction page fault */
188        12 => {
189            let mut vaddr = trapframe.epc as usize;
190            let task = get_scheduler()
191                .get_current_task(get_cpu().get_cpuid())
192                .unwrap();
193            use crate::object::capability::memory_mapping::{AccessKind, AccessOp};
194            loop {
195                let access = AccessKind {
196                    op: AccessOp::Instruction,
197                    vaddr,
198                    size: None,
199                };
200                match task.vm_manager.lazy_map_page_with(access) {
201                    Ok(_) => (),
202                    Err(_) => {
203                        print_traplog(trapframe);
204                        log_fatal_page_fault_context(
205                            trapframe,
206                            cause,
207                            vaddr,
208                            task.get_id(),
209                            &task.name.read(),
210                            task.vm_manager.get_asid(),
211                        );
212                        panic!(
213                            "Failed to map page for instruction page fault at vaddr: {:#x}",
214                            vaddr
215                        );
216                    }
217                }
218
219                if vaddr & 0b11 == 0 {
220                    // If the address is aligned, we can stop
221                    break;
222                }
223                vaddr = (vaddr + 4) & !0b11; // Align to the next 4-byte boundary
224            }
225        }
226        /* Load/Store page fault */
227        13 | 15 => {
228            let mut vaddr;
229            unsafe {
230                asm!("csrr {}, stval", out(reg) vaddr);
231            }
232            let task = get_scheduler()
233                .get_current_task(get_cpu().get_cpuid())
234                .unwrap();
235            use crate::object::capability::memory_mapping::{AccessKind, AccessOp};
236            loop {
237                let op = if cause == 13 {
238                    AccessOp::Load
239                } else {
240                    AccessOp::Store
241                };
242                let access = AccessKind {
243                    op,
244                    vaddr,
245                    size: None,
246                };
247                match task.vm_manager.lazy_map_page_with(access) {
248                    Ok(_) => (),
249                    Err(_) => {
250                        print_traplog(trapframe);
251                        log_fatal_page_fault_context(
252                            trapframe,
253                            cause,
254                            vaddr,
255                            task.get_id(),
256                            &task.name.read(),
257                            task.vm_manager.get_asid(),
258                        );
259                        panic!(
260                            "Failed to map page for load/store page fault at vaddr: {:#x}",
261                            vaddr
262                        );
263                    }
264                }
265
266                if vaddr & 0b11 == 0 {
267                    // If the address is aligned, we can stop
268                    break;
269                }
270                vaddr = (vaddr + 4) & !0b11; // Align to the next 4-byte boundary
271            }
272        }
273        _ => {
274            print_traplog(trapframe);
275            panic!("Unhandled exception: {}", cause);
276        }
277    }
278}