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