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 INVALID_ADDRESS: usize = !8; }
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 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 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 #[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 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 | 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 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}