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