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 <brho@google.com>
Signed-off-by: Neel Natu <neelnatu@google.com>
Signed-off-by: Ofir Weisse <oweisse@google.com>
Signed-off-by: Peter Oskolkov <posk@google.com>
This commit is contained in:
Peter Oskolkov 2025-08-15 01:50:42 +00:00 committed by Bo Chen
parent dd8687aebb
commit 2c7d6be3f4
2 changed files with 18 additions and 19 deletions

View file

@ -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)?;
}

View file

@ -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<T> = result::Result<T, Error>;
// 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();
}
}