Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
20 changes: 17 additions & 3 deletions alioth-cli/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::collections::HashMap;
use std::ffi::CString;
use std::path::{Path, PathBuf};

use alioth::board::BoardConfig;
use alioth::board::{BoardConfig, CpuConfig};
#[cfg(target_arch = "x86_64")]
use alioth::device::fw_cfg::FwCfgItemParam;
use alioth::errors::{DebugTrace, trace_error};
Expand Down Expand Up @@ -184,10 +184,15 @@ pub struct BootArgs {
#[arg(short, long, value_name = "PATH")]
initramfs: Option<Box<Path>>,

/// Number of VCPUs assigned to the guest.
/// DEPRECATED: Use --cpu instead.
#[arg(long, default_value_t = 1)]
num_cpu: u16,

#[arg(short('p'), long, help(
help_text::<CpuConfig>("Configure the VCPUs of the guest.")
))]
cpu: Option<Box<str>>,

/// DEPRECATED: Use --memory instead.
#[arg(long, default_value = "1G")]
mem_size: String,
Expand Down Expand Up @@ -379,9 +384,18 @@ pub fn boot(args: BootArgs) -> Result<(), Error> {
..Default::default()
}
};
let cpu_config = if let Some(s) = args.cpu {
serde_aco::from_args(&s, &objects).context(error::ParseArg { arg: s })?
} else {
eprintln!("Please update the cmd line to --cpu count={}", args.num_cpu);
CpuConfig {
count: args.num_cpu,
..Default::default()
}
};
let board_config = BoardConfig {
mem: mem_config,
num_cpu: args.num_cpu,
cpu: cpu_config,
coco,
};
let vm = Machine::new(hypervisor, board_config).context(error::CreateVm)?;
Expand Down
8 changes: 4 additions & 4 deletions alioth/src/arch/aarch64/reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,10 @@ bitfield! {
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct MpidrEl1(u64);
impl Debug;
pub aff3, set_aff3: 39, 32;
pub u8, aff3, set_aff3: 39, 32;
pub u, set_u: 30;
pub mt, set_mt: 24;
pub aff2, set_aff2: 23, 16;
pub aff1, set_aff1: 15, 8;
pub aff0, set_aff0: 7, 0;
pub u8, aff2, set_aff2: 23, 16;
pub u8, aff1, set_aff1: 15, 8;
pub u8, aff0, set_aff0: 7, 0;
}
69 changes: 68 additions & 1 deletion alioth/src/board/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ use std::thread::JoinHandle;

use libc::{MAP_PRIVATE, MAP_SHARED};
use parking_lot::{Condvar, Mutex, RwLock, RwLockReadGuard};
use serde::Deserialize;
use serde_aco::Help;
use snafu::{ResultExt, Snafu};

#[cfg(target_arch = "x86_64")]
Expand Down Expand Up @@ -68,6 +70,8 @@ pub enum Error {
Memory { source: Box<crate::mem::Error> },
#[snafu(display("Failed to load payload"), context(false))]
Loader { source: Box<crate::loader::Error> },
#[snafu(display("Invalid CPU topology"))]
InvalidCpuTopology,
#[snafu(display("Failed to create VCPU-{index}"))]
CreateVcpu {
index: u16,
Expand Down Expand Up @@ -97,6 +101,61 @@ pub enum Error {

type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Default, Help)]
pub struct CpuTopology {
#[serde(default)]
/// Enable SMT (Hyperthreading).
pub smt: bool,
#[serde(default)]
/// Number of cores per socket.
pub cores: u16,
#[serde(default)]
/// Number of sockets.
pub sockets: u8,
}

impl CpuTopology {
pub fn encode(&self, index: u16) -> (u8, u16, u8) {
let total_cores = self.cores * self.sockets as u16;
let thread_id = index / total_cores;
let core_id = index % total_cores % self.cores;
let socket_id = index % total_cores / self.cores;
(thread_id as u8, core_id, socket_id as u8)
}
}

const fn default_cpu_count() -> u16 {
1
}

#[derive(Debug, Deserialize, Default, Help)]
pub struct CpuConfig {
/// Number of VCPUs assigned to the guest. [default: 1]
#[serde(default = "default_cpu_count")]
pub count: u16,
/// Architecture specific CPU topology.
#[serde(default)]
pub topology: CpuTopology,
}

impl CpuConfig {
pub fn fixup(&mut self) -> Result<()> {
if self.topology.sockets == 0 {
self.topology.sockets = 1;
}
let vcpus_per_core = 1 + self.topology.smt as u16;
if self.topology.cores == 0 {
self.topology.cores = self.count / self.topology.sockets as u16 / vcpus_per_core;
}
let vcpus_per_socket = self.topology.cores * vcpus_per_core;
let count = self.topology.sockets as u16 * vcpus_per_socket;
if count != self.count {
return error::InvalidCpuTopology.fail();
}
Ok(())
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BoardState {
Created,
Expand All @@ -116,14 +175,18 @@ pub const PCIE_MMIO_64_SIZE: u64 = 1 << 40;

pub struct BoardConfig {
pub mem: MemConfig,
pub num_cpu: u16,
pub cpu: CpuConfig,
pub coco: Option<Coco>,
}

impl BoardConfig {
pub fn pcie_mmio_64_start(&self) -> u64 {
(self.mem.size.saturating_sub(RAM_32_SIZE) + MEM_64_START).next_power_of_two()
}

pub fn config_fixup(&mut self) -> Result<()> {
self.cpu.fixup()
}
}

type VcpuGuard<'a> = RwLockReadGuard<'a, Vec<(JoinHandle<Result<()>>, Sender<()>)>>;
Expand Down Expand Up @@ -444,3 +507,7 @@ where
Ok(pages)
}
}

#[cfg(test)]
#[path = "board_test.rs"]
mod tests;
113 changes: 76 additions & 37 deletions alioth/src/board/board_aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::arch::layout::{
RAM_32_SIZE, RAM_32_START,
};
use crate::arch::reg::MpidrEl1;
use crate::board::{Board, BoardConfig, PCIE_MMIO_64_SIZE, Result, VcpuGuard};
use crate::board::{Board, BoardConfig, CpuTopology, PCIE_MMIO_64_SIZE, Result, VcpuGuard};
use crate::firmware::dt::{DeviceTree, Node, PropVal};
use crate::hv::{GicV2, GicV2m, GicV3, Hypervisor, Its, Vcpu, Vm};
use crate::loader::{Executable, InitState, Payload};
Expand Down Expand Up @@ -59,7 +59,7 @@ impl<V: Vm> ArchBoard<V> {
where
H: Hypervisor<Vm = V>,
{
let gic = match vm.create_gic_v3(GIC_DIST_START, GIC_V3_REDIST_START, config.num_cpu) {
let gic = match vm.create_gic_v3(GIC_DIST_START, GIC_V3_REDIST_START, config.cpu.count) {
Ok(v3) => Gic::V3(v3),
Err(e) => {
log::error!("Cannot create GIC v3: {e:?}trying v2...");
Expand Down Expand Up @@ -91,22 +91,59 @@ impl<V: Vm> ArchBoard<V> {
}
}

fn encode_mpidr(index: u16) -> MpidrEl1 {
fn encode_mpidr(topology: &CpuTopology, index: u16) -> MpidrEl1 {
let (thread_id, core_id, socket_id) = topology.encode(index);
let mut mpidr = MpidrEl1(0);
let index = index as u64;
mpidr.set_aff0(index & 0xf);
mpidr.set_aff1(index >> 4);
mpidr.set_aff2(index >> 12);
mpidr.set_aff3(index >> 20);
mpidr.set_aff0(thread_id);
mpidr.set_aff1(core_id as u8);
mpidr.set_aff2(socket_id);
mpidr
}

fn dt_cpu_node(topology: &CpuTopology, socket: u8, core: u16) -> Node {
let mut mpidr = MpidrEl1(0);
mpidr.set_aff1(core as u8);
mpidr.set_aff2(socket);
if topology.smt {
Node {
props: HashMap::new(),
nodes: vec![
(
"thread0".to_owned(),
Node {
props: HashMap::from([(
"cpu",
PropVal::PHandle(PHANDLE_CPU | mpidr.0 as u32),
)]),
nodes: Vec::new(),
},
),
(
"thread1".to_owned(),
Node {
props: HashMap::from([(
"cpu",
PropVal::PHandle(PHANDLE_CPU | mpidr.0 as u32 | 1),
)]),
nodes: Vec::new(),
},
),
],
}
} else {
Node {
nodes: Vec::new(),
props: HashMap::from([("cpu", PropVal::PHandle(PHANDLE_CPU | mpidr.0 as u32))]),
}
}
}

impl<V> Board<V>
where
V: Vm,
{
pub fn encode_cpu_identity(&self, index: u16) -> u64 {
encode_mpidr(index).0
encode_mpidr(&self.config.cpu.topology, index).0
}

pub fn setup_firmware(&self, _: &Path, _: &Payload) -> Result<InitState> {
Expand Down Expand Up @@ -222,7 +259,7 @@ where
}

pub fn create_cpu_nodes(&self, root: &mut Node) {
let mut cpu_nodes: Vec<_> = (0..(self.config.num_cpu))
let mut cpu_nodes: Vec<_> = (0..(self.config.cpu.count))
.map(|index| {
let mpidr = self.encode_cpu_identity(index);
(
Expand All @@ -233,42 +270,44 @@ where
("compatible", PropVal::Str("arm,arm-v8")),
("enable-method", PropVal::Str("psci")),
("reg", PropVal::U64(mpidr)),
("phandle", PropVal::PHandle(PHANDLE_CPU | index as u32)),
("phandle", PropVal::PHandle(PHANDLE_CPU | mpidr as u32)),
]),
nodes: Vec::new(),
},
)
})
.collect();
let cores = (0..(self.config.num_cpu))
.map(|index| {
(
format!("core{index}"),
Node {
props: HashMap::from([(
"cpu",
PropVal::PHandle(PHANDLE_CPU | index as u32),
)]),
nodes: Vec::new(),
},
)
})
.collect();

let cpu_map = Node {
props: HashMap::new(),
nodes: vec![(
"socket0".to_owned(),
Node {
props: HashMap::new(),
nodes: vec![(
"cluster0".to_owned(),
nodes: (0..self.config.cpu.topology.sockets)
.map(|socket| {
(
format!("socket{socket}",),
Node {
props: HashMap::new(),
nodes: cores,
nodes: vec![(
"cluster0".to_owned(),
Node {
props: HashMap::new(),
nodes: (0..self.config.cpu.topology.cores)
.map(|core| {
(
format!("core{core}"),
dt_cpu_node(
&self.config.cpu.topology,
socket,
core,
),
)
})
.collect(),
},
)],
},
)],
},
)],
)
})
.collect(),
};
cpu_nodes.push(("cpu-map".to_owned(), cpu_map));
let cpus = Node {
Expand Down Expand Up @@ -335,7 +374,7 @@ where
let ppi = 1;
let level_trigger = 4;
let cpu_mask = match self.arch.gic {
Gic::V2(_) => (1 << self.config.num_cpu) - 1,
Gic::V2(_) => (1 << self.config.cpu.count) - 1,
Gic::V3 { .. } => 0,
};
for pin in irq_pins {
Expand Down Expand Up @@ -422,7 +461,7 @@ where
GIC_DIST_START,
64 << 10,
GIC_V3_REDIST_START,
self.config.num_cpu as u64 * (128 << 10),
self.config.cpu.count as u64 * (128 << 10),
]),
),
("phandle", PropVal::U32(PHANDLE_GIC)),
Expand Down
11 changes: 6 additions & 5 deletions alioth/src/board/board_aarch64_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
use rstest::rstest;

use crate::arch::reg::MpidrEl1;
use crate::board::CpuTopology;
use crate::board::aarch64::encode_mpidr;

#[rstest]
#[case(1, 1)]
#[case(8, 8)]
#[case(23, (1 << 8) | 7)]
fn test_encode_mpidr(#[case] index: u16, #[case] mpidr: u64) {
assert_eq!(encode_mpidr(index), MpidrEl1(mpidr));
#[case(CpuTopology{smt: false, cores: 1, sockets: 1}, 1, 1)]
#[case(CpuTopology{smt: true, cores: 8, sockets: 1}, 8, 1)]
#[case(CpuTopology{smt: true, cores: 8, sockets: 4}, 45, (1 << 16) | (5 << 8) | 1)]
fn test_encode_mpidr(#[case] topology: CpuTopology, #[case] index: u16, #[case] mpidr: u64) {
assert_eq!(encode_mpidr(&topology, index), MpidrEl1(mpidr));
}
Loading
Loading