Skip to main content

islet_rmm/rsi/
psci.rs

1use crate::event::RsiHandle;
2use crate::granule::GranuleState;
3use crate::listen;
4use crate::realm::rd::{Rd, State};
5use crate::rec::context::{get_reg, set_reg, RegOffset};
6use crate::rec::Rec;
7use crate::rmi;
8use crate::rmi::error::Error;
9use crate::rmi::rec::mpidr::MPIDR;
10use crate::rsi;
11use crate::{get_granule, get_granule_if};
12
13struct PsciReturn;
14impl PsciReturn {
15    const SUCCESS: usize = 0;
16    const AFFINITY_INFO_ON: usize = 0;
17    const AFFINITY_INFO_OFF: usize = 1;
18    const NOT_SUPPORTED: usize = !0;
19    const INVALID_PARAMS: usize = !1;
20    const DENIED: usize = !2;
21    const ALREADY_ON: usize = !3;
22    //const ON_PENDING: usize = !4;
23    //const INTERNAL_FAILURE: usize = !5;
24    //const NOT_PRESENT: usize = !6; // UL(-7)
25    //const DISABLED: usize = !7; // UL(-8);
26    const INVALID_ADDRESS: usize = !8; //UL(-9);
27}
28
29const SMCCC_MAJOR_VERSION: usize = 1;
30const SMCCC_MINOR_VERSION: usize = 2;
31
32const PSCI_MAJOR_VERSION: usize = 1;
33const PSCI_MINOR_VERSION: usize = 1;
34
35extern crate alloc;
36
37pub fn set_event_handler(rsi: &mut RsiHandle) {
38    listen!(rsi, rsi::PSCI_VERSION, |_arg, ret, _rmm, rec, _run| {
39        if set_reg(rec, 0, psci_version()).is_err() {
40            warn!("Unable to set register 0. rec: {:?}", rec);
41        }
42        ret[0] = rmi::SUCCESS_REC_ENTER;
43        Ok(())
44    });
45
46    listen!(rsi, rsi::PSCI_CPU_ON, |_arg, ret, _rmm, rec, run| {
47        let rd = get_granule_if!(rec.owner()?, GranuleState::RD)?;
48        let rd = rd.content::<Rd>()?;
49
50        let target_mpidr = get_reg(rec, 1)? as u64;
51        let entry_addr = get_reg(rec, 2)?;
52
53        if !rd.addr_in_par(entry_addr) {
54            set_reg(rec, 0, PsciReturn::INVALID_ADDRESS)?;
55            ret[0] = rmi::SUCCESS_REC_ENTER;
56            return Ok(());
57        }
58
59        let target_index = MPIDR::from(target_mpidr).index();
60        if target_index >= rd.rec_index() {
61            set_reg(rec, 0, PsciReturn::INVALID_PARAMS)?;
62            ret[0] = rmi::SUCCESS_REC_ENTER;
63            return Ok(());
64        }
65        if target_index == rec.vcpuid() {
66            set_reg(rec, 0, PsciReturn::ALREADY_ON)?;
67            ret[0] = rmi::SUCCESS_REC_ENTER;
68            return Ok(());
69        }
70
71        rec.set_psci_pending(true);
72        run.set_exit_reason(rmi::EXIT_PSCI);
73        run.set_gpr(0, rsi::PSCI_CPU_ON as u64)?;
74        run.set_gpr(1, target_mpidr)?;
75        // set 0 for the rest of gprs
76        ret[0] = rmi::SUCCESS;
77        Ok(())
78    });
79
80    listen!(rsi, rsi::PSCI_CPU_OFF, |_arg, ret, _rmm, rec, run| {
81        rec.set_runnable(0);
82        run.set_exit_reason(rmi::EXIT_PSCI);
83        run.set_gpr(0, rsi::PSCI_CPU_OFF as u64)?;
84        ret[0] = rmi::SUCCESS;
85        Ok(())
86    });
87
88    listen!(rsi, rsi::PSCI_SYSTEM_OFF, |_arg, ret, _rmm, rec, run| {
89        let mut rd = get_granule_if!(rec.owner()?, GranuleState::RD)?;
90        let mut rd = rd.content_mut::<Rd>()?;
91        rd.set_state(State::SystemOff);
92        run.set_exit_reason(rmi::EXIT_PSCI);
93        run.set_gpr(0, rsi::PSCI_SYSTEM_OFF as u64)?;
94        ret[0] = rmi::SUCCESS;
95        Ok(())
96    });
97
98    listen!(rsi, rsi::PSCI_CPU_SUSPEND, |_arg, ret, _rmm, rec, run| {
99        let _power_state = get_reg(rec, 1)? as u64;
100        let _entry_addr = get_reg(rec, 2)?;
101        let _context_id = get_reg(rec, 3)?;
102
103        run.set_exit_reason(rmi::EXIT_PSCI);
104        run.set_gpr(0, rsi::PSCI_CPU_SUSPEND as u64)?;
105        // set 0 for the rest of gprs
106        ret[0] = rmi::SUCCESS;
107        Ok(())
108    });
109
110    listen!(rsi, rsi::PSCI_SYSTEM_RESET, |_arg, ret, _rmm, rec, run| {
111        let mut rd = get_granule_if!(rec.owner()?, GranuleState::RD)?;
112        let mut rd = rd.content_mut::<Rd>()?;
113        rd.set_state(State::SystemOff);
114        run.set_exit_reason(rmi::EXIT_PSCI);
115        run.set_gpr(0, rsi::PSCI_SYSTEM_RESET as u64)?;
116        ret[0] = rmi::SUCCESS;
117        Ok(())
118    });
119
120    listen!(rsi, rsi::PSCI_AFFINITY_INFO, |_arg, ret, _rmm, rec, run| {
121        #[cfg(feature = "gst_page_table")]
122        let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
123
124        let affinity = get_reg(rec, 1)? as u64;
125        let lowest_level = get_reg(rec, 2)?;
126
127        if lowest_level != 0 {
128            set_reg(rec, 0, PsciReturn::INVALID_PARAMS)?;
129            ret[0] = rmi::SUCCESS_REC_ENTER;
130            return Ok(());
131        }
132
133        let target_index = MPIDR::from(affinity).index();
134        // check if target_index is in the range
135        #[cfg(feature = "gst_page_table")]
136        if target_index >= rd_granule.num_children() {
137            set_reg(rec, 0, PsciReturn::INVALID_PARAMS)?;
138            ret[0] = rmi::SUCCESS_REC_ENTER;
139            return Ok(());
140        }
141
142        if target_index == rec.vcpuid() {
143            set_reg(rec, 0, PsciReturn::SUCCESS)?;
144            ret[0] = rmi::SUCCESS_REC_ENTER;
145            return Ok(());
146        }
147
148        rec.set_psci_pending(true);
149        run.set_exit_reason(rmi::EXIT_PSCI);
150        run.set_gpr(0, rsi::PSCI_AFFINITY_INFO as u64)?;
151        run.set_gpr(1, affinity)?;
152        // set 0 for the rest of gprs
153        ret[0] = rmi::SUCCESS;
154        Ok(())
155    });
156
157    listen!(rsi, rsi::PSCI_FEATURES, |_arg, ret, _rmm, rec, _run| {
158        let feature_id = get_reg(rec, 1)?;
159        let retval = match feature_id {
160            rsi::SMCCC_VERSION //XXX: this should be added for realm-linux booting
161            | rsi::PSCI_CPU_SUSPEND
162            | rsi::PSCI_CPU_OFF
163            | rsi::PSCI_CPU_ON
164            | rsi::PSCI_AFFINITY_INFO
165            | rsi::PSCI_SYSTEM_OFF
166            | rsi::PSCI_SYSTEM_RESET
167            | rsi::PSCI_FEATURES
168            | rsi::PSCI_VERSION => PsciReturn::SUCCESS,
169            _ => PsciReturn::NOT_SUPPORTED,
170        };
171        if set_reg(rec, 0, retval).is_err() {
172            warn!("Unable to set register 0. rec: {:?}", rec);
173        }
174        ret[0] = rmi::SUCCESS_REC_ENTER;
175        Ok(())
176    });
177
178    listen!(rsi, rsi::SMCCC_VERSION, |_arg, ret, _rmm, rec, _run| {
179        if set_reg(rec, 0, smccc_version()).is_err() {
180            warn!("Unable to set register 0. rec: {:?}", rec);
181        }
182        ret[0] = rmi::SUCCESS_REC_ENTER;
183        Ok(())
184    });
185}
186
187fn psci_version() -> usize {
188    (PSCI_MAJOR_VERSION << 16) | PSCI_MINOR_VERSION
189}
190
191fn smccc_version() -> usize {
192    (SMCCC_MAJOR_VERSION << 16) | SMCCC_MINOR_VERSION
193}
194
195fn return_code_permitted(
196    caller: &mut Rec<'_>,
197    target: &mut Rec<'_>,
198    status: usize,
199) -> Result<(), Error> {
200    if status == PsciReturn::SUCCESS {
201        return Ok(());
202    }
203
204    let command = get_reg(caller, 0)?;
205    if command == rsi::PSCI_CPU_ON && !target.runnable() && status == PsciReturn::DENIED {
206        return Ok(());
207    }
208    Err(Error::RmiErrorInput)
209}
210
211pub fn complete_psci(
212    caller: &mut Rec<'_>,
213    target: &mut Rec<'_>,
214    status: usize,
215) -> Result<(), Error> {
216    let target_vcpuid = target.vcpuid();
217
218    let target_mpidr = get_reg(caller, 1)? as u64;
219    // TODO: check the below again
220    if MPIDR::from(target_mpidr).index() != target_vcpuid {
221        return Err(Error::RmiErrorInput);
222    }
223
224    return_code_permitted(caller, target, status)?;
225
226    let command = get_reg(caller, 0)?;
227
228    let psci_ret = match command {
229        rsi::PSCI_CPU_ON if target.runnable() => PsciReturn::ALREADY_ON,
230        rsi::PSCI_CPU_ON if status == PsciReturn::DENIED => PsciReturn::DENIED,
231        rsi::PSCI_CPU_ON => {
232            let entry_point = get_reg(caller, 2)?;
233            let context_id = get_reg(caller, 3)?;
234            target.reset_ctx();
235            set_reg(target, 0, context_id)?;
236            set_reg(target, RegOffset::PC, entry_point)?;
237            target.set_runnable(1);
238            PsciReturn::SUCCESS
239        }
240        rsi::PSCI_AFFINITY_INFO if target.runnable() => PsciReturn::AFFINITY_INFO_ON,
241        rsi::PSCI_AFFINITY_INFO => PsciReturn::AFFINITY_INFO_OFF,
242        _ => PsciReturn::NOT_SUPPORTED,
243    };
244
245    set_reg(caller, 0, psci_ret)?;
246    set_reg(caller, 1, 0)?;
247    set_reg(caller, 2, 0)?;
248    set_reg(caller, 3, 0)?;
249    caller.set_psci_pending(false);
250    Ok(())
251}