Skip to content

Commit 63aabef

Browse files
committed
feat(aarch64): implement system register emulation and IRQ handling
- Add system register emulation support for CNTP_TVAL_EL0 and CNTP_CTL_EL0 - Implement IRQ handler registration and dispatching - Update exception handling to use new system register emulator - Refactor Aarch64VCpu to support generic AxVCpuHal
2 parents 5d508d6 + df81de6 commit 63aabef

File tree

6 files changed

+180
-54
lines changed

6 files changed

+180
-54
lines changed

Cargo.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ edition = "2021"
66
[dependencies]
77
log = "0.4.21"
88
spin = "0.9"
9+
spin = "0.9"
10+
lazy_static = { version = "1.5", features = ["spin_no_std"] }
911

1012
aarch64-cpu = "9.3"
11-
smccc = "0.1.1"
1213
tock-registers = "0.8"
1314
numeric-enum-macro = "0.2"
1415

@@ -17,6 +18,9 @@ percpu = "0.1.4"
1718
crate_interface = "0.1"
1819
lazy_static = { version = "1.5", features = ["spin_no_std"] }
1920
axhal = { path = "../../arceos/modules/axhal" }
21+
aarch64_sysreg = "0.1.1"
22+
2023
axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" }
21-
axvcpu = { git = "https://github.com/arceos-hypervisor/axvcpu.git", branch = "axvcpuhal" }
22-
aarch64_sysreg = "0.1.1"
24+
axvcpu = { git = "https://github.com/arceos-hypervisor/axvcpu.git" }
25+
axhal = { git = "https://github.com/arceos-hypervisor/arceos.git", branch = "vmm" }
26+
axdevice = { git = "https://github.com/arceos-hypervisor/axdevice.git" }

src/exception.rs

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ pub fn handle_exception_sync(ctx: &mut TrapFrame) -> AxResult<AxVCpuExitReason>
9696
})
9797
}
9898
Some(ESR_EL2::EC::Value::TrappedMsrMrs) => handle_system_register(ctx),
99-
// Some(ESR_EL2::EC::Value::InstrAbortLowerEL) => Ok(AxVCpuExitReason::Nothing),
10099
_ => {
101100
panic!(
102101
"handler not presents for EC_{} @ipa 0x{:x}, @pc 0x{:x}, @esr 0x{:x},
@@ -115,31 +114,6 @@ pub fn handle_exception_sync(ctx: &mut TrapFrame) -> AxResult<AxVCpuExitReason>
115114
}
116115
}
117116

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-
143117
fn handle_system_register(context_frame: &mut TrapFrame) -> AxResult<AxVCpuExitReason> {
144118
let iss = ESR_EL2.read(ESR_EL2::ISS);
145119
let addr = exception_sysreg_addr(iss.try_into().unwrap());
@@ -150,13 +124,12 @@ fn handle_system_register(context_frame: &mut TrapFrame) -> AxResult<AxVCpuExitR
150124
context_frame.set_exception_pc(val);
151125
// TODO! gicv3 ICC_SRE_ADDR / ICC_SGIR_ADDR
152126
if write {
153-
return Ok(AxVCpuExitReason::SysregWrite {
127+
return Ok(AxVCpuExitReason::SysRegWrite {
154128
addr,
155-
reg,
156-
data: context_frame.gpr(reg as usize) as u64,
129+
value: context_frame.gpr(reg as usize) as u64,
157130
});
158131
}
159-
Ok(AxVCpuExitReason::SysregRead { addr, reg })
132+
Ok(AxVCpuExitReason::SysRegRead { addr, reg })
160133
}
161134

162135
fn handle_data_abort(context_frame: &mut TrapFrame) -> AxResult<AxVCpuExitReason> {
@@ -253,9 +226,12 @@ fn handle_psci_call(ctx: &mut TrapFrame) -> Option<AxResult<AxVCpuExitReason>> {
253226
})
254227
}
255228

256-
/// Dispatches IRQs to the appropriate handler provided by the underlying host OS.
229+
/// Dispatches IRQs to the appropriate handler provided by the underlying host OS,
230+
/// which is registered at [`crate::pcpu::IRQ_HANDLER`] during `Aarch64PerCpu::new()`.
257231
fn dispatch_irq() {
258-
crate_interface::call_interface!(crate::HalIf::irq_hanlder())
232+
unsafe { crate::pcpu::IRQ_HANDLER.current_ref_raw() }
233+
.get()
234+
.unwrap()()
259235
}
260236

261237
/// A trampoline function for handling exceptions (VM exits) in EL2.
@@ -276,11 +252,14 @@ fn dispatch_irq() {
276252
#[no_mangle]
277253
unsafe extern "C" fn vmexit_trampoline() {
278254
core::arch::asm!(
255+
"mov x6, x0", // Save the exit reason.
279256
"bl {vcpu_running}", // Check if vcpu is running.
257+
"mov x7, x0", // Save the return value of vcpu_running.
258+
"mov x0, x6", // Restore the exit reason.
280259
// If vcpu_running returns true, jump to `return_run_guest`,
281260
// after that the control flow is handed back to Aarch64VCpu.run(),
282261
// simulating the normal return of the `run_guest` function.
283-
"cbnz x0, {return_run_guest}",
262+
"cbnz x7, {return_run_guest}",
284263
// If vcpu_running returns false, there is no active vcpu running,
285264
// jump to `dispatch_irq`.
286265
"bl {dispatch_irq}",

src/lib.rs

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

1818
pub use self::pcpu::Aarch64PerCpu;
19-
pub use self::registers::{emu_register_add, emu_register_handle_read, emu_register_handle_write};
19+
pub use self::system_registers::Aarch64EmuRegs;
2020
pub use self::vcpu::{Aarch64VCpu, Aarch64VCpuCreateConfig};
2121

2222
/// context frame for aarch64

src/pcpu.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::marker::PhantomData;
1+
use core::{cell::OnceCell, marker::PhantomData};
22

33
use aarch64_cpu::registers::*;
44
use tock_registers::interfaces::ReadWriteable;
@@ -10,26 +10,34 @@ use axvcpu::{AxArchPerCpu, AxVCpuHal};
1010
#[repr(C)]
1111
#[repr(align(4096))]
1212
pub struct Aarch64PerCpu<H: AxVCpuHal> {
13-
//stack_top_addr has no use yet?
1413
/// per cpu id
1514
pub cpu_id: usize,
16-
/// context address of this cpu
17-
pub ctx: Option<usize>,
1815
_phantom: PhantomData<H>,
1916
}
2017

2118
#[percpu::def_percpu]
2219
static ORI_EXCEPTION_VECTOR_BASE: usize = 0;
2320

21+
/// IRQ handler registered by underlying host OS during per-cpu initialization,
22+
/// for dispatching IRQs to the host OS.
23+
///
24+
/// Set `IRQ_HANDLER` as per-cpu variable to avoid the need of `OnceLock`.
25+
#[percpu::def_percpu]
26+
pub static IRQ_HANDLER: OnceCell<&(dyn Fn() + Send + Sync)> = OnceCell::new();
27+
2428
extern "C" {
2529
fn exception_vector_base_vcpu();
2630
}
2731

2832
impl<H: AxVCpuHal> AxArchPerCpu for Aarch64PerCpu<H> {
2933
fn new(cpu_id: usize) -> AxResult<Self> {
34+
// Register IRQ handler for this CPU.
35+
let _ = unsafe { IRQ_HANDLER.current_ref_mut_raw() }
36+
.set(&|| H::irq_hanlder())
37+
.map(|_| {});
38+
3039
Ok(Self {
3140
cpu_id,
32-
ctx: None,
3341
_phantom: PhantomData,
3442
})
3543
}

src/system_registers.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
extern crate alloc;
2+
use crate::vcpu::Aarch64VCpu;
3+
use aarch64_cpu::registers::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;
8+
use axdevice::timer::{register_timer, VmmTimerEvent};
9+
use axvcpu::{AxVCpu, AxVCpuHal};
10+
use spin::RwLock;
11+
12+
use axhal::irq::MyVgic;
13+
14+
type RegVcpu<H> = Arc<AxVCpu<Aarch64VCpu<H>>>;
15+
16+
/// Struct representing an entry in the emulator register list.
17+
pub struct EmuRegEntry<H: AxVCpuHal> {
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, u64, RegVcpu<H>) -> bool,
24+
/// The handler read function for the emulator register.
25+
pub handle_read: fn(SystemRegType, usize, RegVcpu<H>) -> 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+
/// Struct representing the emulator registers.
35+
pub struct Aarch64EmuRegs<H: AxVCpuHal> {
36+
/// The list of emulator registers.
37+
pub emu_regs: RwLock<Vec<EmuRegEntry<H>>>,
38+
}
39+
40+
impl<H: AxVCpuHal> Aarch64EmuRegs<H> {
41+
const EMU_REGISTERS: [EmuRegEntry<H>; 2] = [
42+
EmuRegEntry {
43+
emu_type: EmuRegType::SysReg,
44+
addr: SystemRegType::CNTP_TVAL_EL0,
45+
handle_write: |addr, value, _| {
46+
info!("Write to emulator register: {:?}, value: {}", addr, value);
47+
// CNTP_TVAL_EL0.set(value);
48+
let now = axhal::time::monotonic_time_nanos();
49+
debug!("Current time: {}", now);
50+
register_timer(
51+
value + now,
52+
VmmTimerEvent::new(|_| {
53+
info!("Timer expired");
54+
let gich = MyVgic::get_gich();
55+
let hcr = gich.get_hcr();
56+
gich.set_hcr(hcr | 1 << 0);
57+
let mut lr = 0;
58+
lr |= 30 << 0;
59+
lr |= 1 << 19;
60+
lr |= 1 << 28;
61+
gich.set_lr(0, lr);
62+
}),
63+
);
64+
true
65+
},
66+
handle_read: |_, _, _| true,
67+
},
68+
EmuRegEntry {
69+
emu_type: EmuRegType::SysReg,
70+
addr: SystemRegType::CNTP_CTL_EL0,
71+
handle_write: |addr, value, _| {
72+
info!("Write to emulator register: {:?}, value: {}", addr, value);
73+
// if value & 0x1 != 0 {
74+
// axhal::irq::set_enable(30, true);
75+
// } else {
76+
// axhal::irq::set_enable(30, false);
77+
// }
78+
79+
// let gich = MyVgic::get_gich();
80+
// let gicd = MyVgic::get_gicd();
81+
// let gicc = MyVgic::get_gicv();
82+
// CNTP_CTL_EL0.set(value);
83+
// let hcr = gich.get_hcr();
84+
// gich.set_hcr(hcr | 1 << 0);
85+
// let mut lr = 0;
86+
// lr |= 30 << 0;
87+
// lr |= 1 << 19;
88+
// lr |= 1 << 28;
89+
// gich.set_lr(0, lr);
90+
// gicd.lock().set_pend(30, true, 0);
91+
// gicc.set_ctlr(ctlr);
92+
93+
true
94+
},
95+
handle_read: |_, _, _| true,
96+
},
97+
];
98+
99+
/// Handle a write to an emulator register.
100+
pub fn emu_register_handle_write(addr: SystemRegType, value: u64, vcpu: RegVcpu<H>) -> bool {
101+
let emu_reg = Self::EMU_REGISTERS;
102+
103+
for entry in emu_reg.iter() {
104+
if entry.addr == addr {
105+
return (entry.handle_write)(addr, value, vcpu);
106+
}
107+
}
108+
error!("Invalid emulated register write: {}", addr);
109+
false
110+
}
111+
112+
/// Handle a read from an emulator register.
113+
pub fn emu_register_handle_read(addr: SystemRegType, reg: usize, vcpu: RegVcpu<H>) -> bool {
114+
let emu_reg = Self::EMU_REGISTERS;
115+
116+
for entry in emu_reg.iter() {
117+
if entry.addr == addr {
118+
return (entry.handle_read)(addr, reg, vcpu);
119+
}
120+
}
121+
error!("Invalid emulated register read: {}", addr);
122+
false
123+
}
124+
}

src/vcpu.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,15 @@ pub struct VmCpuRegisters {
5757
/// A virtual CPU within a guest
5858
#[repr(C)]
5959
#[derive(Debug)]
60-
pub struct Aarch64VCpu {
60+
pub struct Aarch64VCpu<H: AxVCpuHal> {
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>,
6869
}
6970

7071
/// Configuration for creating a new `Aarch64VCpu`
@@ -76,7 +77,7 @@ pub struct Aarch64VCpuCreateConfig {
7677
pub mpidr_el1: u64,
7778
}
7879

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

8283
type SetupConfig = ();
@@ -87,6 +88,7 @@ impl axvcpu::AxArchVCpu for Aarch64VCpu {
8788
host_stack_top: 0,
8889
guest_system_regs: GuestSystemRegisters::default(),
8990
mpidr: config.mpidr_el1,
91+
_phantom: PhantomData,
9092
})
9193
}
9294

@@ -136,10 +138,14 @@ impl axvcpu::AxArchVCpu for Aarch64VCpu {
136138
fn set_gpr(&mut self, idx: usize, val: usize) {
137139
self.ctx.set_gpr(idx, val);
138140
}
141+
142+
fn inject_interrupt(&mut self, vector: usize) -> AxResult {
143+
Ok(())
144+
}
139145
}
140146

141147
// Private function
142-
impl Aarch64VCpu {
148+
impl<H: AxVCpuHal> Aarch64VCpu<H> {
143149
fn init_hv(&mut self) {
144150
self.ctx.spsr = (SPSR_EL1::M::EL1h
145151
+ SPSR_EL1::I::Masked
@@ -168,12 +174,11 @@ impl Aarch64VCpu {
168174
.into();
169175
self.guest_system_regs.hcr_el2 = (HCR_EL2::VM::Enable
170176
+ HCR_EL2::RW::EL1IsAarch64
171-
// + HCR_EL2::IMO::EnableVirtualIRQ
172-
// + HCR_EL2::FMO::EnableVirtualFIQ
173-
+ HCR_EL2::TSC::EnableTrapEl1SmcToEl2)
177+
+ HCR_EL2::IMO::EnableVirtualIRQ
178+
+ HCR_EL2::FMO::EnableVirtualFIQ
179+
+ HCR_EL2::TSC::EnableTrapEl1SmcToEl2
180+
+ HCR_EL2::RW::EL1IsAarch64)
174181
.into();
175-
self.guest_system_regs.cnthctl_el2 =
176-
(CNTHCTL_EL2::EL1PCEN::CLEAR + CNTHCTL_EL2::EL1PCTEN::CLEAR).into();
177182
// self.system_regs.hcr_el2 |= 1<<27;
178183
// + HCR_EL2::IMO::EnableVirtualIRQ).into();
179184
// trap el1 smc to el2
@@ -200,7 +205,7 @@ impl Aarch64VCpu {
200205
}
201206

202207
/// Private functions related to vcpu runtime control flow.
203-
impl Aarch64VCpu {
208+
impl<H: AxVCpuHal> Aarch64VCpu<H> {
204209
/// Save host context and run guest.
205210
///
206211
/// When a VM-Exit happens when guest's vCpu is running,
@@ -228,7 +233,13 @@ impl Aarch64VCpu {
228233
);
229234

230235
// the dummy return value, the real return value is in x0 when `return_run_guest` returns
231-
0
236+
let exit_reason: usize;
237+
core::arch::asm!(
238+
"mov {}, x0",
239+
out(reg) exit_reason,
240+
options(nostack)
241+
);
242+
exit_reason
232243
}
233244

234245
/// Restores guest system control registers.
@@ -282,7 +293,7 @@ impl Aarch64VCpu {
282293
match exit_reason {
283294
TrapKind::Synchronous => handle_exception_sync(&mut self.ctx),
284295
TrapKind::Irq => Ok(AxVCpuExitReason::ExternalInterrupt {
285-
vector: axhal::irq::fetch_irq() as _,
296+
vector: H::irq_fetch() as _,
286297
}),
287298
_ => panic!("Unhandled exception {:?}", exit_reason),
288299
}

0 commit comments

Comments
 (0)