1use super::mpidr::MPIDR;
2use super::params::Params;
3use super::run::{EntryFlag, Run};
4use super::vtcr::{activate_stage2_mmu, prepare_vtcr};
5use crate::event::RmiHandle;
6#[cfg(feature = "gst_page_table")]
7use crate::granule::{set_granule, set_granule_with_parent, GranuleState};
8#[cfg(not(feature = "gst_page_table"))]
9use crate::granule::{set_granule, GranuleState};
10use crate::host;
11use crate::listen;
12use crate::measurement::HashContext;
13use crate::realm::rd::{Rd, State};
14use crate::rec::context::{set_reg, RegOffset};
15use crate::rec::State as RecState;
16use crate::rec::{max_recs_order, Rec, RmmRecEmulatableAbort::NotEmulatableAbort};
17use crate::rmi;
18use crate::rmi::error::Error;
19use crate::rsi::do_host_call;
20use crate::rsi::psci::complete_psci;
21use crate::{get_granule, get_granule_if};
22
23use aarch64_cpu::registers::*;
24use armv9a::bits_in_reg;
25
26extern crate alloc;
27
28fn prepare_args(rd: &mut Rd, mpidr: u64) -> Result<(usize, u64, u64), Error> {
29 let page_table = rd.s2_table().lock().get_base_address() as u64;
30 let vttbr = bits_in_reg(
31 VTTBR_EL2::VMID.mask << VTTBR_EL2::VMID.shift,
32 rd.id() as u64,
33 ) | bits_in_reg(
34 VTTBR_EL2::BADDR.mask << VTTBR_EL2::BADDR.shift,
35 page_table >> 1,
36 );
37 let vmpidr = mpidr | (MPIDR_EL1::RES1.mask << MPIDR_EL1::RES1.shift);
38 let vcpuid = rd.vcpu_index;
39 rd.vcpu_index += 1;
40 Ok((vcpuid, vttbr, vmpidr))
41}
42
43pub fn set_event_handler(rmi: &mut RmiHandle) {
44 #[cfg(not(kani))]
45 listen!(rmi, rmi::REC_CREATE, |arg, ret, rmm| {
46 let rd = arg[0];
47 let rec = arg[1];
48 let params_ptr = arg[2];
49 let owner = rd;
50
51 if rec == rd {
52 return Err(Error::RmiErrorInput);
53 }
54
55 rmm.page_table.map(params_ptr, false);
56 let params = host::copy_from::<Params>(params_ptr).ok_or(Error::RmiErrorInput)?;
57 rmm.page_table.unmap(params_ptr);
58 params.verify_compliance(rec, rd, params_ptr)?;
59
60 let rec_index = MPIDR::from(params.mpidr).index();
61 let mut rd_granule = get_granule_if!(rd, GranuleState::RD)?;
62 let mut rd = rd_granule.content_mut::<Rd>()?;
63 if !rd.at_state(State::New) {
64 return Err(Error::RmiErrorRealm(0));
65 }
66 if rd.num_recs() == (1 << max_recs_order()) - 1 {
67 return Err(Error::RmiErrorRealm(0));
68 }
69
70 if rec_index != rd.rec_index() {
71 return Err(Error::RmiErrorInput);
72 }
73 let mut rec_granule = get_granule_if!(rec, GranuleState::Delegated)?;
75 #[cfg(not(kani))]
76 rmm.page_table.map(rec, true);
78 let mut rec = rec_granule.new_uninit_with::<Rec<'_>>(Rec::new())?;
79 match prepare_args(&mut rd, params.mpidr) {
80 Ok((vcpuid, vttbr, vmpidr)) => {
81 ret[1] = vcpuid;
82
83 for i in 0..rmi::MAX_REC_AUX_GRANULES {
86 let aux = params.aux[i] as usize;
87 rmm.page_table.map(aux, true);
88 let mut aux_granule = get_granule_if!(aux, GranuleState::Delegated)?;
89 set_granule(&mut aux_granule, GranuleState::RecAux)?;
90 }
91 rec.init(owner, vcpuid, params.flags, params.aux, vttbr, vmpidr)?;
92 }
93 Err(_) => return Err(Error::RmiErrorInput),
94 }
95
96 for (idx, gpr) in params.gprs.iter().enumerate() {
97 if set_reg(&mut rec, idx, *gpr as usize).is_err() {
98 return Err(Error::RmiErrorInput);
99 }
100 }
101 if set_reg(&mut rec, RegOffset::PC, params.pc as usize).is_err() {
102 return Err(Error::RmiErrorInput);
103 }
104 rec.set_vtcr(prepare_vtcr(&rd)?);
105
106 rd.inc_recs();
107 #[cfg(not(kani))]
108 HashContext::new(&mut rd)?.measure_rec_params(¶ms)?;
110
111 #[cfg(not(feature = "gst_page_table"))]
112 rd_granule.inc_count();
113
114 #[cfg(feature = "gst_page_table")]
115 return set_granule_with_parent(rd_granule.clone(), &mut rec_granule, GranuleState::Rec);
116 #[cfg(not(feature = "gst_page_table"))]
117 return set_granule(&mut rec_granule, GranuleState::Rec);
118 });
119
120 #[cfg(any(not(kani), feature = "mc_rmi_rec_destroy"))]
121 listen!(rmi, rmi::REC_DESTROY, |arg, _ret, rmm| {
122 let mut rec_granule = get_granule_if!(arg[0], GranuleState::Rec)?;
123
124 let rec = rec_granule.content::<Rec<'_>>()?;
125 if rec.get_state() == RecState::Running {
126 return Err(Error::RmiErrorRec);
127 }
128
129 #[cfg(not(kani))]
130 for i in 0..rmi::MAX_REC_AUX_GRANULES {
131 let rec_aux = rec.aux(i) as usize;
132 let mut rec_aux_granule = get_granule_if!(rec_aux, GranuleState::RecAux)?;
133 set_granule(&mut rec_aux_granule, GranuleState::Delegated)?;
134 rmm.page_table.unmap(rec_aux);
135 }
136 #[cfg(kani)]
137 {
138 let rec_aux = rec.aux(0) as usize;
141 kani::assume(crate::granule::validate_addr(rec_aux));
143 let mut rec_aux_granule = get_granule!(rec_aux)?;
144 set_granule(&mut rec_aux_granule, GranuleState::Delegated)?;
145 }
146
147 let rd = rec.owner()?;
148 #[cfg(kani)]
149 {
150 kani::assume(crate::granule::validate_addr(rd));
152 let rd_granule = get_granule!(rd)?;
153 kani::assume(rd_granule.state() == GranuleState::RD);
154 }
155 let mut rd_granule = get_granule_if!(rd, GranuleState::RD)?;
156 #[cfg(not(feature = "gst_page_table"))]
157 rd_granule.dec_count();
158 let mut rd = rd_granule.content::<Rd>()?;
159 rd.dec_recs();
160
161 set_granule(&mut rec_granule, GranuleState::Delegated).inspect_err(|_| {
162 #[cfg(not(kani))]
163 rmm.page_table.unmap(arg[0]);
165 })?;
166 #[cfg(not(kani))]
167 rmm.page_table.unmap(arg[0]);
169 Ok(())
170 });
171
172 #[cfg(not(kani))]
173 listen!(rmi, rmi::REC_ENTER, |arg, ret, rmm| {
174 let run_pa = arg[1];
175
176 let mut rec_granule = get_granule_if!(arg[0], GranuleState::Rec)?;
178 let mut rec = rec_granule.content_mut::<Rec<'_>>()?;
179
180 rmm.page_table.map(run_pa, false);
182 let mut run = host::copy_from::<Run>(run_pa).ok_or(Error::RmiErrorInput)?;
183 rmm.page_table.unmap(run_pa);
184 run.verify_compliance()?;
185 trace!("{:?}", run);
186
187 let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
188 let rd = rd_granule.content::<Rd>()?;
189 match rd.state() {
190 State::Active => {}
191 State::New => {
192 return Err(Error::RmiErrorRealm(0));
193 }
194 State::SystemOff => {
195 return Err(Error::RmiErrorRealm(1));
196 }
197 _ => {
198 panic!("Unexpected realm state");
199 }
200 }
201 core::mem::drop(rd_granule);
203
204 if !rec.runnable() {
206 return Err(Error::RmiErrorRec);
207 }
208
209 if let RecState::Running = rec.get_state() {
210 error!("Rec is already running: {:?}", *rec);
211 return Err(Error::RmiErrorRec);
212 }
213
214 if rec.psci_pending() {
215 return Err(Error::RmiErrorRec);
216 }
217
218 #[cfg(not(any(miri, test, fuzzing)))]
219 if !crate::rec::gic::validate_state(&run) {
220 return Err(Error::RmiErrorRec);
221 }
222
223 if rec.host_call_pending() {
224 do_host_call(arg, ret, rmm, &mut rec, &mut run)?;
226 }
227
228 #[cfg(not(any(miri, test, fuzzing)))]
229 crate::rec::gic::receive_state_from_host(&mut rec, &run)?;
230 crate::rec::mmio::emulate_mmio(&mut rec, &run)?;
231 crate::rec::sea::host_sea_inject(&mut rec, &run)?;
232
233 crate::rsi::ripas::complete_ripas(&mut rec, &run)?;
234
235 let wfx_flag = run.entry_flags();
236 #[cfg(not(any(miri, test, fuzzing)))]
237 {
238 let hcr_el2 = HCR_EL2.get();
239 let mut wfx = 0;
240 let wfx_mask = !(3 << 13);
241 if wfx_flag.get_masked(EntryFlag::TRAP_WFI) != 0 {
242 wfx |= 1 << 13;
243 }
244 if wfx_flag.get_masked(EntryFlag::TRAP_WFE) != 0 {
245 wfx |= 1 << 14;
246 }
247 HCR_EL2.set((hcr_el2 & wfx_mask) | wfx);
248 }
249
250 #[cfg(not(any(miri, test, fuzzing)))]
251 activate_stage2_mmu(&rec);
252
253 crate::rec::save_host_state(&rec);
254 let mut ret_ns;
255 loop {
256 ret_ns = true;
257 run.set_imm(0);
258
259 rec.set_state(RecState::Running);
260
261 #[cfg(not(any(miri, test, fuzzing)))]
262 {
263 use crate::rmi::rec::exit::handle_realm_exit;
264
265 let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
266 let rd = rd_granule.content::<Rd>()?;
267
268 rec.set_emulatable_abort(NotEmulatableAbort);
269 crate::rec::run_prepare(&rd, rec.vcpuid(), &mut rec, 0)?;
270 core::mem::drop(rd_granule);
273 match crate::rec::run() {
274 Ok(realm_exit_res) => {
275 (ret_ns, ret[0]) =
276 handle_realm_exit(realm_exit_res, rmm, &mut rec, &mut run)?
277 }
278 Err(_) => ret[0] = rmi::ERROR_REC,
279 }
280 }
281
282 #[cfg(any(miri, test))]
283 {
284 use crate::test_utils::mock;
285 mock::realm::setup_psci_complete(&mut rec, &mut run);
286 mock::realm::setup_ripas_state(&mut rec, &mut run);
287 }
288
289 #[cfg(fuzzing)]
290 {
291 use crate::test_utils::mock;
292
293 if arg.len() >= 3 {
297 let cmd = arg[2];
298 let args = &arg[3..];
299
300 rec.set_emulatable_abort(NotEmulatableAbort);
301
302 (_, ret[0]) = mock::realm::emulate_realm(rmm, &mut rec, &mut run, cmd, args)?;
303 }
304 }
305
306 rec.set_state(RecState::Ready);
307
308 if ret_ns {
309 break;
310 }
311 }
312
313 #[cfg(not(any(miri, test, fuzzing)))]
314 crate::rec::gic::send_state_to_host(&rec, &mut run)?;
315 crate::rec::timer::send_state_to_host(&rec, &mut run)?;
316 crate::rec::pmu::send_state_to_host(&rec, &mut run)?;
317 crate::rec::restore_host_state(&rec);
318
319 rmm.page_table.map(run_pa, false);
321 let ret = host::copy_to_ptr::<Run>(&run, run_pa).ok_or(Error::RmiErrorInput);
322 rmm.page_table.unmap(run_pa);
323 ret
324 });
325
326 #[cfg(not(kani))]
327 listen!(rmi, rmi::PSCI_COMPLETE, |arg, _ret, _rmm| {
328 let caller_pa = arg[0];
329 let target_pa = arg[1];
330
331 if caller_pa == target_pa {
332 return Err(Error::RmiErrorInput);
333 }
334 let mut caller_granule = get_granule_if!(caller_pa, GranuleState::Rec)?;
335 let mut caller = caller_granule.content_mut::<Rec<'_>>()?;
336
337 let mut target_granule = get_granule_if!(target_pa, GranuleState::Rec)?;
338 let mut target = target_granule.content_mut::<Rec<'_>>()?;
339
340 let status = arg[2];
341
342 if !caller.psci_pending() {
343 return Err(Error::RmiErrorInput);
344 }
345
346 if caller.realmid()? != target.realmid()? {
347 return Err(Error::RmiErrorInput);
348 }
349
350 complete_psci(&mut caller, &mut target, status)
351 });
352}
353
354#[cfg(test)]
355mod test {
356 use crate::event::realmexit::RecExitReason;
357 use crate::rmi::rec::run::Run;
358 use crate::rmi::*;
359 use crate::rsi::PSCI_CPU_ON;
360 use crate::test_utils::*;
361
362 #[test]
367 fn rmi_rec_create_positive() {
368 let rd = realm_create();
369 rec_create(rd, IDX_REC1, IDX_REC1_PARAMS, IDX_REC1_AUX);
370 rec_destroy(IDX_REC1, IDX_REC1_AUX);
371 realm_destroy(rd);
372
373 miri_teardown();
374 }
375
376 #[test]
380 fn rmi_rec_enter_positive() {
381 let rd = mock::host::realm_setup();
382
383 let (rec1, run1) = (granule_addr(IDX_REC1), granule_addr(IDX_REC1_RUN));
384 let ret = rmi::<REC_ENTER>(&[rec1, run1]);
385 assert_eq!(ret[0], SUCCESS);
386
387 unsafe {
388 let run = &*(run1 as *const Run);
389 let reason: u64 = RecExitReason::PSCI.into();
390 assert_eq!(run.exit_reason(), reason as u8);
391 assert_eq!(run.gpr(0).unwrap(), PSCI_CPU_ON as u64);
392 }
393
394 let rec2 = granule_addr(IDX_REC2);
395 const PSCI_E_SUCCESS: usize = 0;
396 let ret = rmi::<PSCI_COMPLETE>(&[rec1, rec2, PSCI_E_SUCCESS]);
397 assert_eq!(ret[0], SUCCESS);
398
399 mock::host::realm_teardown(rd);
400
401 miri_teardown();
402 }
403}