Skip to main content

islet_rmm/exception/
trap.rs

1mod frame;
2pub mod syndrome;
3
4use self::frame::TrapFrame;
5use self::syndrome::Fault;
6use self::syndrome::Syndrome;
7use super::lower::synchronous;
8use crate::cpu;
9use crate::event::realmexit::{ExitSyncType, RecExitReason};
10use crate::rec::simd;
11use crate::rec::Rec;
12
13use aarch64_cpu::registers::*;
14
15#[repr(u16)]
16#[derive(Debug, Copy, Clone)]
17pub enum Source {
18    CurrentSPEL0,
19    CurrentSPELx,
20    LowerAArch64,
21    LowerAArch32,
22}
23
24#[repr(u16)]
25#[derive(Debug, Copy, Clone)]
26pub enum Kind {
27    Synchronous,
28    Irq,
29    Fiq,
30    SError,
31}
32
33#[repr(C)]
34#[derive(Debug, Copy, Clone)]
35pub struct Info {
36    source: Source,
37    kind: Kind,
38}
39
40/// This function is called when an exception occurs from CurrentSPEL0, CurrentSPELx.
41/// The `info` parameter specifies source (first 16 bits) and kind (following 16
42/// bits) of the exception.
43/// The `esr` has the value of a syndrome register (ESR_ELx) holding the cause
44/// of the Synchronous and SError exception.
45/// The `tf` has the TrapFrame of current context.
46#[no_mangle]
47#[allow(unused_variables)]
48pub extern "C" fn handle_exception(info: Info, esr: u32, tf: &mut TrapFrame) {
49    match info.kind {
50        Kind::Synchronous => match Syndrome::from(esr) {
51            Syndrome::Brk(b) => {
52                debug!("brk #{}", b);
53                debug!("{:?}\nESR: {:X}\n{:#X?}", info, esr, tf);
54                tf.elr += 4; //continue
55            }
56            Syndrome::PCAlignmentFault => {
57                debug!("PCAlignmentFault");
58            }
59            Syndrome::DataAbort(fault) => {
60                let far = FAR_EL2.get();
61                debug!("Data Abort (higher), far:{:X}", far);
62                match fault {
63                    Fault::AddressSize { level } => {
64                        debug!("address size, level:{}", level);
65                    }
66                    Fault::Translation { level } => {
67                        debug!("translation, level:{}, esr:{:X}", level, esr);
68                    }
69                    Fault::AccessFlag { level } => {
70                        debug!("access flag, level:{}", level);
71                    }
72                    Fault::Permission { level } => {
73                        debug!("permission, level:{}", level);
74                    }
75                    Fault::Alignment => {
76                        debug!("alignment");
77                    }
78                    Fault::TLBConflict => {
79                        debug!("tlb conflict");
80                    }
81                    Fault::Other(_x) => {
82                        debug!("other");
83                    }
84                }
85            }
86            Syndrome::InstructionAbort(v) => {
87                debug!("Instruction Abort (higher)");
88            }
89            Syndrome::HVC => {
90                debug!("HVC");
91            }
92            Syndrome::SMC => {
93                debug!("SMC");
94            }
95            Syndrome::SysRegInst => {
96                debug!("SysRegInst");
97            }
98            Syndrome::WFX => {
99                debug!("WFX");
100            }
101            Syndrome::FPU | Syndrome::SVE | Syndrome::SME => {
102                // Islet RMM is not supposed to use simd.
103                debug!("ELR_EL2:{:x}", ELR_EL2.get());
104                panic!("RMM is using SIMD instruction");
105            }
106            Syndrome::Other(v) => {
107                debug!("Other");
108            }
109            undefined => {
110                panic!(
111                    "{:?} and esr {:x}, TrapFrame: {:?} on cpu::id {:?}",
112                    info,
113                    esr,
114                    tf,
115                    cpu::id()
116                );
117            }
118        },
119        _ => {
120            panic!(
121                "Unknown exception! Info={:?}, ESR={:x} on CPU {:?}",
122                info,
123                esr,
124                cpu::id()
125            );
126        }
127    }
128}
129
130pub const RET_TO_REC: u64 = 0;
131pub const RET_TO_RMM: u64 = 1;
132/// This function is called when an exception occurs from LowerAArch64.
133/// To enter RMM (EL2), return 1. Otherwise, return 0 to go back to EL1.
134/// The `info` parameter specifies source (first 16 bits) and kind (following 16
135/// bits) of the exception.
136/// The `esr` has the value of a syndrome register (ESR_ELx) holding the cause
137/// of the Synchronous and SError exception.
138/// The `rec` has the Rec context.
139/// The `tf` has the TrapFrame of current context.
140///
141/// Do not write sys_regs of Rec here. (ref. HANDLE_LOWER in vectors.s)
142#[no_mangle]
143#[allow(unused_variables)]
144pub extern "C" fn handle_lower_exception(
145    info: Info,
146    esr: u32,
147    rec: &mut Rec<'_>,
148    tf: &mut TrapFrame,
149) -> u64 {
150    match info.kind {
151        // TODO: adjust elr according to the decision that kvm made
152        Kind::Synchronous => match Syndrome::from(esr) {
153            Syndrome::HVC => {
154                debug!("Synchronous: HVC: {:#X}", rec.context.gp_regs[0]);
155
156                // Inject undefined exception to the realm
157                SPSR_EL1.set(rec.context.spsr_el2);
158                ELR_EL1.set(rec.context.elr_el2);
159                ESR_EL1.write(ESR_EL1::EC::Unknown + ESR_EL1::IL::SET);
160
161                // Return to realm's exception handler
162                let vbar = rec.context.sys_regs.vbar;
163                const SPSR_EL2_MODE_EL1H_OFFSET: u64 = 0x200;
164                rec.context.elr_el2 = vbar + SPSR_EL2_MODE_EL1H_OFFSET;
165
166                tf.regs[0] = RecExitReason::Sync(ExitSyncType::Undefined).into();
167                tf.regs[1] = esr as u64;
168                tf.regs[2] = 0;
169                tf.regs[3] = FAR_EL2.get();
170                RET_TO_REC
171            }
172            Syndrome::SMC => {
173                tf.regs[0] = RecExitReason::Sync(ExitSyncType::RSI).into();
174                tf.regs[1] = rec.context.gp_regs[0]; // RSI command
175                advance_pc(rec);
176                RET_TO_RMM
177            }
178            Syndrome::InstructionAbort(_) | Syndrome::DataAbort(_) => {
179                debug!("Synchronous: InstructionAbort | DataAbort");
180                if let Syndrome::InstructionAbort(_) = Syndrome::from(esr) {
181                    tf.regs[0] = RecExitReason::Sync(ExitSyncType::InstAbort).into()
182                } else {
183                    tf.regs[0] = RecExitReason::Sync(ExitSyncType::DataAbort).into();
184                }
185                tf.regs[1] = esr as u64;
186                tf.regs[2] = HPFAR_EL2.get();
187                tf.regs[3] = FAR_EL2.get();
188                let fipa = HPFAR_EL2.read(HPFAR_EL2::FIPA) << 8;
189                debug!("fipa: {:X}", fipa);
190                debug!("esr_el2: {:X}", esr);
191                RET_TO_RMM
192            }
193            Syndrome::SysRegInst => {
194                debug!("Synchronous: MRS, MSR System Register Instruction");
195                let ret = synchronous::sys_reg::handle(rec, esr as u64);
196                advance_pc(rec);
197                if ret == RET_TO_RMM {
198                    tf.regs[0] = RecExitReason::Sync(ExitSyncType::Undefined).into();
199                    tf.regs[1] = esr as u64;
200                    tf.regs[2] = 0;
201                }
202                ret
203            }
204            Syndrome::WFX => {
205                debug!("Synchronous: WFx");
206                tf.regs[0] = RecExitReason::Sync(ExitSyncType::WFx).into();
207                tf.regs[1] = esr as u64;
208                advance_pc(rec);
209                RET_TO_RMM
210            }
211            Syndrome::FPU | Syndrome::SVE | Syndrome::SME => {
212                debug!("Synchronous: SIMD");
213                let abort: bool = match Syndrome::from(esr) {
214                    Syndrome::SVE => !rec.context.simd.cfg.sve_en,
215                    // Note: Since only FEAT_SVE is set for Realms and
216                    // we are doing lazy restore, being reported as Syndrome::SME
217                    // is actually comming from the NW's SME setting.
218                    Syndrome::SME => !rec.context.simd.cfg.sve_en,
219                    _ => false,
220                };
221                if abort {
222                    // Inject undefined exception to the realm
223                    SPSR_EL1.set(rec.context.spsr_el2);
224                    ELR_EL1.set(rec.context.elr_el2);
225                    ESR_EL1.write(ESR_EL1::EC::Unknown + ESR_EL1::IL::SET);
226
227                    // Return to realm's exception handler
228                    let vbar = rec.context.sys_regs.vbar;
229                    const SPSR_EL2_MODE_EL1H_OFFSET: u64 = 0x200;
230                    rec.context.elr_el2 = vbar + SPSR_EL2_MODE_EL1H_OFFSET;
231
232                    tf.regs[0] = RecExitReason::Sync(ExitSyncType::Undefined).into();
233                    tf.regs[1] = esr as u64;
234                    tf.regs[2] = 0;
235                    tf.regs[3] = FAR_EL2.get();
236                    debug!("Unsupported feature access. Inject abort");
237                    return RET_TO_REC;
238                }
239                // Note: To avoid being trapped from RMM's access to simd,
240                //       setting cptr_el2 should come prior to the context restoration.
241                simd::restore_state_lazy(rec);
242                rec.context.simd.is_used = true;
243                RET_TO_REC
244            }
245            undefined => {
246                debug!("Synchronous: Other");
247                tf.regs[0] = RecExitReason::Sync(ExitSyncType::Undefined).into();
248                tf.regs[1] = esr as u64;
249                RET_TO_RMM
250            }
251        },
252        Kind::Irq => {
253            debug!("IRQ");
254            tf.regs[0] = RecExitReason::IRQ.into();
255            // IRQ isn't interpreted with esr. It just hold previsou info. Void them out.
256            tf.regs[1] = 0;
257            RET_TO_RMM
258        }
259        Kind::SError => {
260            debug!("SError");
261            tf.regs[0] = RecExitReason::SError.into();
262            tf.regs[1] = esr as u64;
263            RET_TO_RMM
264        }
265        _ => {
266            error!(
267                "Unknown exception! Info={:?}, ESR={:x} on CPU {:?}",
268                info,
269                esr,
270                cpu::id()
271            );
272            RET_TO_REC
273        }
274    }
275}
276
277#[inline(always)]
278fn advance_pc(rec: &mut Rec<'_>) {
279    rec.context.elr_el2 += 4;
280}