Skip to content

Commit 5d508d6

Browse files
committed
feat(aarch64): add system register trapping and emulation
- Add CNTHCTL_EL2 register to GuestSystemRegisters - Implement handler for trapped MSR/MRS instructions - Add utility functions for system register exception handling - Update VCpu initialization to enable system register trapping - Replace PhantomData with actual IRQ fetching implementation
1 parent 1a0a682 commit 5d508d6

File tree

8 files changed

+230
-11
lines changed

8 files changed

+230
-11
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
log = "0.4.21"
8+
spin = "0.9"
89

910
aarch64-cpu = "9.3"
1011
smccc = "0.1.1"
@@ -14,6 +15,8 @@ numeric-enum-macro = "0.2"
1415
axerrno = "0.1.0"
1516
percpu = "0.1.4"
1617
crate_interface = "0.1"
17-
18+
lazy_static = { version = "1.5", features = ["spin_no_std"] }
19+
axhal = { path = "../../arceos/modules/axhal" }
1820
axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" }
1921
axvcpu = { git = "https://github.com/arceos-hypervisor/axvcpu.git", branch = "axvcpuhal" }
22+
aarch64_sysreg = "0.1.1"

src/context_frame.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ pub struct GuestSystemRegisters {
156156
cntv_ctl_el0: u32,
157157
cntp_tval_el0: u32,
158158
cntv_tval_el0: u32,
159+
pub cnthctl_el2: u64,
159160

160161
// vpidr and vmpidr
161162
vpidr_el2: u32,
@@ -219,6 +220,7 @@ impl GuestSystemRegisters {
219220
asm!("mrs {0:x}, CNTP_TVAL_EL0", out(reg) self.cntp_tval_el0);
220221
asm!("mrs {0:x}, CNTV_TVAL_EL0", out(reg) self.cntv_tval_el0);
221222
asm!("mrs {0}, CNTVCT_EL0", out(reg) self.cntvct_el0);
223+
asm!("mrs {0}, CNTHCTL_EL2", out(reg) self.cnthctl_el2);
222224
// MRS!("self.vpidr_el2, VPIDR_EL2, "x");
223225
asm!("mrs {0}, VMPIDR_EL2", out(reg) self.vmpidr_el2);
224226

@@ -262,6 +264,7 @@ impl GuestSystemRegisters {
262264
asm!("msr CNTV_CVAL_EL0, {0}", in(reg) self.cntv_cval_el0);
263265
asm!("msr CNTKCTL_EL1, {0:x}", in (reg) self.cntkctl_el1);
264266
asm!("msr CNTV_CTL_EL0, {0:x}", in (reg) self.cntv_ctl_el0);
267+
asm!("msr CNTHCTL_EL2, {0}", in(reg) self.cnthctl_el2);
265268
// The restoration of SP_EL0 is done in `exception_return_el2`,
266269
// which move the value from `self.ctx.sp_el0` to `SP_EL0`.
267270
// asm!("msr SP_EL0, {0}", in(reg) self.sp_el0);

src/exception.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use crate::exception_utils::{
99
exception_data_abort_access_reg, exception_data_abort_access_reg_width,
1010
exception_data_abort_access_width, exception_data_abort_handleable,
1111
exception_data_abort_is_permission_fault, exception_data_abort_is_translate_fault,
12-
exception_esr, exception_fault_addr, exception_next_instruction_step,
12+
exception_esr, exception_fault_addr, exception_next_instruction_step, exception_sysreg_addr,
13+
exception_sysreg_direction_write, exception_sysreg_gpr,
1314
};
1415
use crate::TrapFrame;
1516

@@ -94,6 +95,8 @@ pub fn handle_exception_sync(ctx: &mut TrapFrame) -> AxResult<AxVCpuExitReason>
9495
],
9596
})
9697
}
98+
Some(ESR_EL2::EC::Value::TrappedMsrMrs) => handle_system_register(ctx),
99+
// Some(ESR_EL2::EC::Value::InstrAbortLowerEL) => Ok(AxVCpuExitReason::Nothing),
97100
_ => {
98101
panic!(
99102
"handler not presents for EC_{} @ipa 0x{:x}, @pc 0x{:x}, @esr 0x{:x},
@@ -112,9 +115,53 @@ pub fn handle_exception_sync(ctx: &mut TrapFrame) -> AxResult<AxVCpuExitReason>
112115
}
113116
}
114117

118+
/*
119+
ESR EC:011000 IL, bit [25]:Instruction Length for synchronous exceptions. Possible values of this bit are: 0:16b 1:32b
120+
Op0, bits [21:20]
121+
Op2, bits [19:17]
122+
Op1, bits [16:14]
123+
CRn, bits [13:10]
124+
CRm, bits [4:1] This five parameters are for SYSREG
125+
126+
Rt, bits [9:5] general-purpose register used for the transfer.
127+
Direction, bit [0] 0:Write access, including MSR instructions. 1:Read access, including MRS instructions.
128+
129+
#define SYS_CNTFRQ_EL0 sys_reg(3, 3, 14, 0, 0)
130+
131+
#define SYS_CNTPCT_EL0 sys_reg(3, 3, 14, 0, 1)
132+
#define SYS_CNTPCTSS_EL0 sys_reg(3, 3, 14, 0, 5)
133+
#define SYS_CNTVCTSS_EL0 sys_reg(3, 3, 14, 0, 6)
134+
135+
#define SYS_CNTP_TVAL_EL0 sys_reg(3, 3, 14, 2, 0)
136+
#define SYS_CNTP_CTL_EL0 sys_reg(3, 3, 14, 2, 1)
137+
#define SYS_CNTP_CVAL_EL0 sys_reg(3, 3, 14, 2, 2)
138+
139+
#define SYS_CNTV_CTL_EL0 sys_reg(3, 3, 14, 3, 1)
140+
#define SYS_CNTV_CVAL_EL0 sys_reg(3, 3, 14, 3, 2)
141+
*/
142+
143+
fn handle_system_register(context_frame: &mut TrapFrame) -> AxResult<AxVCpuExitReason> {
144+
let iss = ESR_EL2.read(ESR_EL2::ISS);
145+
let addr = exception_sysreg_addr(iss.try_into().unwrap());
146+
let elr = context_frame.exception_pc();
147+
let val = elr + exception_next_instruction_step();
148+
let write = exception_sysreg_direction_write(iss);
149+
let reg = exception_sysreg_gpr(iss) as usize;
150+
context_frame.set_exception_pc(val);
151+
// TODO! gicv3 ICC_SRE_ADDR / ICC_SGIR_ADDR
152+
if write {
153+
return Ok(AxVCpuExitReason::SysregWrite {
154+
addr,
155+
reg,
156+
data: context_frame.gpr(reg as usize) as u64,
157+
});
158+
}
159+
Ok(AxVCpuExitReason::SysregRead { addr, reg })
160+
}
161+
115162
fn handle_data_abort(context_frame: &mut TrapFrame) -> AxResult<AxVCpuExitReason> {
116163
let addr = exception_fault_addr()?;
117-
debug!("data fault addr {:?}, esr: 0x{:x}", addr, exception_esr());
164+
trace!("data fault addr {:?}, esr: 0x{:x}", addr, exception_esr());
118165

119166
let access_width = exception_data_abort_access_width();
120167
let is_write = exception_data_abort_access_is_write();

src/exception_utils.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,27 @@ pub fn exception_iss() -> usize {
171171
ESR_EL2.read(ESR_EL2::ISS) as usize
172172
}
173173

174+
#[inline(always)]
175+
pub fn exception_sysreg_direction_write(iss: u64) -> bool {
176+
const ESR_ISS_SYSREG_DIRECTION: u64 = 0b1;
177+
(iss & ESR_ISS_SYSREG_DIRECTION) == 0
178+
}
179+
180+
#[inline(always)]
181+
pub fn exception_sysreg_gpr(iss: u64) -> u64 {
182+
const ESR_ISS_SYSREG_REG_OFF: u64 = 5;
183+
const ESR_ISS_SYSREG_REG_LEN: u64 = 5;
184+
const ESR_ISS_SYSREG_REG_MASK: u64 = (1 << ESR_ISS_SYSREG_REG_LEN) - 1;
185+
(iss >> ESR_ISS_SYSREG_REG_OFF) & ESR_ISS_SYSREG_REG_MASK
186+
}
187+
188+
#[inline(always)]
189+
pub const fn exception_sysreg_addr(iss: usize) -> usize {
190+
// (Op0[21..20] + Op2[19..17] + Op1[16..14] + CRn[13..10]) + CRm[4..1]
191+
const ESR_ISS_SYSREG_ADDR: usize = (0xfff << 10) | (0xf << 1);
192+
iss & ESR_ISS_SYSREG_ADDR
193+
}
194+
174195
/// Checks if the data abort exception was caused by a permission fault.
175196
///
176197
/// # Returns

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ mod context_frame;
1212
mod exception_utils;
1313
mod exception;
1414
mod pcpu;
15+
mod registers;
1516
mod vcpu;
1617

1718
pub use self::pcpu::Aarch64PerCpu;
19+
pub use self::registers::{emu_register_add, emu_register_handle_read, emu_register_handle_write};
1820
pub use self::vcpu::{Aarch64VCpu, Aarch64VCpuCreateConfig};
1921

2022
/// context frame for aarch64

src/registers.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
extern crate alloc;
2+
use crate::vcpu::Aarch64VCpu;
3+
use aarch64_cpu::registers::{Readable, Writeable};
4+
use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
5+
use aarch64_sysreg::SystemRegType;
6+
use alloc::sync::Arc;
7+
use alloc::{vec, vec::Vec};
8+
// pub use arm_gicv2::GicInterface;
9+
use axvcpu::AxVCpuHal;
10+
use axvcpu::{AxArchVCpu, AxVCpu};
11+
use lazy_static::lazy_static;
12+
use spin::RwLock;
13+
14+
type RegVcpu = Arc<AxVCpu<Aarch64VCpu>>;
15+
16+
/// Struct representing an entry in the emulator register list.
17+
pub struct EmuRegEntry {
18+
/// The type of the emulator register.
19+
pub emu_type: EmuRegType,
20+
/// The address associated with the emulator register.
21+
pub addr: SystemRegType,
22+
/// The handler write function for the emulator register.
23+
pub handle_write: fn(SystemRegType, usize, u64, RegVcpu) -> bool,
24+
/// The handler read function for the emulator register.
25+
pub handle_read: fn(SystemRegType, usize, RegVcpu) -> bool,
26+
}
27+
28+
/// Enumeration representing the type of emulator registers.
29+
pub enum EmuRegType {
30+
/// System register type for emulator registers.
31+
SysReg,
32+
}
33+
34+
pub fn emu_register_add(
35+
addr: SystemRegType,
36+
handle_write: fn(SystemRegType, usize, u64, RegVcpu) -> bool,
37+
handle_read: fn(SystemRegType, usize, RegVcpu) -> bool,
38+
) {
39+
let mut emu_reg = EMU_REGISTERS.write();
40+
for entry in emu_reg.iter() {
41+
if entry.addr == addr {
42+
error!("Register:{} already exists", addr);
43+
return;
44+
}
45+
}
46+
info!("Register:{} added", addr);
47+
emu_reg.push(EmuRegEntry {
48+
emu_type: EmuRegType::SysReg,
49+
addr,
50+
handle_write,
51+
handle_read,
52+
});
53+
}
54+
55+
pub fn emu_register_handle_write(
56+
addr: SystemRegType,
57+
reg: usize,
58+
value: u64,
59+
vcpu: RegVcpu,
60+
) -> bool {
61+
let emu_reg = EMU_REGISTERS.read();
62+
for entry in emu_reg.iter() {
63+
if entry.addr == addr {
64+
return (entry.handle_write)(addr, reg, value, vcpu);
65+
}
66+
}
67+
panic!("Invalid emulated register write: addr={}", addr);
68+
}
69+
70+
pub fn emu_register_handle_read(addr: SystemRegType, reg: usize, vcpu: RegVcpu) -> bool {
71+
let emu_reg = EMU_REGISTERS.read();
72+
for entry in emu_reg.iter() {
73+
if entry.addr == addr {
74+
return (entry.handle_read)(addr, reg, vcpu);
75+
}
76+
}
77+
panic!("Invalid emulated register read: addr={}", addr);
78+
}
79+
80+
fn handle_write(addr: SystemRegType, _reg: usize, value: u64, _vcpu: RegVcpu) -> bool {
81+
info!(
82+
"write to emulated register: addr: {}, value: {:x}",
83+
addr, value
84+
);
85+
false
86+
}
87+
fn handle_read(addr: SystemRegType, _reg: usize, _vcpu: RegVcpu) -> bool {
88+
info!("read from emulated register: addr: {}", addr);
89+
false
90+
}
91+
92+
lazy_static! {
93+
static ref EMU_REGISTERS: RwLock<Vec<EmuRegEntry>> = RwLock::new(vec![
94+
EmuRegEntry {
95+
emu_type: EmuRegType::SysReg,
96+
addr: SystemRegType::CNTPCT_EL0,
97+
handle_write: handle_write,
98+
handle_read: |_addr, reg, vcpu| {
99+
// Get the current value of CNTPCT_EL0
100+
// info!("Read CNTPCT_EL0");
101+
(*vcpu).set_gpr(reg, CNTPCT_EL0.get() as usize);
102+
true
103+
},
104+
},
105+
EmuRegEntry {
106+
emu_type: EmuRegType::SysReg,
107+
addr: SystemRegType::CNTP_TVAL_EL0,
108+
handle_write: |_addr, _reg, value, _vcpu| {
109+
info!("Write CNTP_TVAL_EL0 0x{:x}",value);
110+
CNTP_TVAL_EL0.set(value);
111+
true
112+
},
113+
handle_read: handle_read,
114+
},
115+
EmuRegEntry {
116+
emu_type: EmuRegType::SysReg,
117+
addr: SystemRegType::CNTP_CTL_EL0,
118+
handle_write: |_addr, _reg, value, _vcpu| {
119+
// CNTP_CTL_EL0.set(value);
120+
// CNTP_TVAL_EL0.set(value);
121+
// axhal::irq::register_handler(30, || {
122+
// info!("Timer Interrupt");
123+
// });
124+
info!("Set Timer Interrupt: {}", value);
125+
axhal::arch::enable_irqs();
126+
true
127+
},
128+
handle_read: handle_read,
129+
},
130+
EmuRegEntry {
131+
emu_type: EmuRegType::SysReg,
132+
addr: SystemRegType::CNTP_CVAL_EL0,
133+
handle_write: handle_write,
134+
handle_read: handle_read,
135+
},
136+
]);
137+
}

src/timer.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/vcpu.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,14 @@ pub struct VmCpuRegisters {
5757
/// A virtual CPU within a guest
5858
#[repr(C)]
5959
#[derive(Debug)]
60-
pub struct Aarch64VCpu<H: AxVCpuHal> {
60+
pub struct Aarch64VCpu {
6161
// DO NOT modify `guest_regs` and `host_stack_top` and their order unless you do know what you are doing!
6262
// DO NOT add anything before or between them unless you do know what you are doing!
6363
ctx: TrapFrame,
6464
host_stack_top: u64,
6565
guest_system_regs: GuestSystemRegisters,
6666
/// The MPIDR_EL1 value for the vCPU.
6767
mpidr: u64,
68-
_phantom: PhantomData<H>,
6968
}
7069

7170
/// Configuration for creating a new `Aarch64VCpu`
@@ -77,7 +76,7 @@ pub struct Aarch64VCpuCreateConfig {
7776
pub mpidr_el1: u64,
7877
}
7978

80-
impl<H: AxVCpuHal> axvcpu::AxArchVCpu for Aarch64VCpu<H> {
79+
impl axvcpu::AxArchVCpu for Aarch64VCpu {
8180
type CreateConfig = Aarch64VCpuCreateConfig;
8281

8382
type SetupConfig = ();
@@ -88,7 +87,6 @@ impl<H: AxVCpuHal> axvcpu::AxArchVCpu for Aarch64VCpu<H> {
8887
host_stack_top: 0,
8988
guest_system_regs: GuestSystemRegisters::default(),
9089
mpidr: config.mpidr_el1,
91-
_phantom: PhantomData,
9290
})
9391
}
9492

@@ -141,7 +139,7 @@ impl<H: AxVCpuHal> axvcpu::AxArchVCpu for Aarch64VCpu<H> {
141139
}
142140

143141
// Private function
144-
impl<H: AxVCpuHal> Aarch64VCpu<H> {
142+
impl Aarch64VCpu {
145143
fn init_hv(&mut self) {
146144
self.ctx.spsr = (SPSR_EL1::M::EL1h
147145
+ SPSR_EL1::I::Masked
@@ -168,7 +166,14 @@ impl<H: AxVCpuHal> Aarch64VCpu<H> {
168166
+ VTCR_EL2::SL0.val(0b01)
169167
+ VTCR_EL2::T0SZ.val(64 - 39))
170168
.into();
171-
self.guest_system_regs.hcr_el2 = (HCR_EL2::VM::Enable + HCR_EL2::RW::EL1IsAarch64).into();
169+
self.guest_system_regs.hcr_el2 = (HCR_EL2::VM::Enable
170+
+ HCR_EL2::RW::EL1IsAarch64
171+
// + HCR_EL2::IMO::EnableVirtualIRQ
172+
// + HCR_EL2::FMO::EnableVirtualFIQ
173+
+ HCR_EL2::TSC::EnableTrapEl1SmcToEl2)
174+
.into();
175+
self.guest_system_regs.cnthctl_el2 =
176+
(CNTHCTL_EL2::EL1PCEN::CLEAR + CNTHCTL_EL2::EL1PCTEN::CLEAR).into();
172177
// self.system_regs.hcr_el2 |= 1<<27;
173178
// + HCR_EL2::IMO::EnableVirtualIRQ).into();
174179
// trap el1 smc to el2
@@ -195,7 +200,7 @@ impl<H: AxVCpuHal> Aarch64VCpu<H> {
195200
}
196201

197202
/// Private functions related to vcpu runtime control flow.
198-
impl<H: AxVCpuHal> Aarch64VCpu<H> {
203+
impl Aarch64VCpu {
199204
/// Save host context and run guest.
200205
///
201206
/// When a VM-Exit happens when guest's vCpu is running,
@@ -277,7 +282,7 @@ impl<H: AxVCpuHal> Aarch64VCpu<H> {
277282
match exit_reason {
278283
TrapKind::Synchronous => handle_exception_sync(&mut self.ctx),
279284
TrapKind::Irq => Ok(AxVCpuExitReason::ExternalInterrupt {
280-
vector: H::irq_fecth() as _,
285+
vector: axhal::irq::fetch_irq() as _,
281286
}),
282287
_ => panic!("Unhandled exception {:?}", exit_reason),
283288
}

0 commit comments

Comments
 (0)