From 2c7d6be3f42ff0c108b8b33b0af88f400e063625 Mon Sep 17 00:00:00 2001 From: Peter Oskolkov Date: Fri, 15 Aug 2025 01:50:42 +0000 Subject: [PATCH] arch: don't construct mptable on x86_64 if too many CPUs MP table is a legacy device that is incompatible with x2apic CPU IDs exceeding 254. The Linux kernel is perfectly happy without MP table in these cases. Signed-off-by: Barret Rhoden Signed-off-by: Neel Natu Signed-off-by: Ofir Weisse Signed-off-by: Peter Oskolkov --- arch/src/x86_64/mod.rs | 6 +++++- arch/src/x86_64/mptable.rs | 31 +++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index a4c0b88bf..27e1375d8 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -33,6 +33,10 @@ use std::arch::x86_64; #[cfg(feature = "tdx")] pub mod tdx; +// While modern architectures support more than 255 CPUs via x2APIC, +// legacy devices such as mptable support at most 254 CPUs. +pub(crate) const MAX_SUPPORTED_CPUS_LEGACY: u32 = 254; + // CPUID feature bits #[cfg(feature = "kvm")] const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit. @@ -915,7 +919,7 @@ pub fn configure_vcpu( // does not recognize the last vCPU if x2apic is not enabled when // there are 256 vCPUs in a flat hierarchy (i.e. max x2apic ID is 255), // so we need to enable x2apic in this case as well. - let enable_x2_apic_mode = get_max_x2apic_id(topology) >= 255; + let enable_x2_apic_mode = get_max_x2apic_id(topology) > MAX_SUPPORTED_CPUS_LEGACY; regs::setup_sregs(&guest_memory.memory(), vcpu, enable_x2_apic_mode) .map_err(Error::SregsConfiguration)?; } diff --git a/arch/src/x86_64/mptable.rs b/arch/src/x86_64/mptable.rs index d709a0043..d688e4137 100644 --- a/arch/src/x86_64/mptable.rs +++ b/arch/src/x86_64/mptable.rs @@ -11,6 +11,7 @@ use libc::c_uchar; use thiserror::Error; use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError}; +use super::MAX_SUPPORTED_CPUS_LEGACY; use crate::layout::{APIC_START, HIGH_RAM_START, IOAPIC_START}; use crate::x86_64::{get_x2apic_id, mpspec}; use crate::GuestMemoryMmap; @@ -61,9 +62,6 @@ pub enum Error { /// Failure while zeroing out the memory for the MP table. #[error("Failure while zeroing out the memory for the MP table")] Clear(#[source] GuestMemoryError), - /// Number of CPUs exceeds the maximum supported CPUs - #[error("Number of CPUs exceeds the maximum supported CPUs")] - TooManyCpus, /// Failure to write the MP floating pointer. #[error("Failure to write the MP floating pointer")] WriteMpfIntel(#[source] GuestMemoryError), @@ -89,11 +87,6 @@ pub enum Error { pub type Result = result::Result; -// With APIC/xAPIC, there are only 255 APIC IDs available. And IOAPIC occupies -// one APIC ID, so only 254 CPUs at maximum may be supported. Actually it's -// a large number for FC usecases. -pub const MAX_SUPPORTED_CPUS: u32 = 254; - // Most of these variables are sourced from the Intel MP Spec 1.4. const SMP_MAGIC_IDENT: &[c_uchar; 4] = b"_MP_"; const MPC_SIGNATURE: &[c_uchar; 4] = b"PCMP"; @@ -141,8 +134,9 @@ pub fn setup_mptable( if num_cpus > 0 { let cpu_id_max = num_cpus - 1; let x2apic_id_max = get_x2apic_id(cpu_id_max, topology); - if x2apic_id_max >= MAX_SUPPORTED_CPUS { - return Err(Error::TooManyCpus); + if x2apic_id_max >= MAX_SUPPORTED_CPUS_LEGACY { + info!("Skipping mptable creation due to too many CPUs"); + return Ok(()); } } @@ -157,7 +151,7 @@ pub fn setup_mptable( } let mut checksum: u8 = 0; - let ioapicid: u8 = MAX_SUPPORTED_CPUS as u8 + 1; + let ioapicid: u8 = MAX_SUPPORTED_CPUS_LEGACY as u8 + 1; // The checked_add here ensures the all of the following base_mp.unchecked_add's will be without // overflow. @@ -392,11 +386,13 @@ mod tests { #[test] fn cpu_entry_count() { - let mem = - GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(MAX_SUPPORTED_CPUS))]) - .unwrap(); + let mem = GuestMemoryMmap::from_ranges(&[( + MPTABLE_START, + compute_mp_size(MAX_SUPPORTED_CPUS_LEGACY), + )]) + .unwrap(); - for i in 0..MAX_SUPPORTED_CPUS { + for i in 0..MAX_SUPPORTED_CPUS_LEGACY { setup_mptable(MPTABLE_START, &mem, i, None).unwrap(); let mpf_intel: MpfIntelWrapper = mem.read_obj(MPTABLE_START).unwrap(); @@ -426,10 +422,9 @@ mod tests { #[test] fn cpu_entry_count_max() { - let cpus = MAX_SUPPORTED_CPUS + 1; + let cpus = MAX_SUPPORTED_CPUS_LEGACY + 1; let mem = GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(cpus))]).unwrap(); - let result = setup_mptable(MPTABLE_START, &mem, cpus, None); - result.unwrap_err(); + setup_mptable(MPTABLE_START, &mem, cpus, None).unwrap(); } }