Skip to content
Open
5 changes: 3 additions & 2 deletions src/arch/aarch64/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,13 @@ impl ArchCpu {
// Return to AArch32 Supervisor (SVC) mode, disable IRQ, FIQ, ABT
SPSR_EL2.set(0x1D3);
}
this_cpu_data().vcpu_state.store(VcpuState::Running);
this_cpu_data().vcpu.state.store(VcpuState::Running);
info!(
"cpu {} started at {:#x?}",
self.cpuid,
this_cpu_data().cpu_on_entry
);
crate::device::irqchip::flush_current_guest_irqs();
unsafe {
vmreturn(self.guest_reg() as *mut _ as usize);
}
Expand All @@ -215,7 +216,7 @@ impl ArchCpu {
assert!(this_cpu_id() == self.cpuid);
let cpu_data = this_cpu_data();
let _lock = cpu_data.ctrl_lock.lock();
cpu_data.vcpu_state.store(VcpuState::Stopped);
cpu_data.vcpu.state.store(VcpuState::Stopped);
drop(_lock);

// reset current cpu -> pc = 0x0 (wfi)
Expand Down
124 changes: 124 additions & 0 deletions src/arch/aarch64/ipi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,79 @@

use crate::device::irqchip::gic_send_event;

use bit_field::BitField;

#[cfg(irq_gicv2)]
use crate::{arch::cpu::this_cpu_id, cpu_data::this_zone};

#[cfg(irq_gicv3)]
use crate::{
arch::cpu::{cpuid_to_mpidr_affinity, this_cpu_id},
cpu_data::this_zone,
};

// ICC_SGI1R_EL1 fields.
const ICC_SGI1R_TARGET_LIST_BITS: core::ops::Range<usize> = 0..16;
#[cfg(irq_gicv3)]
const ICC_SGI1R_AFF1_BITS: core::ops::Range<usize> = 16..24;
const ICC_SGI1R_INTID_BITS: core::ops::Range<usize> = 24..28;
#[cfg(irq_gicv3)]
const ICC_SGI1R_AFF2_BITS: core::ops::Range<usize> = 32..40;
const ICC_SGI1R_IRM_BIT: usize = 40;
#[cfg(irq_gicv3)]
const ICC_SGI1R_RS_BITS: core::ops::Range<usize> = 44..48;
#[cfg(irq_gicv3)]
const ICC_SGI1R_AFF3_BITS: core::ops::Range<usize> = 48..56;

// GICD_SGIR fields.
#[cfg(irq_gicv2)]
const GICD_SGIR_INTID_BITS: core::ops::Range<usize> = 0..4;
#[cfg(irq_gicv2)]
const GICD_SGIR_TARGET_LIST_BITS: core::ops::Range<usize> = 16..24;
#[cfg(irq_gicv2)]
const GICD_SGIR_TARGET_LIST_FILTER_BITS: core::ops::Range<usize> = 24..26;

#[cfg(irq_gicv3)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Sgi1r {
target_list: u16,
aff1: u8,
intid: usize,
aff2: u8,
irm: bool,
range_selector: u8,
aff3: u8,
}

#[cfg(irq_gicv3)]
impl Sgi1r {
fn decode(value: u64) -> Self {
Self {
target_list: value.get_bits(ICC_SGI1R_TARGET_LIST_BITS) as u16,
aff1: value.get_bits(ICC_SGI1R_AFF1_BITS) as u8,
intid: value.get_bits(ICC_SGI1R_INTID_BITS) as usize,
aff2: value.get_bits(ICC_SGI1R_AFF2_BITS) as u8,
irm: value.get_bit(ICC_SGI1R_IRM_BIT),
range_selector: value.get_bits(ICC_SGI1R_RS_BITS) as u8,
aff3: value.get_bits(ICC_SGI1R_AFF3_BITS) as u8,
}
}

fn targets(self, cpu_id: usize, source_cpu: usize) -> bool {
if self.irm {
return cpu_id != source_cpu;
}
let (aff3, aff2, aff1, aff0) = cpuid_to_mpidr_affinity(cpu_id as u64);
let target_aff0 = self.range_selector as u64 * 16;
aff3 == self.aff3 as u64
&& aff2 == self.aff2 as u64
&& aff1 == self.aff1 as u64
&& aff0 >= target_aff0
&& aff0 < target_aff0 + 16
&& self.target_list.get_bit((aff0 - target_aff0) as usize)
}
}

pub fn arch_send_event(cpu_id: u64, sgi_num: u64) {
gic_send_event(cpu_id, sgi_num);
}
Expand All @@ -24,6 +97,57 @@ pub fn arch_notify_event(cpu_id: u64, sgi_num: u64, _event_id: usize, _queue_was
arch_send_event(cpu_id, sgi_num);
}

#[cfg(irq_gicv3)]
pub fn handle_guest_sgi1r(value: u64) {
let sgi = Sgi1r::decode(value);
let source_cpu = this_cpu_id();
for target_cpu in this_zone().cpu_set().iter() {
if sgi.targets(target_cpu, source_cpu) {
if !crate::device::irqchip::post_guest_irq(target_cpu, sgi.intid, None) {
error!(
"failed to post guest SGI {} to CPU{}",
sgi.intid, target_cpu
);
}
}
}
}

#[cfg(irq_gicv2)]
pub fn handle_guest_sgi1r(value: u64) {
post_guest_sgi(
value.get_bits(ICC_SGI1R_INTID_BITS) as usize,
value.get_bits(ICC_SGI1R_TARGET_LIST_BITS) as u8,
value.get_bit(ICC_SGI1R_IRM_BIT) as u32,
);
}

#[cfg(irq_gicv2)]
pub fn handle_guest_sgir(value: u32) {
post_guest_sgi(
value.get_bits(GICD_SGIR_INTID_BITS) as usize,
value.get_bits(GICD_SGIR_TARGET_LIST_BITS) as u8,
value.get_bits(GICD_SGIR_TARGET_LIST_FILTER_BITS),
);
}

#[cfg(irq_gicv2)]
fn post_guest_sgi(irq_id: usize, target_list: u8, filter: u32) {
let source_cpu = this_cpu_id();
for target_cpu in this_zone().cpu_set().iter() {
let selected = match filter {
0 => target_cpu < 8 && target_list.get_bit(target_cpu),
1 => target_cpu != source_cpu,
2 => target_cpu == source_cpu,
_ => false,
};
if selected && !crate::device::irqchip::post_guest_irq(target_cpu, irq_id, Some(source_cpu))
{
warn!("failed to post guest SGI {} to CPU{}", irq_id, target_cpu);
}
}
}

pub fn arch_check_events(event: Option<usize>) {
match event {
_ => {
Expand Down
2 changes: 2 additions & 0 deletions src/arch/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ pub mod s2pt;
pub mod sysreg;
pub mod time;
pub mod trap;
pub mod vcpu;
pub mod zone;
pub use s2pt::stage2_mode_detect;
pub use s2pt::Stage2PageTable;
pub use vcpu::ArchVcpu;
#[cfg(aarch64_uefi_support)]
pub mod uefi;

Expand Down
20 changes: 10 additions & 10 deletions src/arch/aarch64/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ use super::cpu::GeneralRegisters;
use crate::arch::sysreg::smc_call;
use crate::zone::zone_error;
use crate::{
arch::{
cpu::mpidr_to_cpuid,
sysreg::{read_sysreg, write_sysreg},
},
arch::{cpu::mpidr_to_cpuid, sysreg::read_sysreg},
cpu_data::{get_cpu_data, this_cpu_data, this_zone, VcpuState},
device::irqchip::gic_handle_irq,
event::{send_event, IPI_EVENT_SHUTDOWN, IPI_EVENT_WAKEUP},
Expand Down Expand Up @@ -122,6 +119,7 @@ pub fn arch_handle_exit(regs: &mut GeneralRegisters) -> ! {
ExceptionType::EXIT_REASON_EL2_IRQ => irqchip_handle_irq2(),
_ => arch_dump_exit(regs.exit_reason),
}
crate::device::irqchip::flush_current_guest_irqs();
unsafe { vmreturn(regs as *const _ as usize) }
}

Expand Down Expand Up @@ -341,11 +339,11 @@ fn handle_sysreg(regs: &mut GeneralRegisters) {
let val = if rt == 31 { 0 } else { regs.usr[rt] };
trace!("esr_el2 rt{}: {:#x?}", rt, val);
let sgi_id: u64 = (val & (0xf << 24)) >> 24;
if !this_cpu_data().vcpu_state.is_running() {
if !this_cpu_data().vcpu.state.is_running() {
trace!("skip send sgi {:#x?}", sgi_id);
} else {
trace!("send sgi {:#x?}", sgi_id);
write_sysreg!(icc_sgi1r_el1, val);
trace!("post guest sgi {:#x?}", sgi_id);
crate::arch::ipi::handle_guest_sgi1r(val);
}
arch_skip_instruction(regs); //skip sgi write
} else {
Expand Down Expand Up @@ -432,9 +430,9 @@ fn psci_emulate_cpu_on(regs: &mut GeneralRegisters) -> u64 {
let target_data = get_cpu_data(cpu as _);
let _lock = target_data.ctrl_lock.lock();

if target_data.vcpu_state.is_stopped() {
if target_data.vcpu.state.is_stopped() {
target_data.cpu_on_entry = regs.usr[2] as _;
target_data.vcpu_state.store(VcpuState::Ready);
target_data.vcpu.state.store(VcpuState::Ready);
send_event(cpu as _, SGI_IPI_ID as _, IPI_EVENT_WAKEUP);
} else {
error!("psci: cpu {} already on", cpu);
Expand Down Expand Up @@ -463,7 +461,7 @@ fn handle_psci_smc(
todo!();
}
PsciFnId::PSCI_AFFINITY_INFO_32 | PsciFnId::PSCI_AFFINITY_INFO_64 => {
!get_cpu_data(arg0 as _).vcpu_state.is_online() as _
!get_cpu_data(arg0 as _).vcpu.state.is_online() as _
}
PsciFnId::PSCI_MIG_INFO_TYPE => PSCI_TOS_NOT_PRESENT_MP,
PsciFnId::PSCI_FEATURES => psci_emulate_features_info(regs.usr[1]),
Expand All @@ -476,10 +474,12 @@ fn handle_psci_smc(
for cpu_id in zone.read().cpu_set().iter_except(this_cpu_data().id) {
let target_cpu = get_cpu_data(cpu_id);
let _lock = target_cpu.ctrl_lock.lock();
target_cpu.vcpu.clear_irq_state();
target_cpu.zone = None;
send_event(cpu_id, SGI_IPI_ID as _, IPI_EVENT_SHUTDOWN);
}

this_cpu_data().vcpu.clear_irq_state();
this_cpu_data().zone = None;
drop(zone);
remove_zone(zone_id);
Expand Down
41 changes: 41 additions & 0 deletions src/arch/aarch64/vcpu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2025 Syswonder
// hvisor is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
// FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Syswonder Website:
// https://www.syswonder.org
//
// Authors:
//
#[cfg(irq_gicv2)]
use core::sync::atomic::{AtomicU8, Ordering};

/// Architecture-private vCPU state, separate from the host pCPU's `ArchCpu`.
pub struct ArchVcpu {
#[cfg(irq_gicv2)]
pub sgi_source: [AtomicU8; 16],
}

impl ArchVcpu {
pub const fn new() -> Self {
#[cfg(irq_gicv2)]
const ZERO_SOURCE: AtomicU8 = AtomicU8::new(0);
Self {
#[cfg(irq_gicv2)]
sgi_source: [ZERO_SOURCE; 16],
}
}

pub fn clear_irq_state(&self) {
#[cfg(irq_gicv2)]
for source in &self.sgi_source {
source.store(0, Ordering::Release);
}
}
}
4 changes: 2 additions & 2 deletions src/arch/loongarch64/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl ArchCpu {
pub fn run(&mut self) -> ! {
assert!(this_cpu_id() == self.get_cpuid());
this_cpu_data().activate_gpm();
this_cpu_data().vcpu_state.store(VcpuState::Running);
this_cpu_data().vcpu.state.store(VcpuState::Running);
if !self.init {
self.init(this_cpu_data().cpu_on_entry, this_cpu_data().id, 0);
self.init = true;
Expand Down Expand Up @@ -131,7 +131,7 @@ impl ArchCpu {
);
}
info!("loongarch64: ArchCpu::idle: cpuid={}", self.get_cpuid());
this_cpu_data().vcpu_state.store(VcpuState::Stopped);
this_cpu_data().vcpu.state.store(VcpuState::Stopped);
// enable ipi on ecfg
ecfg_ipi_enable();
// The trap vector is installed with interrupts disabled. Enable them only
Expand Down
2 changes: 2 additions & 0 deletions src/arch/loongarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ pub mod s1pt;
pub mod s2pt;
pub mod time;
pub mod trap;
pub mod vcpu;
pub mod zone;
pub use vcpu::ArchVcpu;

pub use s1pt::Stage1PageTable;
pub use s2pt::stage2_mode_detect;
Expand Down
18 changes: 10 additions & 8 deletions src/arch/loongarch64/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::consts::MAX_CPU_NUM;
use crate::cpu_data::this_cpu_data;
use crate::device::irqchip::inject_irq;
use crate::device::irqchip::ls7a2000::chip::*;
use crate::event::{dump_events, handle_next_event, has_pending_events};
use crate::event::{dispatch_host_events, dump_events, handle_next_event, has_pending_events};
use crate::hypercall::{SGI_IPI_ID, *};
use crate::memory::{addr, mmio_handle_access, MMIOAccess};
use crate::zone::Zone;
Expand Down Expand Up @@ -1302,15 +1302,17 @@ fn handle_interrupt(is: usize) {
}

if ipi_status & event_doorbell != 0 {
// Clear before each fetch. If the fetch observes an empty queue while a
// producer enqueues concurrently, its doorbell remains pending and
// re-fires. Clearing after the fetch could lose that coalesced wakeup.
// Clear the observed host doorbell before checking logical state.
// A concurrent producer is either drained by the dispatcher or
// leaves/re-arms a later notification.
clear_ipi_bits(event_doorbell);
if has_pending_events(cpu_id) {
while handle_next_event() {
clear_ipi_bits(event_doorbell);
dispatch_host_events(|| {
if has_pending_events(cpu_id) {
while handle_next_event() {
clear_ipi_bits(event_doorbell);
}
}
}
});
}

let handled_mask = event_doorbell | virtual_ipi_doorbell;
Expand Down
25 changes: 25 additions & 0 deletions src/arch/loongarch64/vcpu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2025 Syswonder
// hvisor is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
// FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Syswonder Website:
// https://www.syswonder.org
//
// Authors:
//
/// Architecture-private vCPU state placeholder.
pub struct ArchVcpu;

impl ArchVcpu {
pub const fn new() -> Self {
Self
}

pub fn clear_irq_state(&self) {}
}
4 changes: 2 additions & 2 deletions src/arch/loongarch64/zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,13 +686,13 @@ fn virtual_ipi_send(target_guest_cpu: usize, ipi_bits: u32, blocking: bool) -> H
let entry = VIRTUAL_IPI_MAILBOX[target_cpu * 4].load(Ordering::Acquire) as usize;
let target_data = get_cpu_data(target_cpu);
let _lock = target_data.ctrl_lock.lock();
if target_data.vcpu_state.is_stopped() {
if target_data.vcpu.state.is_stopped() {
debug!(
"loongarch64: guest boots vCPU{}(pCPU{}) through IPI, entry={:#x}",
target_guest_cpu, target_cpu, entry
);
target_data.cpu_on_entry = entry;
target_data.vcpu_state.store(VcpuState::Ready);
target_data.vcpu.state.store(VcpuState::Ready);
send_event(target_cpu, SGI_IPI_ID as usize, IPI_EVENT_WAKEUP);
} else {
warn!(
Expand Down
Loading
Loading