Skip to main content

islet_rmm/rmi/
rtt.rs

1extern crate alloc;
2
3use crate::event::RmiHandle;
4use crate::granule::{
5    is_granule_aligned, is_not_in_realm, set_granule, GranuleState, GRANULE_SIZE,
6};
7use crate::host;
8use crate::host::DataPage;
9use crate::listen;
10use crate::measurement::HashContext;
11use crate::realm::mm::rtt;
12use crate::realm::mm::rtt::{RTT_MIN_BLOCK_LEVEL, RTT_PAGE_LEVEL};
13use crate::realm::mm::stage2_tte::{level_mask, S2TTE};
14use crate::realm::rd::{Rd, State};
15use crate::rec::Rec;
16use crate::rmi;
17use crate::rmi::error::Error;
18#[cfg(not(feature = "gst_page_table"))]
19use crate::{get_granule, get_granule_if};
20#[cfg(feature = "gst_page_table")]
21use crate::{get_granule, get_granule_if, set_state_and_get_granule};
22
23fn is_valid_rtt_cmd(rd: &Rd, ipa: usize, level: usize) -> bool {
24    if level > RTT_PAGE_LEVEL {
25        return false;
26    }
27
28    if ipa >= rd.ipa_size() {
29        return false;
30    }
31    let mask = level_mask(level).unwrap_or(0);
32    if ipa & mask as usize != ipa {
33        return false;
34    }
35    true
36}
37
38pub fn set_event_handler(rmi: &mut RmiHandle) {
39    listen!(rmi, rmi::RTT_CREATE, |arg, _ret, rmm| {
40        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
41        let rtt_addr = arg[1];
42        let rd = rd_granule.content::<Rd>()?;
43        let ipa = arg[2];
44        let level = arg[3];
45
46        let min_level = rd.s2_starting_level() as usize + 1;
47
48        if (level < min_level) || (level > RTT_PAGE_LEVEL) || !is_valid_rtt_cmd(&rd, ipa, level - 1)
49        {
50            return Err(Error::RmiErrorInput);
51        }
52        if rtt_addr == arg[0] {
53            return Err(Error::RmiErrorInput);
54        }
55        let mut rtt_granule = get_granule_if!(rtt_addr, GranuleState::Delegated)?;
56
57        // The below is added to avoid a fault regarding the RTT entry
58        // during the `create_pgtbl_at()` in `rtt::create()`.
59        #[cfg(not(kani))]
60        rmm.page_table.map(rtt_addr, true);
61        rtt::create(&rd, rtt_addr, ipa, level)?;
62        set_granule(&mut rtt_granule, GranuleState::RTT)?;
63        Ok(())
64    });
65
66    listen!(rmi, rmi::RTT_DESTROY, |arg, ret, _rmm| {
67        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
68        let rd = rd_granule.content::<Rd>()?;
69        let ipa = arg[1];
70        let level = arg[2];
71
72        let min_level = rd.s2_starting_level() as usize + 1;
73
74        if (level < min_level) || (level > RTT_PAGE_LEVEL) || !is_valid_rtt_cmd(&rd, ipa, level - 1)
75        {
76            return Err(Error::RmiErrorInput);
77        }
78        let (ipa, walk_top) = rtt::destroy(&rd, ipa, level, |t| {
79            ret[2] = t;
80        })?;
81        ret[1] = ipa;
82        ret[2] = walk_top;
83        Ok(())
84    });
85
86    listen!(rmi, rmi::RTT_INIT_RIPAS, |arg, ret, _rmm| {
87        let mut rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
88        let mut rd = rd_granule.content_mut::<Rd>()?;
89        let base = arg[1];
90        let top = arg[2];
91
92        if rd.state() != State::New {
93            return Err(Error::RmiErrorRealm(0));
94        }
95
96        if top <= base {
97            return Err(Error::RmiErrorInput);
98        }
99
100        if !is_valid_rtt_cmd(&rd, base, RTT_PAGE_LEVEL)
101            || !is_valid_rtt_cmd(&rd, top, RTT_PAGE_LEVEL)
102            || !rd.addr_in_par(base)
103            || !rd.addr_in_par(top - GRANULE_SIZE)
104        {
105            return Err(Error::RmiErrorInput);
106        }
107
108        let out_top = rtt::init_ripas(&mut rd, base, top)?;
109        ret[1] = out_top; //This is walk_top
110
111        Ok(())
112    });
113
114    listen!(rmi, rmi::RTT_SET_RIPAS, |arg, ret, _rmm| {
115        let base = arg[2];
116        let top = arg[3];
117
118        if arg[0] == arg[1] {
119            warn!("Granules of RD and REC shouldn't be identical");
120            return Err(Error::RmiErrorInput);
121        }
122        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
123        let rd = rd_granule.content::<Rd>()?;
124        let mut rec_granule = get_granule_if!(arg[1], GranuleState::Rec)?;
125        let mut rec = rec_granule.content_mut::<Rec<'_>>()?;
126        if rec.realmid()? != rd.id() {
127            warn!("RD:{:X} doesn't own REC:{:X}", arg[0], arg[1]);
128            return Err(Error::RmiErrorRec);
129        }
130
131        if rec.ripas_addr() != base as u64 || rec.ripas_end() < top as u64 {
132            return Err(Error::RmiErrorInput);
133        }
134
135        if !is_granule_aligned(base)
136            || !is_granule_aligned(top)
137            || !rd.addr_in_par(base)
138            || top.checked_sub(GRANULE_SIZE).is_none()
139            || !rd.addr_in_par(top - GRANULE_SIZE)
140        {
141            return Err(Error::RmiErrorInput);
142        }
143
144        let out_top = rtt::set_ripas(&rd, base, top, rec.ripas_state(), rec.ripas_flags())?;
145        ret[1] = out_top;
146        rec.set_ripas_addr(out_top as u64);
147        Ok(())
148    });
149
150    listen!(rmi, rmi::RTT_READ_ENTRY, |arg, ret, _rmm| {
151        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
152        let rd = rd_granule.content::<Rd>()?;
153        let ipa = arg[1];
154        let level = arg[2];
155        if !is_valid_rtt_cmd(&rd, ipa, level) {
156            return Err(Error::RmiErrorInput);
157        }
158
159        let res = rtt::read_entry(&rd, ipa, level)?;
160        ret[1..5].copy_from_slice(&res[0..4]);
161
162        Ok(())
163    });
164
165    listen!(rmi, rmi::DATA_CREATE, |arg, _ret, rmm| {
166        // target_pa: location where realm data is created.
167        let rd = arg[0];
168        let target_pa = arg[1];
169        let ipa = arg[2];
170        let src_pa = arg[3];
171        let flags = arg[4];
172
173        if target_pa == rd || target_pa == src_pa || rd == src_pa {
174            return Err(Error::RmiErrorInput);
175        }
176
177        // rd granule lock
178        let mut rd_granule = get_granule_if!(rd, GranuleState::RD)?;
179        let mut rd = rd_granule.content_mut::<Rd>()?;
180
181        // Make sure DATA_CREATE is only processed
182        // when the realm is in its New state.
183        if !rd.at_state(State::New) {
184            return Err(Error::RmiErrorRealm(0));
185        }
186
187        validate_ipa(&rd, ipa)?;
188
189        if !is_not_in_realm(src_pa) {
190            return Err(Error::RmiErrorInput);
191        };
192
193        // data granule lock for the target page
194        let mut target_page_granule = get_granule_if!(target_pa, GranuleState::Delegated)?;
195        let mut target_page = target_page_granule.content_mut::<DataPage>()?;
196        #[cfg(not(kani))]
197        // `page_table` is currently not reachable in model checking harnesses
198        rmm.page_table.map(target_pa, true);
199
200        // copy src to target
201        #[cfg(not(kani))]
202        rmm.page_table.map(src_pa, false);
203        host::copy_to_obj::<DataPage>(src_pa, &mut target_page).ok_or(Error::RmiErrorInput)?;
204        #[cfg(not(kani))]
205        rmm.page_table.unmap(src_pa);
206
207        // map ipa to taget_pa in S2 table
208        rtt::data_create(&rd, ipa, target_pa, false)?;
209
210        #[cfg(not(kani))]
211        // `rsi` is currently not reachable in model checking harnesses
212        HashContext::new(&mut rd)?.measure_data_granule(&target_page, ipa, flags)?;
213
214        set_granule(&mut target_page_granule, GranuleState::Data)?;
215        Ok(())
216    });
217
218    listen!(rmi, rmi::DATA_CREATE_UNKNOWN, |arg, _ret, rmm| {
219        // rd granule lock
220        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
221        let rd = rd_granule.content::<Rd>()?;
222
223        // target_phys: location where realm data is created.
224        let target_pa = arg[1];
225        let ipa = arg[2];
226        if target_pa == arg[0] {
227            return Err(Error::RmiErrorInput);
228        }
229
230        validate_ipa(&rd, ipa)?;
231
232        // 0. Make sure granule state can make a transition to DATA
233        // data granule lock for the target page
234        let mut target_page_granule = get_granule_if!(target_pa, GranuleState::Delegated)?;
235        #[cfg(not(kani))]
236        // `page_table` is currently not reachable in model checking harnesses
237        rmm.page_table.map(target_pa, true);
238
239        // 1. map ipa to target_pa in S2 table
240        rtt::data_create(&rd, ipa, target_pa, true)?;
241
242        // TODO: 2. perform measure
243        // L0czek - not needed here see: tf-rmm/runtime/rmi/rtt.c:883
244        set_granule(&mut target_page_granule, GranuleState::Data)?;
245        Ok(())
246    });
247
248    listen!(rmi, rmi::DATA_DESTROY, |arg, ret, _rmm| {
249        // rd granule lock
250        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
251        let rd = rd_granule.content::<Rd>()?;
252        let ipa = arg[1];
253
254        if !rd.addr_in_par(ipa) || !is_valid_rtt_cmd(&rd, ipa, RTT_PAGE_LEVEL) {
255            return Err(Error::RmiErrorInput);
256        }
257
258        let (pa, top) = rtt::data_destroy(&rd, ipa, |t| {
259            ret[2] = t;
260        })?;
261
262        // data granule lock and change state
263        #[cfg(feature = "gst_page_table")]
264        set_state_and_get_granule!(pa, GranuleState::Delegated)?;
265
266        #[cfg(not(feature = "gst_page_table"))]
267        {
268            let mut granule = get_granule!(pa)?;
269            set_granule(&mut granule, GranuleState::Delegated)?;
270        }
271
272        ret[1] = pa;
273        ret[2] = top;
274        Ok(())
275    });
276
277    // Map an unprotected IPA to a non-secure PA.
278    listen!(rmi, rmi::RTT_MAP_UNPROTECTED, |arg, _ret, _rmm| {
279        let ipa = arg[1];
280        let level = arg[2];
281        let host_s2tte = arg[3];
282        let s2tte = S2TTE::from(host_s2tte);
283        if !s2tte.is_host_ns_valid(level) {
284            return Err(Error::RmiErrorInput);
285        }
286
287        // rd granule lock
288        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
289        let rd = rd_granule.content::<Rd>()?;
290
291        if (level < rd.s2_starting_level() as usize)
292            || (level < RTT_MIN_BLOCK_LEVEL)
293            || (level > RTT_PAGE_LEVEL)
294            || !is_valid_rtt_cmd(&rd, ipa, level)
295        {
296            return Err(Error::RmiErrorInput);
297        }
298
299        rtt::map_unprotected(&rd, ipa, level, host_s2tte)?;
300        Ok(())
301    });
302
303    // Unmap a non-secure PA at an unprotected IPA
304    listen!(rmi, rmi::RTT_UNMAP_UNPROTECTED, |arg, ret, _rmm| {
305        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
306        let rd = rd_granule.content::<Rd>()?;
307
308        let ipa = arg[1];
309
310        let level = arg[2];
311        if (level < rd.s2_starting_level() as usize)
312            || (level < RTT_MIN_BLOCK_LEVEL)
313            || (level > RTT_PAGE_LEVEL)
314            || !is_valid_rtt_cmd(&rd, ipa, level)
315        {
316            return Err(Error::RmiErrorInput);
317        }
318
319        let top = rtt::unmap_unprotected(&rd, ipa, level, |t| {
320            ret[1] = t;
321        })?;
322        ret[1] = top;
323
324        Ok(())
325    });
326
327    // Destroy a homogeneous RTT and map as a bigger block at its parent RTT
328    listen!(rmi, rmi::RTT_FOLD, |arg, ret, _rmm| {
329        let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?;
330        let rd = rd_granule.content::<Rd>()?;
331        let ipa = arg[1];
332        let level = arg[2];
333
334        let min_level = rd.s2_starting_level() as usize + 1;
335
336        if (level < min_level) || (level > RTT_PAGE_LEVEL) || !is_valid_rtt_cmd(&rd, ipa, level - 1)
337        {
338            return Err(Error::RmiErrorInput);
339        }
340
341        let rtt = rtt::fold(&rd, ipa, level)?;
342        ret[1] = rtt;
343        Ok(())
344    });
345}
346
347pub fn validate_ipa(rd: &Rd, ipa: usize) -> Result<(), Error> {
348    if !is_granule_aligned(ipa) {
349        error!("ipa: {:x} is not aligned with {:x}", ipa, GRANULE_SIZE);
350        return Err(Error::RmiErrorInput);
351    }
352
353    if !rd.addr_in_par(ipa) {
354        error!(
355            "ipa: {:x} is not in protected ipa range {:x}",
356            ipa,
357            rd.par_size()
358        );
359        return Err(Error::RmiErrorInput);
360    }
361
362    if !is_valid_rtt_cmd(rd, ipa, RTT_PAGE_LEVEL) {
363        return Err(Error::RmiErrorInput);
364    }
365
366    Ok(())
367}
368
369#[cfg(test)]
370mod test {
371    use crate::granule::GRANULE_SIZE;
372    use crate::realm::rd::{Rd, State};
373    use crate::rmi::*;
374    use crate::test_utils::{mock, *};
375
376    use alloc::vec;
377
378    // Source: https://github.com/ARM-software/cca-rmm-acs
379    // Test Case: cmd_rtt_create
380    // Covered RMIs: RTT_CREATE, RTT_DESTROY, RTT_READ_ENTRY
381    #[test]
382    fn rmi_rtt_create_positive() {
383        let rd = realm_create();
384
385        let (rtt1, rtt2, rtt3, rtt4) = (
386            mock::host::alloc_granule(IDX_RTT_LEVEL1),
387            mock::host::alloc_granule(IDX_RTT_LEVEL2),
388            mock::host::alloc_granule(IDX_RTT_LEVEL3),
389            mock::host::alloc_granule(IDX_RTT_OTHER),
390        );
391
392        for rtt in &[rtt1, rtt2, rtt3, rtt4] {
393            let ret = rmi::<GRANULE_DELEGATE>(&[*rtt]);
394            assert_eq!(ret[0], SUCCESS);
395        }
396
397        let test_data = vec![
398            (rtt1, 0x0, 0x1),
399            (rtt2, 0x0, 0x2),
400            (rtt3, 0x0, 0x3),
401            (rtt4, 0x40000000, 0x2),
402        ];
403
404        unsafe {
405            let rd_obj = &*(rd as *const Rd);
406            assert!(rd_obj.at_state(State::New));
407        };
408
409        for (rtt, ipa, level) in &test_data {
410            let ret = rmi::<RTT_CREATE>(&[rd, *rtt, *ipa, *level]);
411            assert_eq!(ret[0], SUCCESS);
412        }
413
414        let (rtt4_ipa, rtt4_level) = (test_data[3].1, test_data[3].2);
415        let ret = rmi::<RTT_READ_ENTRY>(&[rd, rtt4_ipa, rtt4_level - 1]);
416        assert_eq!(ret[0], SUCCESS);
417
418        let (state, desc) = (ret[2], ret[3]);
419        const RMI_TABLE: usize = 2;
420        assert_eq!(state, RMI_TABLE);
421        assert_eq!(desc, rtt4);
422
423        for (_, ipa, level) in test_data.iter().rev() {
424            let ret = rmi::<RTT_DESTROY>(&[rd, *ipa, *level]);
425            assert_eq!(ret[0], SUCCESS);
426        }
427
428        for rtt in &[rtt1, rtt2, rtt3, rtt4] {
429            let ret = rmi::<GRANULE_UNDELEGATE>(&[*rtt]);
430            assert_eq!(ret[0], SUCCESS);
431        }
432
433        realm_destroy(rd);
434
435        miri_teardown();
436    }
437
438    // Source: https://github.com/ARM-software/cca-rmm-acs
439    // Test Case: cmd_rtt_init_ripas
440    // Covered RMIs: RTT_INIT_RIPAS, RTT_READ_ENTRY
441    #[test]
442    fn rmi_rtt_init_ripas_positive() {
443        let rd = realm_create();
444        let ipa = 0;
445        mock::host::map(rd, ipa);
446
447        let base = (ipa / L3_SIZE) * L3_SIZE;
448        let top = base + L3_SIZE;
449        let ret = rmi::<RTT_INIT_RIPAS>(&[rd, base, top]);
450        assert_eq!(ret[0], SUCCESS);
451        assert_eq!(ret[1], top);
452
453        let ret = rmi::<RTT_READ_ENTRY>(&[rd, ipa, MAP_LEVEL]);
454        assert_eq!(ret[0], SUCCESS);
455
456        let (level, ripas) = (ret[1], ret[4]);
457        const RMI_RAM: usize = 1;
458        assert_eq!(level, MAP_LEVEL);
459        assert_eq!(ripas, RMI_RAM);
460
461        mock::host::unmap(rd, ipa, false);
462
463        realm_destroy(rd);
464
465        miri_teardown();
466    }
467
468    // Source: https://github.com/ARM-software/cca-rmm-acs
469    // Test Case: cmd_rtt_map_unprotected
470    // Covered RMIs: RTT_MAP_UNPROTECTED, RTT_UNMAP_UNPROTECTED
471    #[test]
472    fn rmi_rtt_map_unprotected_positive() {
473        let rd = realm_create();
474
475        const IPA_ADDR_UNPROTECTED_UNASSIGNED: usize = (1 << (IPA_WIDTH - 1)) + L3_SIZE;
476        mock::host::map(rd, IPA_ADDR_UNPROTECTED_UNASSIGNED);
477
478        let ipa = IPA_ADDR_UNPROTECTED_UNASSIGNED;
479        let level = MAP_LEVEL;
480        let ns = mock::host::alloc_granule(IDX_NS_DESC);
481        let desc = ns | ATTR_NORMAL_WB_WA_RA | ATTR_STAGE2_AP_RW;
482
483        let ret = rmi::<RTT_MAP_UNPROTECTED>(&[rd, ipa, level, desc]);
484        assert_eq!(ret[0], SUCCESS);
485
486        let ret = rmi::<RTT_READ_ENTRY>(&[rd, ipa, level]);
487        assert_eq!(ret[0], SUCCESS);
488
489        let (_level, state, out_desc, _ripas) = (ret[1], ret[2], ret[3], ret[4]);
490        const RMI_ASSIGNED: usize = 1;
491        assert_eq!(state, RMI_ASSIGNED);
492        assert_eq!(out_desc, desc);
493
494        let ret = rmi::<RTT_UNMAP_UNPROTECTED>(&[rd, ipa, level]);
495        assert_eq!(ret[0], SUCCESS);
496
497        mock::host::unmap(rd, ipa, false);
498
499        realm_destroy(rd);
500
501        miri_teardown();
502    }
503
504    // Source: https://github.com/ARM-software/cca-rmm-acs
505    // Test Case: cmd_data_destroy
506    // Covered RMIs: DATA_CREATE, DATA_CREATE_UNKNOWN, DATA_DESTROY
507    #[test]
508    fn rmi_data_create_positive() {
509        let rd = realm_create();
510
511        const IPA_ADDR_ASSIGNED: usize = GRANULE_SIZE;
512        const IPA_ADDR_DATA: usize = GRANULE_SIZE * 3;
513        const IPA_ADDR_PROTECTED_ASSIGNED_EMPTY: usize = GRANULE_SIZE * 4;
514
515        const RMI_UNASSIGNED: usize = 0;
516        const RMI_DESTROYED: usize = 2;
517        const RMI_EMPTY: usize = 0;
518
519        data_create(rd, IPA_ADDR_ASSIGNED, IDX_DATA1, IDX_SRC1);
520
521        data_create(rd, IPA_ADDR_DATA, IDX_DATA2, IDX_SRC2);
522
523        let ipa = IPA_ADDR_ASSIGNED;
524        let ret = rmi::<DATA_DESTROY>(&[rd, ipa]);
525        assert_eq!(ret[0], SUCCESS);
526
527        let (data, top) = (ret[1], ret[2]);
528        assert_eq!(data, granule_addr(IDX_DATA1));
529        assert_eq!(top, IPA_ADDR_DATA);
530
531        // Check for RIPAS and HIPAS from RIPAS = RAM
532        let ret = rmi::<RTT_READ_ENTRY>(&[rd, ipa, MAP_LEVEL]);
533        assert_eq!(ret[0], SUCCESS);
534
535        let (_level, state, _desc, ripas) = (ret[1], ret[2], ret[3], ret[4]);
536        assert_eq!(state, RMI_UNASSIGNED);
537        assert_eq!(ripas, RMI_DESTROYED);
538
539        // Check for RIPAS transition from ASSIGNED,DESTROYED
540        let data = mock::host::alloc_granule(IDX_DATA3);
541        let ret = rmi::<GRANULE_DELEGATE>(&[data]);
542        assert_eq!(ret[0], SUCCESS);
543
544        let ret = rmi::<DATA_CREATE_UNKNOWN>(&[rd, data, ipa]);
545        assert_eq!(ret[0], SUCCESS);
546
547        let ret = rmi::<DATA_DESTROY>(&[rd, ipa]);
548        assert_eq!(ret[0], SUCCESS);
549
550        let ret = rmi::<RTT_READ_ENTRY>(&[rd, ipa, MAP_LEVEL]);
551        assert_eq!(ret[0], SUCCESS);
552
553        let (_level, state, _desc, ripas) = (ret[1], ret[2], ret[3], ret[4]);
554        assert_eq!(state, RMI_UNASSIGNED);
555        assert_eq!(ripas, RMI_DESTROYED);
556
557        // Check for RIPAS and HIPAS from RIPAS = EMPTY
558        let ipa = IPA_ADDR_PROTECTED_ASSIGNED_EMPTY;
559        let ret = rmi::<RTT_READ_ENTRY>(&[rd, ipa, MAP_LEVEL]);
560        assert_eq!(ret[0], SUCCESS);
561
562        mock::host::map(rd, ipa);
563
564        let data = mock::host::alloc_granule(IDX_DATA4);
565        let ret = rmi::<GRANULE_DELEGATE>(&[data]);
566        assert_eq!(ret[0], SUCCESS);
567
568        let ret = rmi::<DATA_CREATE_UNKNOWN>(&[rd, data, ipa]);
569        assert_eq!(ret[0], SUCCESS);
570
571        let ret = rmi::<RTT_READ_ENTRY>(&[rd, ipa, MAP_LEVEL]);
572        assert_eq!(ret[0], SUCCESS);
573
574        let ret = rmi::<DATA_DESTROY>(&[rd, ipa]);
575        assert_eq!(ret[0], SUCCESS);
576
577        let ret = rmi::<RTT_READ_ENTRY>(&[rd, ipa, MAP_LEVEL]);
578        assert_eq!(ret[0], SUCCESS);
579
580        let (_level, state, _desc, ripas) = (ret[1], ret[2], ret[3], ret[4]);
581        assert_eq!(state, RMI_UNASSIGNED);
582        assert_eq!(ripas, RMI_EMPTY);
583
584        let ret = rmi::<DATA_DESTROY>(&[rd, IPA_ADDR_DATA]);
585        assert_eq!(ret[0], SUCCESS);
586
587        // Cleanup
588        mock::host::unmap(rd, ipa, false);
589        for idx in IDX_DATA1..IDX_DATA4 + 1 {
590            let ret = rmi::<GRANULE_UNDELEGATE>(&[granule_addr(idx)]);
591            assert_eq!(ret[0], SUCCESS);
592        }
593
594        realm_destroy(rd);
595
596        miri_teardown();
597    }
598
599    // Source: https://github.com/ARM-software/cca-rmm-acs
600    // Test Case: cmd_rtt_fold
601    // Covered RMIs: RTT_FOLD
602    #[test]
603    fn rmi_rtt_fold_positive() {
604        let rd = realm_create();
605
606        const IPA_HOMOGENEOUS_RTT: usize = 0;
607        let ipa = IPA_HOMOGENEOUS_RTT;
608
609        mock::host::map(rd, ipa);
610
611        let base = ipa;
612        let top = ipa + L2_SIZE;
613        let ret = rmi::<RTT_INIT_RIPAS>(&[rd, base, top]);
614        assert_eq!(ret[0], SUCCESS);
615
616        // Save Parent rtte.addr for comparision
617        let ret = rmi::<RTT_READ_ENTRY>(&[rd, base, MAP_LEVEL - 1]);
618        let (_level, _state, parent_desc, _ripas) = (ret[1], ret[2], ret[3], ret[4]);
619        assert_eq!(ret[0], SUCCESS);
620
621        // Save fold.addr, fold.ripas for Positive Observability
622        let ret = rmi::<RTT_READ_ENTRY>(&[rd, base, MAP_LEVEL]);
623        let (_level, fold_state, fold_desc, fold_ripas) = (ret[1], ret[2], ret[3], ret[4]);
624        assert_eq!(ret[0], SUCCESS);
625
626        let ret = rmi::<RTT_FOLD>(&[rd, base, MAP_LEVEL]);
627        assert_eq!(ret[0], SUCCESS);
628        let out_rtt = ret[1];
629        assert_eq!(out_rtt, parent_desc);
630
631        // Compare rtte_addr, rtte_ripas in folded RTTE
632        let ret = rmi::<RTT_READ_ENTRY>(&[rd, base, MAP_LEVEL - 1]);
633        assert_eq!(ret[0], SUCCESS);
634
635        // Compare HIPAS, RIPAS and addr of parent RTTE to its child RTTE
636        let (_level, state, desc, ripas) = (ret[1], ret[2], ret[3], ret[4]);
637        assert_eq!(fold_state, state);
638        assert_eq!(fold_desc, desc);
639        assert_eq!(fold_ripas, ripas);
640
641        mock::host::unmap(rd, ipa, true);
642
643        realm_destroy(rd);
644
645        miri_teardown();
646    }
647
648    // Source: https://github.com/ARM-software/cca-rmm-acs
649    // Test Case: cmd_rtt_set_ripas
650    // Covered RMIs: RTT_SET_RIPAS
651    #[test]
652    fn rmi_rtt_set_ripas_positive() {
653        use crate::rmi::rec::run::Run;
654        use crate::rsi::PSCI_CPU_ON;
655
656        let rd = mock::host::realm_setup();
657
658        let (rec1, run1) = (granule_addr(IDX_REC1), granule_addr(IDX_REC1_RUN));
659        let ret = rmi::<REC_ENTER>(&[rec1, run1]);
660        assert_eq!(ret[0], SUCCESS);
661
662        let ipa = 0;
663        mock::host::map(rd, ipa);
664
665        unsafe {
666            let run = &*(run1 as *const Run);
667            let (base, top) = run.ripas();
668
669            let ret = rmi::<RTT_SET_RIPAS>(&[rd, rec1, base as usize, top as usize]);
670            assert_eq!(ret[0], SUCCESS);
671        }
672
673        mock::host::unmap(rd, ipa, false);
674        mock::host::realm_teardown(rd);
675
676        miri_teardown();
677    }
678}