Alioth Code Coverage

vmexit.rs0.00%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::atomic::Ordering;
16
17use snafu::ResultExt;
18
19use crate::arch::psci::{PSCI_VERSION_1_1, PsciAffinityInfo, PsciErr, PsciFunc, PsciMigrateInfo};
20use crate::arch::reg::{EsrEl2DataAbort, EsrEl2Ec, EsrEl2SysReg, MpidrEl1, Reg, SReg, encode};
21use crate::hv::hvf::check_ret;
22use crate::hv::hvf::vcpu::HvfVcpu;
23use crate::hv::hvf::vm::VcpuEvent;
24use crate::hv::{Result, Vcpu, VmExit, error};
25use crate::sys::hvf::{HvReg, HvVcpuExitException, hv_vcpu_get_reg};
26
27impl HvfVcpu {
28 // https://esr.arm64.dev/
29 pub fn handle_exception(&mut self, exception: &HvVcpuExitException) -> Result<()> {
30 let esr = exception.syndrome;
31 match esr.ec() {
32 EsrEl2Ec::DATA_ABORT_LOWER => {
33 self.decode_data_abort(EsrEl2DataAbort(esr.iss()), exception.physical_address)
34 }
35 EsrEl2Ec::HVC_64 => self.handle_hvc(),
36 EsrEl2Ec::SYS_REG_64 => self.handle_sys_reg(EsrEl2SysReg(esr.iss())),
37 _ => error::VmExit {
38 msg: format!("Unhandled ESR: {esr:x?}"),
39 }
40 .fail(),
41 }
42 }
43
44 pub fn decode_data_abort(&mut self, iss: EsrEl2DataAbort, gpa: u64) -> Result<()> {
45 if !iss.isv() {
46 return error::VmExit {
47 msg: format!("Unhandled iss: {iss:x?}"),
48 }
49 .fail();
50 }
51 let srt = iss.srt();
52 let reg = HvReg::from(srt);
53 let write = if iss.wnr() {
54 let mut value = 0;
55 // On aarch64, register index of 31 corresponds to the zero register
56 // in the context of memory access.
57 if srt != 31 {
58 let ret = unsafe { hv_vcpu_get_reg(self.vcpu_id, reg, &mut value) };
59 check_ret(ret).context(error::VcpuReg)?;
60 }
61 Some(value)
62 } else {
63 self.exit_reg = Some(reg);
64 None
65 };
66 self.vmexit = Some(VmExit::Mmio {
67 addr: gpa as _,
68 write,
69 size: 1 << iss.sas(),
70 });
71 self.advance_pc()
72 }
73
74 pub fn handle_hvc(&mut self) -> Result<()> {
75 let func = self.get_reg(Reg::X0)?;
76 let ret = match PsciFunc::from(func as u32) {
77 PsciFunc::PSCI_VERSION => PSCI_VERSION_1_1 as u64,
78 PsciFunc::MIGRATE_INFO_TYPE => PsciMigrateInfo::NOT_REQUIRED.raw() as u64,
79 PsciFunc::PSCI_FEATURES => {
80 let f = self.get_reg(Reg::X1)?;
81 match PsciFunc::from(f as u32) {
82 PsciFunc::PSCI_VERSION
83 | PsciFunc::MIGRATE_INFO_TYPE
84 | PsciFunc::PSCI_FEATURES
85 | PsciFunc::SYSTEM_OFF
86 | PsciFunc::SYSTEM_OFF2_32
87 | PsciFunc::SYSTEM_OFF2_64
88 | PsciFunc::CPU_ON_32
89 | PsciFunc::CPU_ON_64
90 | PsciFunc::CPU_OFF
91 | PsciFunc::AFFINITY_INFO_32
92 | PsciFunc::AFFINITY_INFO_64
93 | PsciFunc::SYSTEM_RESET
94 | PsciFunc::SYSTEM_RESET2_32
95 | PsciFunc::SYSTEM_RESET2_64 => 0,
96 _ => u64::MAX,
97 }
98 }
99 PsciFunc::SYSTEM_OFF | PsciFunc::SYSTEM_OFF2_32 | PsciFunc::SYSTEM_OFF2_64 => {
100 self.vmexit = Some(VmExit::Shutdown);
101 return Ok(());
102 }
103 PsciFunc::CPU_ON_32 | PsciFunc::CPU_ON_64 => {
104 let mpidr = self.get_reg(Reg::X1)?;
105 let pc = self.get_reg(Reg::X2)?;
106 let context = self.get_reg(Reg::X3)?;
107 if let Some(vcpu) = self.vcpus.lock().get(&MpidrEl1(mpidr)) {
108 vcpu.sender.send(VcpuEvent::PowerOn { pc, context })?;
109 0
110 } else {
111 log::error!("Failed to find CPU with mpidr {mpidr:#x}");
112 u64::MAX
113 }
114 }
115 PsciFunc::SYSTEM_RESET | PsciFunc::SYSTEM_RESET2_32 | PsciFunc::SYSTEM_RESET2_64 => {
116 self.vmexit = Some(VmExit::Reboot);
117 return Ok(());
118 }
119 PsciFunc::AFFINITY_INFO_32 | PsciFunc::AFFINITY_INFO_64 => self.psci_affinity_info()?,
120 PsciFunc::CPU_OFF => self.psci_cpu_off()?,
121 f => {
122 return error::VmExit {
123 msg: format!("HVC: {f:x?}"),
124 }
125 .fail();
126 }
127 };
128 self.set_regs(&[(Reg::X0, ret)])
129 }
130
131 pub fn handle_sys_reg(&mut self, iss: EsrEl2SysReg) -> Result<()> {
132 if iss.is_read() {
133 return error::VmExit {
134 msg: format!("Unhandled iss: {iss:x?}"),
135 }
136 .fail();
137 }
138 let rt = HvReg::from(iss.rt());
139 let mut val = 0;
140 let ret = unsafe { hv_vcpu_get_reg(self.vcpu_id, rt, &mut val) };
141 check_ret(ret).context(error::VcpuReg)?;
142 let sreg = SReg::from(encode(
143 iss.op0(),
144 iss.op1(),
145 iss.crn(),
146 iss.crm(),
147 iss.op2(),
148 ));
149 if sreg == SReg::OSDLR_EL1 || sreg == SReg::OSLAR_EL1 {
150 log::warn!("vCPU-{} wrote {val:#x} to {sreg:?}", self.vcpu_id);
151 self.advance_pc()?;
152 return Ok(());
153 }
154 error::VmExit {
155 msg: format!("Unhandled iss: {iss:x?}, sreg: {sreg:?}"),
156 }
157 .fail()
158 }
159
160 fn psci_affinity_info(&mut self) -> Result<u64> {
161 let lowest_affinity_level = self.get_reg(Reg::X2)?;
162 if lowest_affinity_level != 0 {
163 // PSCI 1.0 and later no longer requires AFFINITY_INFO to
164 // support affinity levels greater than 0.
165 return Ok(PsciErr::INVALID_PARAMETERS.raw() as u64);
166 }
167 let target_affinity = self.get_reg(Reg::X1)?;
168 let vcpus = self.vcpus.lock();
169 let Some(vcpu) = vcpus.get(&MpidrEl1(target_affinity)) else {
170 return Ok(PsciErr::INVALID_PARAMETERS.raw() as u64);
171 };
172 let info = if vcpu.power_on.load(Ordering::Relaxed) {
173 PsciAffinityInfo::ON
174 } else {
175 PsciAffinityInfo::OFF
176 };
177 Ok(info.raw())
178 }
179
180 fn psci_cpu_off(&mut self) -> Result<u64> {
181 self.power_on.store(false, Ordering::Relaxed);
182 match self.receiver.recv()? {
183 VcpuEvent::PowerOn { pc, context } => {
184 self.power_on(pc, context)?;
185 Ok(context)
186 }
187 VcpuEvent::Interrupt => {
188 self.vmexit = Some(VmExit::Interrupted);
189 Ok(0)
190 }
191 }
192 }
193}
194