1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 // 4 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style license that can be 6 // found in the LICENSE-BSD-3-Clause file. 7 8 use std::mem; 9 use std::result; 10 use std::slice; 11 12 use libc::c_uchar; 13 use thiserror::Error; 14 use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryError}; 15 16 use crate::layout::{APIC_START, HIGH_RAM_START, IOAPIC_START}; 17 use crate::x86_64::{get_x2apic_id, mpspec}; 18 use crate::GuestMemoryMmap; 19 20 // This is a workaround to the Rust enforcement specifying that any implementation of a foreign 21 // trait (in this case `ByteValued`) where: 22 // * the type that is implementing the trait is foreign or 23 // * all of the parameters being passed to the trait (if there are any) are also foreign 24 // is prohibited. 25 #[derive(Copy, Clone, Default)] 26 struct MpcBusWrapper(mpspec::mpc_bus); 27 #[derive(Copy, Clone, Default)] 28 struct MpcCpuWrapper(mpspec::mpc_cpu); 29 #[derive(Copy, Clone, Default)] 30 struct MpcIntsrcWrapper(mpspec::mpc_intsrc); 31 #[derive(Copy, Clone, Default)] 32 struct MpcIoapicWrapper(mpspec::mpc_ioapic); 33 #[derive(Copy, Clone, Default)] 34 struct MpcTableWrapper(mpspec::mpc_table); 35 #[derive(Copy, Clone, Default)] 36 struct MpcLintsrcWrapper(mpspec::mpc_lintsrc); 37 #[derive(Copy, Clone, Default)] 38 struct MpfIntelWrapper(mpspec::mpf_intel); 39 40 // SAFETY: These `mpspec` wrapper types are only data, reading them from data is a safe initialization. 41 unsafe impl ByteValued for MpcBusWrapper {} 42 // SAFETY: see above 43 unsafe impl ByteValued for MpcCpuWrapper {} 44 // SAFETY: see above 45 unsafe impl ByteValued for MpcIntsrcWrapper {} 46 // SAFETY: see above 47 unsafe impl ByteValued for MpcIoapicWrapper {} 48 // SAFETY: see above 49 unsafe impl ByteValued for MpcTableWrapper {} 50 // SAFETY: see above 51 unsafe impl ByteValued for MpcLintsrcWrapper {} 52 // SAFETY: see above 53 unsafe impl ByteValued for MpfIntelWrapper {} 54 55 #[derive(Debug, Error)] 56 pub enum Error { 57 /// There was too little guest memory to store the entire MP table. 58 #[error("There was too little guest memory to store the entire MP table")] 59 NotEnoughMemory, 60 /// The MP table has too little address space to be stored. 61 #[error("The MP table has too little address space to be stored")] 62 AddressOverflow, 63 /// Failure while zeroing out the memory for the MP table. 64 #[error("Failure while zeroing out the memory for the MP table: {0}")] 65 Clear(GuestMemoryError), 66 /// Number of CPUs exceeds the maximum supported CPUs 67 #[error("Number of CPUs exceeds the maximum supported CPUs")] 68 TooManyCpus, 69 /// Failure to write the MP floating pointer. 70 #[error("Failure to write the MP floating pointer: {0}")] 71 WriteMpfIntel(GuestMemoryError), 72 /// Failure to write MP CPU entry. 73 #[error("Failure to write MP CPU entry: {0}")] 74 WriteMpcCpu(GuestMemoryError), 75 /// Failure to write MP ioapic entry. 76 #[error("Failure to write MP ioapic entry: {0}")] 77 WriteMpcIoapic(GuestMemoryError), 78 /// Failure to write MP bus entry. 79 #[error("Failure to write MP bus entry: {0}")] 80 WriteMpcBus(GuestMemoryError), 81 /// Failure to write MP interrupt source entry. 82 #[error("Failure to write MP interrupt source entry: {0}")] 83 WriteMpcIntsrc(GuestMemoryError), 84 /// Failure to write MP local interrupt source entry. 85 #[error("Failure to write MP local interrupt source entry: {0}")] 86 WriteMpcLintsrc(GuestMemoryError), 87 /// Failure to write MP table header. 88 #[error("Failure to write MP table header: {0}")] 89 WriteMpcTable(GuestMemoryError), 90 } 91 92 pub type Result<T> = result::Result<T, Error>; 93 94 // With APIC/xAPIC, there are only 255 APIC IDs available. And IOAPIC occupies 95 // one APIC ID, so only 254 CPUs at maximum may be supported. Actually it's 96 // a large number for FC usecases. 97 pub const MAX_SUPPORTED_CPUS: u32 = 254; 98 99 // Most of these variables are sourced from the Intel MP Spec 1.4. 100 const SMP_MAGIC_IDENT: &[c_uchar; 4] = b"_MP_"; 101 const MPC_SIGNATURE: &[c_uchar; 4] = b"PCMP"; 102 const MPC_SPEC: u8 = 4; 103 const MPC_OEM: &[c_uchar; 8] = b"FC "; 104 const MPC_PRODUCT_ID: &[c_uchar; 12] = &[b'0'; 12]; 105 const BUS_TYPE_ISA: &[c_uchar; 6] = b"ISA "; 106 const APIC_VERSION: u8 = 0x14; 107 const CPU_STEPPING: u32 = 0x600; 108 const CPU_FEATURE_APIC: u32 = 0x200; 109 const CPU_FEATURE_FPU: u32 = 0x001; 110 111 fn compute_checksum<T: Copy>(v: &T) -> u8 { 112 // SAFETY: we are only reading the bytes within the size of the `T` reference `v`. 113 let v_slice = unsafe { slice::from_raw_parts(v as *const T as *const u8, mem::size_of::<T>()) }; 114 let mut checksum: u8 = 0; 115 for i in v_slice.iter() { 116 checksum = checksum.wrapping_add(*i); 117 } 118 checksum 119 } 120 121 fn mpf_intel_compute_checksum(v: &mpspec::mpf_intel) -> u8 { 122 let checksum = compute_checksum(v).wrapping_sub(v.checksum); 123 (!checksum).wrapping_add(1) 124 } 125 126 fn compute_mp_size(num_cpus: u8) -> usize { 127 mem::size_of::<MpfIntelWrapper>() 128 + mem::size_of::<MpcTableWrapper>() 129 + mem::size_of::<MpcCpuWrapper>() * (num_cpus as usize) 130 + mem::size_of::<MpcIoapicWrapper>() 131 + mem::size_of::<MpcBusWrapper>() 132 + mem::size_of::<MpcIntsrcWrapper>() * 16 133 + mem::size_of::<MpcLintsrcWrapper>() * 2 134 } 135 136 /// Performs setup of the MP table for the given `num_cpus`. 137 pub fn setup_mptable( 138 offset: GuestAddress, 139 mem: &GuestMemoryMmap, 140 num_cpus: u8, 141 topology: Option<(u8, u8, u8)>, 142 ) -> Result<()> { 143 if num_cpus > 0 { 144 let cpu_id_max = num_cpus - 1; 145 let x2apic_id_max = get_x2apic_id(cpu_id_max.into(), topology); 146 if x2apic_id_max >= MAX_SUPPORTED_CPUS { 147 return Err(Error::TooManyCpus); 148 } 149 } 150 151 // Used to keep track of the next base pointer into the MP table. 152 let mut base_mp = offset; 153 154 let mp_size = compute_mp_size(num_cpus); 155 156 if offset.unchecked_add(mp_size as u64) >= HIGH_RAM_START { 157 warn!("Skipping mptable creation due to insufficient space"); 158 return Ok(()); 159 } 160 161 let mut checksum: u8 = 0; 162 let ioapicid: u8 = MAX_SUPPORTED_CPUS as u8 + 1; 163 164 // The checked_add here ensures the all of the following base_mp.unchecked_add's will be without 165 // overflow. 166 if let Some(end_mp) = base_mp.checked_add((mp_size - 1) as u64) { 167 if !mem.address_in_range(end_mp) { 168 return Err(Error::NotEnoughMemory); 169 } 170 } else { 171 return Err(Error::AddressOverflow); 172 } 173 174 mem.read_exact_volatile_from(base_mp, &mut vec![0; mp_size].as_slice(), mp_size) 175 .map_err(Error::Clear)?; 176 177 { 178 let mut mpf_intel = MpfIntelWrapper(mpspec::mpf_intel::default()); 179 let size = mem::size_of::<MpfIntelWrapper>() as u64; 180 mpf_intel.0.signature = *SMP_MAGIC_IDENT; 181 mpf_intel.0.length = 1; 182 mpf_intel.0.specification = 4; 183 mpf_intel.0.physptr = (base_mp.raw_value() + size) as u32; 184 mpf_intel.0.checksum = mpf_intel_compute_checksum(&mpf_intel.0); 185 mem.write_obj(mpf_intel, base_mp) 186 .map_err(Error::WriteMpfIntel)?; 187 base_mp = base_mp.unchecked_add(size); 188 } 189 190 // We set the location of the mpc_table here but we can't fill it out until we have the length 191 // of the entire table later. 192 let table_base = base_mp; 193 base_mp = base_mp.unchecked_add(mem::size_of::<MpcTableWrapper>() as u64); 194 195 { 196 let size = mem::size_of::<MpcCpuWrapper>(); 197 for cpu_id in 0..num_cpus { 198 let mut mpc_cpu = MpcCpuWrapper(mpspec::mpc_cpu::default()); 199 mpc_cpu.0.type_ = mpspec::MP_PROCESSOR as u8; 200 mpc_cpu.0.apicid = get_x2apic_id(cpu_id as u32, topology) as u8; 201 mpc_cpu.0.apicver = APIC_VERSION; 202 mpc_cpu.0.cpuflag = mpspec::CPU_ENABLED as u8 203 | if cpu_id == 0 { 204 mpspec::CPU_BOOTPROCESSOR as u8 205 } else { 206 0 207 }; 208 mpc_cpu.0.cpufeature = CPU_STEPPING; 209 mpc_cpu.0.featureflag = CPU_FEATURE_APIC | CPU_FEATURE_FPU; 210 mem.write_obj(mpc_cpu, base_mp) 211 .map_err(Error::WriteMpcCpu)?; 212 base_mp = base_mp.unchecked_add(size as u64); 213 checksum = checksum.wrapping_add(compute_checksum(&mpc_cpu.0)); 214 } 215 } 216 { 217 let size = mem::size_of::<MpcBusWrapper>(); 218 let mut mpc_bus = MpcBusWrapper(mpspec::mpc_bus::default()); 219 mpc_bus.0.type_ = mpspec::MP_BUS as u8; 220 mpc_bus.0.busid = 0; 221 mpc_bus.0.bustype = *BUS_TYPE_ISA; 222 mem.write_obj(mpc_bus, base_mp) 223 .map_err(Error::WriteMpcBus)?; 224 base_mp = base_mp.unchecked_add(size as u64); 225 checksum = checksum.wrapping_add(compute_checksum(&mpc_bus.0)); 226 } 227 { 228 let size = mem::size_of::<MpcIoapicWrapper>(); 229 let mut mpc_ioapic = MpcIoapicWrapper(mpspec::mpc_ioapic::default()); 230 mpc_ioapic.0.type_ = mpspec::MP_IOAPIC as u8; 231 mpc_ioapic.0.apicid = ioapicid; 232 mpc_ioapic.0.apicver = APIC_VERSION; 233 mpc_ioapic.0.flags = mpspec::MPC_APIC_USABLE as u8; 234 mpc_ioapic.0.apicaddr = IOAPIC_START.0 as u32; 235 mem.write_obj(mpc_ioapic, base_mp) 236 .map_err(Error::WriteMpcIoapic)?; 237 base_mp = base_mp.unchecked_add(size as u64); 238 checksum = checksum.wrapping_add(compute_checksum(&mpc_ioapic.0)); 239 } 240 // Per kvm_setup_default_irq_routing() in kernel 241 for i in 0..16 { 242 let size = mem::size_of::<MpcIntsrcWrapper>(); 243 let mut mpc_intsrc = MpcIntsrcWrapper(mpspec::mpc_intsrc::default()); 244 mpc_intsrc.0.type_ = mpspec::MP_INTSRC as u8; 245 mpc_intsrc.0.irqtype = mpspec::MP_IRQ_SOURCE_TYPES_MP_INT as u8; 246 mpc_intsrc.0.irqflag = mpspec::MP_IRQDIR_DEFAULT as u16; 247 mpc_intsrc.0.srcbus = 0; 248 mpc_intsrc.0.srcbusirq = i; 249 mpc_intsrc.0.dstapic = ioapicid; 250 mpc_intsrc.0.dstirq = i; 251 mem.write_obj(mpc_intsrc, base_mp) 252 .map_err(Error::WriteMpcIntsrc)?; 253 base_mp = base_mp.unchecked_add(size as u64); 254 checksum = checksum.wrapping_add(compute_checksum(&mpc_intsrc.0)); 255 } 256 { 257 let size = mem::size_of::<MpcLintsrcWrapper>(); 258 let mut mpc_lintsrc = MpcLintsrcWrapper(mpspec::mpc_lintsrc::default()); 259 mpc_lintsrc.0.type_ = mpspec::MP_LINTSRC as u8; 260 mpc_lintsrc.0.irqtype = mpspec::MP_IRQ_SOURCE_TYPES_MP_EXT_INT as u8; 261 mpc_lintsrc.0.irqflag = mpspec::MP_IRQDIR_DEFAULT as u16; 262 mpc_lintsrc.0.srcbusid = 0; 263 mpc_lintsrc.0.srcbusirq = 0; 264 mpc_lintsrc.0.destapic = 0; 265 mpc_lintsrc.0.destapiclint = 0; 266 mem.write_obj(mpc_lintsrc, base_mp) 267 .map_err(Error::WriteMpcLintsrc)?; 268 base_mp = base_mp.unchecked_add(size as u64); 269 checksum = checksum.wrapping_add(compute_checksum(&mpc_lintsrc.0)); 270 } 271 { 272 let size = mem::size_of::<MpcLintsrcWrapper>(); 273 let mut mpc_lintsrc = MpcLintsrcWrapper(mpspec::mpc_lintsrc::default()); 274 mpc_lintsrc.0.type_ = mpspec::MP_LINTSRC as u8; 275 mpc_lintsrc.0.irqtype = mpspec::MP_IRQ_SOURCE_TYPES_MP_NMI as u8; 276 mpc_lintsrc.0.irqflag = mpspec::MP_IRQDIR_DEFAULT as u16; 277 mpc_lintsrc.0.srcbusid = 0; 278 mpc_lintsrc.0.srcbusirq = 0; 279 mpc_lintsrc.0.destapic = 0xFF; /* to all local APICs */ 280 mpc_lintsrc.0.destapiclint = 1; 281 mem.write_obj(mpc_lintsrc, base_mp) 282 .map_err(Error::WriteMpcLintsrc)?; 283 base_mp = base_mp.unchecked_add(size as u64); 284 checksum = checksum.wrapping_add(compute_checksum(&mpc_lintsrc.0)); 285 } 286 287 // At this point we know the size of the mp_table. 288 let table_end = base_mp; 289 290 { 291 let mut mpc_table = MpcTableWrapper(mpspec::mpc_table::default()); 292 mpc_table.0.signature = *MPC_SIGNATURE; 293 mpc_table.0.length = table_end.unchecked_offset_from(table_base) as u16; 294 mpc_table.0.spec = MPC_SPEC; 295 mpc_table.0.oem = *MPC_OEM; 296 mpc_table.0.productid = *MPC_PRODUCT_ID; 297 mpc_table.0.lapic = APIC_START.0 as u32; 298 checksum = checksum.wrapping_add(compute_checksum(&mpc_table.0)); 299 mpc_table.0.checksum = (!checksum).wrapping_add(1); 300 mem.write_obj(mpc_table, table_base) 301 .map_err(Error::WriteMpcTable)?; 302 } 303 304 Ok(()) 305 } 306 307 #[cfg(test)] 308 mod tests { 309 use vm_memory::{ 310 bitmap::BitmapSlice, GuestUsize, VolatileMemoryError, VolatileSlice, WriteVolatile, 311 }; 312 313 use super::*; 314 use crate::layout::MPTABLE_START; 315 316 fn table_entry_size(type_: u8) -> usize { 317 match type_ as u32 { 318 mpspec::MP_PROCESSOR => mem::size_of::<MpcCpuWrapper>(), 319 mpspec::MP_BUS => mem::size_of::<MpcBusWrapper>(), 320 mpspec::MP_IOAPIC => mem::size_of::<MpcIoapicWrapper>(), 321 mpspec::MP_INTSRC => mem::size_of::<MpcIntsrcWrapper>(), 322 mpspec::MP_LINTSRC => mem::size_of::<MpcLintsrcWrapper>(), 323 _ => panic!("unrecognized mpc table entry type: {type_}"), 324 } 325 } 326 327 #[test] 328 fn bounds_check() { 329 let num_cpus = 4; 330 let mem = 331 GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus))]).unwrap(); 332 333 setup_mptable(MPTABLE_START, &mem, num_cpus, None).unwrap(); 334 } 335 336 #[test] 337 fn bounds_check_fails() { 338 let num_cpus = 4; 339 let mem = GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus) - 1)]) 340 .unwrap(); 341 342 assert!(setup_mptable(MPTABLE_START, &mem, num_cpus, None).is_err()); 343 } 344 345 #[test] 346 fn mpf_intel_checksum() { 347 let num_cpus = 1; 348 let mem = 349 GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus))]).unwrap(); 350 351 setup_mptable(MPTABLE_START, &mem, num_cpus, None).unwrap(); 352 353 let mpf_intel: MpfIntelWrapper = mem.read_obj(MPTABLE_START).unwrap(); 354 355 assert_eq!( 356 mpf_intel_compute_checksum(&mpf_intel.0), 357 mpf_intel.0.checksum 358 ); 359 } 360 361 #[test] 362 fn mpc_table_checksum() { 363 let num_cpus = 4; 364 let mem = 365 GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(num_cpus))]).unwrap(); 366 367 setup_mptable(MPTABLE_START, &mem, num_cpus, None).unwrap(); 368 369 let mpf_intel: MpfIntelWrapper = mem.read_obj(MPTABLE_START).unwrap(); 370 let mpc_offset = GuestAddress(mpf_intel.0.physptr as GuestUsize); 371 let mpc_table: MpcTableWrapper = mem.read_obj(mpc_offset).unwrap(); 372 373 struct Sum(u8); 374 impl WriteVolatile for Sum { 375 fn write_volatile<B: BitmapSlice>( 376 &mut self, 377 buf: &VolatileSlice<B>, 378 ) -> result::Result<usize, VolatileMemoryError> { 379 let mut tmp = vec![0u8; buf.len()]; 380 tmp.write_all_volatile(buf)?; 381 382 for v in tmp.iter() { 383 self.0 = self.0.wrapping_add(*v); 384 } 385 386 Ok(buf.len()) 387 } 388 } 389 390 let mut sum = Sum(0); 391 mem.write_volatile_to(mpc_offset, &mut sum, mpc_table.0.length as usize) 392 .unwrap(); 393 assert_eq!(sum.0, 0); 394 } 395 396 #[test] 397 fn cpu_entry_count() { 398 let mem = GuestMemoryMmap::from_ranges(&[( 399 MPTABLE_START, 400 compute_mp_size(MAX_SUPPORTED_CPUS as u8), 401 )]) 402 .unwrap(); 403 404 for i in 0..MAX_SUPPORTED_CPUS as u8 { 405 setup_mptable(MPTABLE_START, &mem, i, None).unwrap(); 406 407 let mpf_intel: MpfIntelWrapper = mem.read_obj(MPTABLE_START).unwrap(); 408 let mpc_offset = GuestAddress(mpf_intel.0.physptr as GuestUsize); 409 let mpc_table: MpcTableWrapper = mem.read_obj(mpc_offset).unwrap(); 410 let mpc_end = mpc_offset 411 .checked_add(mpc_table.0.length as GuestUsize) 412 .unwrap(); 413 414 let mut entry_offset = mpc_offset 415 .checked_add(mem::size_of::<MpcTableWrapper>() as GuestUsize) 416 .unwrap(); 417 let mut cpu_count = 0; 418 while entry_offset < mpc_end { 419 let entry_type: u8 = mem.read_obj(entry_offset).unwrap(); 420 entry_offset = entry_offset 421 .checked_add(table_entry_size(entry_type) as GuestUsize) 422 .unwrap(); 423 assert!(entry_offset <= mpc_end); 424 if entry_type as u32 == mpspec::MP_PROCESSOR { 425 cpu_count += 1; 426 } 427 } 428 assert_eq!(cpu_count, i); 429 } 430 } 431 432 #[test] 433 fn cpu_entry_count_max() { 434 let cpus = MAX_SUPPORTED_CPUS + 1; 435 let mem = 436 GuestMemoryMmap::from_ranges(&[(MPTABLE_START, compute_mp_size(cpus as u8))]).unwrap(); 437 438 let result = setup_mptable(MPTABLE_START, &mem, cpus as u8, None); 439 assert!(result.is_err()); 440 } 441 } 442