Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions os/arceos/modules/axruntime/runtime.ld
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ SECTIONS {
KEEP(*(.driver.register*))
__edriver_register = .;

. = ALIGN(0x10);
__saxdevice_factory = .;
KEEP(*(.axdevice.factory*))
__eaxdevice_factory = .;

. = ALIGN(0x10);
_ex_table_start = .;
KEEP(*(__ex_table))
Expand Down
71 changes: 71 additions & 0 deletions os/axvisor/configs/vms/qemu/aarch64/linux-smp1-gppt-gic.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Vm base info configs
#
[base]
# Guest vm id.
id = 1
# Guest vm name.
name = "linux-qemu-gppt-gic"
# Virtualization type.
vm_type = 1
# The number of virtual CPUs.
cpu_num = 1
# Guest vm physical cpu sets.
phys_cpu_ids = [0]

#
# Vm kernel configs
#
[kernel]
# The entry point of the kernel image.
entry_point = 0x8020_0000
# The location of image: "memory" | "fs".
# Load from file system.
image_location = "fs"
# The file path of the kernel image.
kernel_path = "/guest/linux/linux-qemu"
# The load address of the kernel image.
kernel_load_addr = 0x8020_0000
# The file path of the device tree blob (DTB).
# dtb_path = "tmp/linux-aarch64-qemu-smp1.dtb"
# The load address of the device tree blob (DTB).
dtb_load_addr = 0x8000_0000

# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`).
# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`, 2 means `MAP_RESERVED`.
memory_regions = [
[0x8000_0000, 0x1000_0000, 0x7, 1], # System RAM 1G MAP_IDENTICAL
]

#
# Device specifications
#
[devices]
# The interrupt mode.
interrupt_mode = "passthrough"

# Pass-through devices.
# Name Base-Ipa Base-Pa Length Alloc-Irq.
passthrough_devices = [
["/"],
#["/timer"],
]

# Passthrough addresses.
# Base-GPA Length.
passthrough_addresses = [
#[0x28041000, 0x100_0000]
]

# Devices that are not desired to be passed through to the guest
excluded_devices = [
# ["/gic-v3"],
]

# Emu_devices.
# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig.
emu_devices = [
# Keep this case focused on the first native DeviceOps migration target.
# ["gppt-gicd", 0x0800_0000, 0x1_0000, 0, 0x21, []],
["gppt-gicr", 0x080a_0000, 0x2_0000, 0, 0x20, [1, 0x2_0000, 0]], # 1 vcpu, stride 0x20000, starts with pcpu 0
# ["gppt-gits", 0x0808_0000, 0x2_0000, 0, 0x22, [0x0808_0000]], # host_gits_base
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
env = { AX_IP = "10.0.2.15", AX_GW = "10.0.2.2" }
features = [
"ept-level-4",
"ax-driver/virtio-blk",
"fs",
]
log = "Info"
plat_dyn = true
target = "aarch64-unknown-none-softfloat"
vm_configs = ["os/axvisor/configs/vms/qemu/aarch64/linux-smp1-gppt-gic.toml"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
args = [
"-nographic",
"-cpu",
"cortex-a72",
"-machine",
"virt,virtualization=on,gic-version=3",
"-smp",
"4",
"-device",
"virtio-blk-device,drive=disk0",
"-drive",
"id=disk0,if=none,format=raw,file=${workspace}/tmp/axbuild/rootfs/rootfs-aarch64-alpine.img",
"-append",
"root=/dev/vda rw init=/bin/sh",
"-m",
"8g",
]
fail_regex = [
"(?i)\\bpanic(?:ked)?\\b",
"(?i)kernel panic",
"(?i)login incorrect",
"(?i)permission denied",
]
success_regex = ["(?m)^guest test pass!\\s*$"]
shell_prefix = "~ #"
shell_init_cmd = "pwd && echo 'guest test pass!'"
to_bin = true
uefi = false
156 changes: 139 additions & 17 deletions virtualization/arm_vgic/src/v3/vgicr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use alloc::{format, rc::Rc, vec, vec::Vec};
use core::{cell::UnsafeCell, ptr};

use ax_errno::ax_err_type;
use ax_kspin::SpinNoIrq as Mutex;
use ax_memory_addr::PhysAddr;
use axdevice_base::{AccessWidth, BaseDeviceOps};
use axvm_types::{GuestPhysAddr, GuestPhysAddrRange, HostPhysAddr};
use log::{debug, trace};
use axdevice_base::{
AccessWidth, BusAddress, BusKind, BusOp, BusResponse, DeviceBuildContext, DeviceCapabilities,
DeviceError, DeviceFactory, DeviceMeta, DeviceOps, DeviceResult, EmuDeviceType, Resource,
};
use axvm_types::{EmulatedDeviceConfig, GuestPhysAddr, GuestPhysAddrRange, HostPhysAddr};
use log::{debug, info, trace};
use spin::Once;

use super::{
Expand All @@ -30,6 +35,12 @@ use crate::host;
/// Default size per GICR region.
pub const DEFAULT_SIZE_PER_GICR: usize = 0x20000; // 128K: 64K for SGI/PPI, then 64K for LPI

fn mmio_resources(addr: GuestPhysAddr, size: usize) -> Vec<Resource> {
vec![Resource::Mmio(GuestPhysAddrRange::from_start_size(
addr, size,
))]
}

/// Virtual GICR registers.
pub struct VGicRRegs {
/// LPI configuration table base address.
Expand All @@ -38,6 +49,8 @@ pub struct VGicRRegs {

/// Virtual GICv3 Redistributor.
pub struct VGicR {
meta: DeviceMeta,

/// The address of the VGicR in the guest physical address space.
pub addr: GuestPhysAddr,
/// The size of the VGicR in bytes.
Expand All @@ -64,35 +77,26 @@ impl VGicR {
unsafe { &mut *self.regs.get() }
}

/// Creates a new VGicR instance.
pub fn new(addr: GuestPhysAddr, size: Option<usize>, cpu_id: usize) -> Self {
/// Creates a new VGicR instance with native device metadata.
pub fn new(meta: DeviceMeta, addr: GuestPhysAddr, size: Option<usize>, cpu_id: usize) -> Self {
let size = size.unwrap_or(DEFAULT_SIZE_PER_GICR);
let host_gicr_base_this_cpu = crate::api_reexp::get_host_gicr_base() + cpu_id * size;

Self {
meta,
addr,
size,
cpu_id,
host_gicr_base_this_cpu,
regs: UnsafeCell::new(VGicRRegs { propbaser: 0 }),
}
}
}

impl BaseDeviceOps<GuestPhysAddrRange> for VGicR {
fn emu_type(&self) -> axdevice_base::EmuDeviceType {
axdevice_base::EmuDeviceType::GPPTRedistributor
}

fn address_range(&self) -> GuestPhysAddrRange {
GuestPhysAddrRange::from_start_size(self.addr, self.size)
}

fn handle_read(
&self,
addr: <GuestPhysAddrRange as axdevice_base::DeviceAddrRange>::Addr,
width: AccessWidth,
) -> ax_errno::AxResult<usize> {
fn handle_read(&self, addr: GuestPhysAddr, width: AccessWidth) -> ax_errno::AxResult<usize> {
let gicr_base = self.host_gicr_base_this_cpu;
let reg = addr - self.addr;

Expand Down Expand Up @@ -158,7 +162,7 @@ impl BaseDeviceOps<GuestPhysAddrRange> for VGicR {

fn handle_write(
&self,
addr: <GuestPhysAddrRange as axdevice_base::DeviceAddrRange>::Addr,
addr: GuestPhysAddr,
width: AccessWidth,
value: usize,
) -> ax_errno::AxResult<()> {
Expand Down Expand Up @@ -221,6 +225,124 @@ impl BaseDeviceOps<GuestPhysAddrRange> for VGicR {
}
}

impl DeviceOps for VGicR {
fn id(&self) -> axdevice_base::DeviceId {
self.meta.id()
}

fn name(&self) -> &str {
self.meta.name()
}

fn resources(&self) -> &[Resource] {
self.meta.resources()
}

fn capabilities(&self) -> DeviceCapabilities {
self.meta.capabilities()
}

fn access(&self, access: axdevice_base::BusAccess) -> DeviceResult<BusResponse> {
if access.kind != BusKind::Mmio {
return Err(DeviceError::BusAddressMismatch {
kind: BusKind::Mmio,
address: access.addr,
});
}

let BusAddress::Mmio(addr) = access.addr else {
return Err(DeviceError::BusAddressMismatch {
kind: BusKind::Mmio,
address: access.addr,
});
};

if !self.address_range().contains(addr) {
return Err(DeviceError::AddressOutOfRange {
kind: BusKind::Mmio,
address: access.addr,
});
}

match access.op {
BusOp::Read => self
.handle_read(addr, access.width)
.map(|value| BusResponse::Read { value })
.map_err(DeviceError::from),
BusOp::Write { value } => self
.handle_write(addr, access.width, value)
.map(|()| BusResponse::Write)
.map_err(DeviceError::from),
}
}
}

/// Factory for ARM GIC partial passthrough redistributors.
pub struct GpptRedistributorFactory;

static GPPT_REDISTRIBUTOR_FACTORY: GpptRedistributorFactory = GpptRedistributorFactory;

axdevice_base::register_device_factory!("gppt-redistributor", GPPT_REDISTRIBUTOR_FACTORY);

impl DeviceFactory for GpptRedistributorFactory {
fn ty(&self) -> EmuDeviceType {
EmuDeviceType::GPPTRedistributor
}

fn build(
&self,
ctx: &mut dyn DeviceBuildContext,
config: &EmulatedDeviceConfig,
) -> DeviceResult<Vec<Rc<dyn DeviceOps>>> {
const ARG_ERR_MSG: &str = "expect 3 args for gppt redistributor (cpu_num, stride, pcpu_id)";

let cpu_num = config
.cfg_list
.first()
.copied()
.ok_or_else(|| DeviceError::from(ax_err_type!(InvalidInput, ARG_ERR_MSG)))?;
let stride = config
.cfg_list
.get(1)
.copied()
.ok_or_else(|| DeviceError::from(ax_err_type!(InvalidInput, ARG_ERR_MSG)))?;
let pcpu_id = config
.cfg_list
.get(2)
.copied()
.ok_or_else(|| DeviceError::from(ax_err_type!(InvalidInput, ARG_ERR_MSG)))?;

let mut devices: Vec<Rc<dyn DeviceOps>> = Vec::new();
for i in 0..cpu_num {
let addr = GuestPhysAddr::from(config.base_gpa + i * stride);
let size = config.length;
let name = if config.name.is_empty() {
format!("gppt-redistributor-{i}")
} else {
format!("{}-{i}", config.name)
};
let meta = DeviceMeta::new(
ctx.alloc_device_id(),
name,
mmio_resources(addr, size),
DeviceCapabilities::none(),
);
let device: Rc<dyn DeviceOps> =
Rc::new(VGicR::new(meta, addr, Some(size), pcpu_id + i));
devices.push(device);

info!(
"GPPT Redistributor factory built native VGicR for vCPU {i} with base GPA {:#x} \
and length {:#x}",
addr.as_usize(),
size
);
}

Ok(devices)
}
}

// todo: move the lpi prop table to arm-gic-driver, and find a good interface to use it.
/// LPI property table for managing Locality-specific Peripheral Interrupts.
pub struct LpiPropTable {
Expand Down
Loading