vmexit.rs0.00%
1
// Copyright 2024 Google LLC2
//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 at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// 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 and13
// limitations under the License.14
15
use std::sync::atomic::Ordering;16
17
use snafu::ResultExt;18
19
use crate::arch::psci::{PSCI_VERSION_1_1, PsciAffinityInfo, PsciErr, PsciFunc, PsciMigrateInfo};20
use crate::arch::reg::{EsrEl2DataAbort, EsrEl2Ec, EsrEl2SysReg, MpidrEl1, Reg, SReg, encode};21
use crate::hv::hvf::check_ret;22
use crate::hv::hvf::vcpu::HvfVcpu;23
use crate::hv::hvf::vm::VcpuEvent;24
use crate::hv::{Result, Vcpu, VmExit, error};25
use crate::sys::hvf::{HvReg, HvVcpuExitException, hv_vcpu_get_reg};26
27
impl 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 register56
// 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
None65
};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_VERSION83
| PsciFunc::MIGRATE_INFO_TYPE84
| PsciFunc::PSCI_FEATURES85
| PsciFunc::SYSTEM_OFF86
| PsciFunc::SYSTEM_OFF2_3287
| PsciFunc::SYSTEM_OFF2_6488
| PsciFunc::CPU_ON_3289
| PsciFunc::CPU_ON_6490
| PsciFunc::CPU_OFF91
| PsciFunc::AFFINITY_INFO_3292
| PsciFunc::AFFINITY_INFO_6493
| PsciFunc::SYSTEM_RESET94
| PsciFunc::SYSTEM_RESET2_3295
| 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
0110
} else {111
log::error!("Failed to find CPU with mpidr {mpidr:#x}");112
u64::MAX113
}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 to164
// 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::ON174
} else {175
PsciAffinityInfo::OFF176
};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