1pub mod android;
8pub mod fdt;
9pub mod pstore;
10pub mod serial;
11
12pub mod sys;
13
14use std::collections::BTreeMap;
15use std::error::Error as StdError;
16use std::fs::File;
17use std::io;
18use std::ops::Deref;
19use std::path::PathBuf;
20use std::str::FromStr;
21use std::sync::mpsc;
22use std::sync::mpsc::SendError;
23use std::sync::Arc;
24
25use acpi_tables::sdt::SDT;
26use base::syslog;
27use base::AsRawDescriptors;
28use base::FileGetLen;
29use base::FileReadWriteAtVolatile;
30use base::RecvTube;
31use base::SendTube;
32use base::Tube;
33use devices::virtio::VirtioDevice;
34use devices::BarRange;
35use devices::Bus;
36use devices::BusDevice;
37use devices::BusDeviceObj;
38use devices::BusError;
39use devices::BusResumeDevice;
40use devices::FwCfgParameters;
41use devices::GpeScope;
42use devices::HotPlugBus;
43use devices::IrqChip;
44use devices::IrqEventSource;
45use devices::PciAddress;
46use devices::PciBus;
47use devices::PciDevice;
48use devices::PciDeviceError;
49use devices::PciInterruptPin;
50use devices::PciRoot;
51use devices::PciRootCommand;
52use devices::PreferredIrq;
53#[cfg(any(target_os = "android", target_os = "linux"))]
54use devices::ProxyDevice;
55use devices::SerialHardware;
56use devices::SerialParameters;
57pub use fdt::apply_device_tree_overlays;
58pub use fdt::DtbOverlay;
59#[cfg(feature = "gdb")]
60use gdbstub::arch::Arch;
61pub use hypervisor::CpuConfigArch;
62pub use hypervisor::HypervisorArch;
63use hypervisor::MemCacheType;
64pub use hypervisor::VcpuArch;
65pub use hypervisor::VcpuInitArch;
66use hypervisor::Vm;
67pub use hypervisor::VmArch;
68#[cfg(windows)]
69use jail::FakeMinijailStub as Minijail;
70#[cfg(any(target_os = "android", target_os = "linux"))]
71use minijail::Minijail;
72use remain::sorted;
73use resources::SystemAllocator;
74use resources::SystemAllocatorConfig;
75use serde::de::Visitor;
76use serde::Deserialize;
77use serde::Serialize;
78use serde_keyvalue::FromKeyValues;
79pub use serial::add_serial_devices;
80pub use serial::get_serial_cmdline;
81pub use serial::set_default_serial_parameters;
82pub use serial::GetSerialCmdlineError;
83pub use serial::SERIAL_ADDR;
84use sync::Condvar;
85use sync::Mutex;
86use thiserror::Error;
87use uuid::Uuid;
88use vm_control::BatControl;
89use vm_control::BatteryType;
90use vm_control::PmResource;
91use vm_memory::GuestAddress;
92use vm_memory::GuestMemory;
93use vm_memory::GuestMemoryError;
94use vm_memory::MemoryRegionInformation;
95use vm_memory::MemoryRegionOptions;
96
97cfg_if::cfg_if! {
98 if #[cfg(target_arch = "aarch64")] {
99 pub use devices::IrqChipAArch64 as IrqChipArch;
100 #[cfg(feature = "gdb")]
101 pub use gdbstub_arch::aarch64::AArch64 as GdbArch;
102 } else if #[cfg(target_arch = "riscv64")] {
103 pub use devices::IrqChipRiscv64 as IrqChipArch;
104 #[cfg(feature = "gdb")]
105 pub use gdbstub_arch::riscv::Riscv64 as GdbArch;
106 } else if #[cfg(target_arch = "x86_64")] {
107 pub use devices::IrqChipX86_64 as IrqChipArch;
108 #[cfg(feature = "gdb")]
109 pub use gdbstub_arch::x86::X86_64_SSE as GdbArch;
110 }
111}
112
113pub enum VmImage {
114 Kernel(File),
115 Bios(File),
116}
117
118#[derive(Clone, Debug, Deserialize, Serialize, FromKeyValues, PartialEq, Eq)]
119#[serde(deny_unknown_fields, rename_all = "kebab-case")]
120pub struct Pstore {
121 pub path: PathBuf,
122 pub size: u32,
123}
124
125#[derive(Clone, Copy, Debug, Serialize, Deserialize, FromKeyValues)]
126#[serde(deny_unknown_fields, rename_all = "kebab-case")]
127pub enum FdtPosition {
128 Start,
130 End,
132 AfterPayload,
134}
135
136#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
138pub struct CpuSet(Vec<usize>);
139
140impl CpuSet {
141 pub fn new<I: IntoIterator<Item = usize>>(cpus: I) -> Self {
142 CpuSet(cpus.into_iter().collect())
143 }
144
145 pub fn iter(&self) -> std::slice::Iter<'_, usize> {
146 self.0.iter()
147 }
148}
149
150impl FromIterator<usize> for CpuSet {
151 fn from_iter<T>(iter: T) -> Self
152 where
153 T: IntoIterator<Item = usize>,
154 {
155 CpuSet::new(iter)
156 }
157}
158
159#[cfg(target_arch = "aarch64")]
160fn sve_auto_default() -> bool {
161 true
162}
163
164#[cfg(target_arch = "aarch64")]
166#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
167#[serde(deny_unknown_fields, rename_all = "kebab-case")]
168pub struct SveConfig {
169 #[serde(default = "sve_auto_default")]
171 pub auto: bool,
172}
173
174#[cfg(target_arch = "aarch64")]
175impl Default for SveConfig {
176 fn default() -> Self {
177 SveConfig {
178 auto: sve_auto_default(),
179 }
180 }
181}
182
183#[cfg(all(target_os = "android", target_arch = "aarch64"))]
187#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize, FromKeyValues)]
188#[serde(deny_unknown_fields, rename_all = "kebab-case")]
189pub struct FfaConfig {
190 #[serde(default)]
192 pub auto: bool,
193}
194
195fn parse_cpu_range(s: &str, cpuset: &mut Vec<usize>) -> Result<(), String> {
196 fn parse_cpu(s: &str) -> Result<usize, String> {
197 s.parse()
198 .map_err(|_| format!("invalid CPU index {s} - index must be a non-negative integer"))
199 }
200
201 let (first_cpu, last_cpu) = match s.split_once('-') {
202 Some((first_cpu, last_cpu)) => {
203 let first_cpu = parse_cpu(first_cpu)?;
204 let last_cpu = parse_cpu(last_cpu)?;
205
206 if last_cpu < first_cpu {
207 return Err(format!(
208 "invalid CPU range {s} - ranges must be from low to high"
209 ));
210 }
211 (first_cpu, last_cpu)
212 }
213 None => {
214 let cpu = parse_cpu(s)?;
215 (cpu, cpu)
216 }
217 };
218
219 cpuset.extend(first_cpu..=last_cpu);
220
221 Ok(())
222}
223
224impl FromStr for CpuSet {
225 type Err = String;
226
227 fn from_str(s: &str) -> Result<Self, Self::Err> {
228 let mut cpuset = Vec::new();
229 for part in s.split(',') {
230 parse_cpu_range(part, &mut cpuset)?;
231 }
232 Ok(CpuSet::new(cpuset))
233 }
234}
235
236impl Deref for CpuSet {
237 type Target = Vec<usize>;
238
239 fn deref(&self) -> &Self::Target {
240 &self.0
241 }
242}
243
244impl IntoIterator for CpuSet {
245 type Item = usize;
246 type IntoIter = std::vec::IntoIter<Self::Item>;
247
248 fn into_iter(self) -> Self::IntoIter {
249 self.0.into_iter()
250 }
251}
252
253#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
255pub enum DevicePowerManagerConfig {
256 PkvmHvc,
259}
260
261impl FromStr for DevicePowerManagerConfig {
262 type Err = String;
263
264 fn from_str(s: &str) -> Result<Self, Self::Err> {
265 match s {
266 "pkvm-hvc" => Ok(Self::PkvmHvc),
267 _ => Err(format!("DevicePowerManagerConfig '{s}' not supported")),
268 }
269 }
270}
271
272impl<'de> Deserialize<'de> for CpuSet {
275 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
276 where
277 D: serde::Deserializer<'de>,
278 {
279 struct CpuSetVisitor;
280 impl<'de> Visitor<'de> for CpuSetVisitor {
281 type Value = CpuSet;
282
283 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
284 formatter.write_str("CpuSet")
285 }
286
287 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
288 where
289 A: serde::de::SeqAccess<'de>,
290 {
291 #[derive(Deserialize)]
292 #[serde(untagged)]
293 enum CpuSetValue<'a> {
294 Single(usize),
295 Range(&'a str),
296 }
297
298 let mut cpus = Vec::new();
299 while let Some(cpuset) = seq.next_element::<CpuSetValue>()? {
300 match cpuset {
301 CpuSetValue::Single(cpu) => cpus.push(cpu),
302 CpuSetValue::Range(range) => {
303 parse_cpu_range(range, &mut cpus).map_err(serde::de::Error::custom)?;
304 }
305 }
306 }
307
308 Ok(CpuSet::new(cpus))
309 }
310 }
311
312 deserializer.deserialize_seq(CpuSetVisitor)
313 }
314}
315
316impl Serialize for CpuSet {
318 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
319 where
320 S: serde::Serializer,
321 {
322 use serde::ser::SerializeSeq;
323
324 let mut seq = serializer.serialize_seq(None)?;
325
326 let mut serialize_range = |start: usize, end: usize| -> Result<(), S::Error> {
328 if start == end {
329 seq.serialize_element(&start)?;
330 } else {
331 seq.serialize_element(&format!("{start}-{end}"))?;
332 }
333
334 Ok(())
335 };
336
337 let mut range = None;
339 for core in &self.0 {
340 range = match range {
341 None => Some((core, core)),
342 Some((start, end)) if *end == *core - 1 => Some((start, core)),
343 Some((start, end)) => {
344 serialize_range(*start, *end)?;
345 Some((core, core))
346 }
347 };
348 }
349
350 if let Some((start, end)) = range {
351 serialize_range(*start, *end)?;
352 }
353
354 seq.end()
355 }
356}
357
358#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
360pub enum VcpuAffinity {
361 Global(CpuSet),
363 PerVcpu(BTreeMap<usize, CpuSet>),
368}
369
370#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
372pub struct MemoryRegionConfig {
373 pub start: u64,
374 pub size: Option<u64>,
375}
376
377#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
379pub struct PciConfig {
380 #[cfg(target_arch = "aarch64")]
382 pub cam: Option<MemoryRegionConfig>,
383 #[cfg(target_arch = "x86_64")]
385 pub ecam: Option<MemoryRegionConfig>,
386 pub mem: Option<MemoryRegionConfig>,
388}
389
390pub const DEFAULT_CPU_CAPACITY: u32 = 1024;
391
392#[sorted]
393#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
394pub struct VcpuProperties {
395 pub capacity: Option<u32>,
396 pub dynamic_power_coefficient: Option<u32>,
397 pub frequencies: Vec<u32>,
398 #[cfg(all(
399 target_arch = "aarch64",
400 any(target_os = "android", target_os = "linux")
401 ))]
402 pub normalized_cpu_ipc_ratio: Option<u32>,
403 #[cfg(all(
404 target_arch = "aarch64",
405 any(target_os = "android", target_os = "linux")
406 ))]
407 pub vcpu_domain: Option<u32>,
408 #[cfg(all(
409 target_arch = "aarch64",
410 any(target_os = "android", target_os = "linux")
411 ))]
412 pub vcpu_domain_path: Option<PathBuf>,
413}
414
415pub fn derive_vcpu_properties(
417 vcpu_count: usize,
418 vcpu_capacity: &std::collections::BTreeMap<usize, u32>,
419 dynamic_power_coefficient: &std::collections::BTreeMap<usize, u32>,
420 vcpu_frequencies: &std::collections::BTreeMap<usize, Vec<u32>>,
421 #[cfg(all(
422 target_arch = "aarch64",
423 any(target_os = "android", target_os = "linux")
424 ))]
425 normalized_cpu_ipc_ratio: &std::collections::BTreeMap<usize, u32>,
426 #[cfg(all(
427 target_arch = "aarch64",
428 any(target_os = "android", target_os = "linux")
429 ))]
430 vcpu_domain: &std::collections::BTreeMap<usize, u32>,
431 #[cfg(all(
432 target_arch = "aarch64",
433 any(target_os = "android", target_os = "linux")
434 ))]
435 vcpu_domain_path: &std::collections::BTreeMap<usize, std::path::PathBuf>,
436) -> std::collections::BTreeMap<usize, VcpuProperties> {
437 let mut vcpu_properties = std::collections::BTreeMap::new();
438 for vcpu_id in 0..vcpu_count {
439 let vcpu_prop_capacity = vcpu_capacity.get(&vcpu_id).copied();
440
441 vcpu_properties.insert(
442 vcpu_id,
443 VcpuProperties {
444 capacity: vcpu_prop_capacity,
445 frequencies: vcpu_frequencies.get(&vcpu_id).cloned().unwrap_or_default(),
446 dynamic_power_coefficient: dynamic_power_coefficient.get(&vcpu_id).copied(),
447 #[cfg(all(
448 target_arch = "aarch64",
449 any(target_os = "android", target_os = "linux")
450 ))]
451 normalized_cpu_ipc_ratio: normalized_cpu_ipc_ratio.get(&vcpu_id).copied(),
452 #[cfg(all(
453 target_arch = "aarch64",
454 any(target_os = "android", target_os = "linux")
455 ))]
456 vcpu_domain: vcpu_domain.get(&vcpu_id).copied(),
457 #[cfg(all(
458 target_arch = "aarch64",
459 any(target_os = "android", target_os = "linux")
460 ))]
461 vcpu_domain_path: vcpu_domain_path.get(&vcpu_id).cloned(),
462 },
463 );
464 }
465 vcpu_properties
466}
467
468#[sorted]
471pub struct VmComponents {
472 pub acpi_sdts: Vec<SDT>,
473 pub android_fstab: Option<File>,
474 pub boot_cpu: usize,
475 pub bootorder_fw_cfg_blob: Vec<u8>,
476 #[cfg(target_arch = "x86_64")]
477 pub break_linux_pci_config_io: bool,
478
479 pub delay_rt: bool,
480 pub dev_pm: Option<DevicePowerManagerConfig>,
481 pub extra_kernel_params: Vec<String>,
482 #[cfg(target_arch = "x86_64")]
483 pub force_s2idle: bool,
484 pub fw_cfg_enable: bool,
485 pub fw_cfg_parameters: Vec<FwCfgParameters>,
486 pub host_cpu_topology: bool,
487 pub hugepages: bool,
488 pub hv_cfg: hypervisor::Config,
489 pub initrd_image: Option<File>,
490 pub itmt: bool,
491 pub memory_size: u64,
492 #[cfg(target_arch = "aarch64")]
493 pub nested: hypervisor::NestedMode,
494 pub no_i8042: bool,
495 pub no_rtc: bool,
496 pub no_smt: bool,
497
498 pub pci_config: PciConfig,
499 pub pflash_block_size: u32,
500 pub pflash_image: Option<File>,
501 pub pstore: Option<Pstore>,
502 pub pvm_fw: Option<File>,
505 pub rt_cpus: CpuSet,
506 #[cfg(target_arch = "x86_64")]
507 pub smbios: SmbiosOptions,
508 pub smccc_trng: bool,
509 #[cfg(target_arch = "aarch64")]
510 pub sve_config: SveConfig,
511 pub swiotlb: Option<u64>,
512 pub vcpu_affinity: Option<VcpuAffinity>,
513 pub vcpu_clusters: Vec<CpuSet>,
515 pub vcpu_properties: BTreeMap<usize, VcpuProperties>,
516 #[cfg(any(target_os = "android", target_os = "linux"))]
517 pub vfio_platform_pm: bool,
518 #[cfg(all(
519 target_arch = "aarch64",
520 any(target_os = "android", target_os = "linux")
521 ))]
522 pub virt_cpufreq_v2: bool,
523 pub vm_image: VmImage,
524}
525
526#[sorted]
528pub struct RunnableLinuxVm {
529 pub bat_control: Option<BatControl>,
530 pub delay_rt: bool,
531 pub devices_thread: Option<std::thread::JoinHandle<()>>,
532 pub hotplug_bus: BTreeMap<u8, Arc<Mutex<dyn HotPlugBus>>>,
533 pub hypercall_bus: Arc<Bus>,
534 pub io_bus: Arc<Bus>,
535 pub irq_chip: Arc<dyn IrqChipArch>,
536 pub mmio_bus: Arc<Bus>,
537 pub no_smt: bool,
538 pub pid_debug_label_map: BTreeMap<u32, String>,
539 #[cfg(any(target_os = "android", target_os = "linux"))]
540 pub platform_devices: Vec<Arc<Mutex<dyn BusDevice>>>,
541 pub pm: Option<Arc<Mutex<dyn PmResource + Send>>>,
542 pub resume_notify_devices: Vec<Arc<Mutex<dyn BusResumeDevice>>>,
544 pub root_config: Arc<Mutex<PciRoot>>,
545 pub rt_cpus: CpuSet,
546 pub suspend_tube: (Arc<Mutex<SendTube>>, RecvTube),
547 pub vcpu_affinity: Option<VcpuAffinity>,
548 pub vcpu_count: usize,
549 pub vcpu_init: Vec<VcpuInitArch>,
550 pub vcpus: Option<Vec<Arc<dyn VcpuArch>>>,
553 pub vm: Arc<dyn VmArch>,
554 pub vm_request_tubes: Vec<Tube>,
555}
556
557pub struct VirtioDeviceStub {
559 pub dev: Box<dyn VirtioDevice>,
560 pub jail: Option<Minijail>,
561}
562
563pub trait LinuxArch {
566 type Error: StdError;
567 type ArchMemoryLayout;
568
569 fn arch_memory_layout(
572 components: &VmComponents,
573 ) -> std::result::Result<Self::ArchMemoryLayout, Self::Error>;
574
575 fn guest_memory_layout(
582 components: &VmComponents,
583 arch_memory_layout: &Self::ArchMemoryLayout,
584 hypervisor: &impl hypervisor::Hypervisor,
585 ) -> std::result::Result<Vec<(GuestAddress, u64, MemoryRegionOptions)>, Self::Error>;
586
587 fn get_system_allocator_config(
597 vm: &dyn Vm,
598 arch_memory_layout: &Self::ArchMemoryLayout,
599 ) -> SystemAllocatorConfig;
600
601 fn build_vm(
622 components: VmComponents,
623 arch_memory_layout: &Self::ArchMemoryLayout,
624 vm_evt_wrtube: &SendTube,
625 system_allocator: &mut SystemAllocator,
626 serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
627 serial_jail: Option<Minijail>,
628 battery: (Option<BatteryType>, Option<Minijail>),
629 vm: Arc<dyn VmArch>,
630 ramoops_region: Option<pstore::RamoopsRegion>,
631 devices: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
632 irq_chip: Arc<dyn IrqChipArch>,
633 vcpu_ids: &mut Vec<usize>,
634 dump_device_tree_blob: Option<PathBuf>,
635 debugcon_jail: Option<Minijail>,
636 #[cfg(target_arch = "x86_64")] pflash_jail: Option<Minijail>,
637 #[cfg(target_arch = "x86_64")] fw_cfg_jail: Option<Minijail>,
638 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
639 guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
640 device_tree_overlays: Vec<DtbOverlay>,
641 fdt_position: Option<FdtPosition>,
642 no_pmu: bool,
643 ) -> std::result::Result<RunnableLinuxVm, Self::Error>;
644
645 fn configure_vcpu(
658 vm: &dyn Vm,
659 hypervisor: &dyn HypervisorArch,
660 irq_chip: &dyn IrqChipArch,
661 vcpu: &dyn VcpuArch,
662 vcpu_init: VcpuInitArch,
663 vcpu_id: usize,
664 num_vcpus: usize,
665 cpu_config: Option<CpuConfigArch>,
666 ) -> Result<(), Self::Error>;
667
668 fn register_pci_device(
670 linux: &mut RunnableLinuxVm,
671 device: Box<dyn PciDevice>,
672 #[cfg(any(target_os = "android", target_os = "linux"))] minijail: Option<Minijail>,
673 resources: &mut SystemAllocator,
674 hp_control_tube: &mpsc::Sender<PciRootCommand>,
675 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
676 ) -> Result<PciAddress, Self::Error>;
677
678 fn get_host_cpu_frequencies_khz() -> Result<BTreeMap<usize, Vec<u32>>, Self::Error>;
680
681 fn get_host_cpu_max_freq_khz() -> Result<BTreeMap<usize, u32>, Self::Error>;
683
684 fn get_host_cpu_capacity() -> Result<BTreeMap<usize, u32>, Self::Error>;
686
687 fn get_host_cpu_clusters() -> Result<Vec<CpuSet>, Self::Error>;
689}
690
691#[cfg(feature = "gdb")]
692pub trait GdbOps {
693 type Error: StdError;
694
695 fn read_registers(vcpu: &dyn VcpuArch) -> Result<<GdbArch as Arch>::Registers, Self::Error>;
697
698 fn write_registers(
700 vcpu: &dyn VcpuArch,
701 regs: &<GdbArch as Arch>::Registers,
702 ) -> Result<(), Self::Error>;
703
704 fn read_memory(
706 vcpu: &dyn VcpuArch,
707 guest_mem: &GuestMemory,
708 vaddr: GuestAddress,
709 len: usize,
710 ) -> Result<Vec<u8>, Self::Error>;
711
712 fn write_memory(
714 vcpu: &dyn VcpuArch,
715 guest_mem: &GuestMemory,
716 vaddr: GuestAddress,
717 buf: &[u8],
718 ) -> Result<(), Self::Error>;
719
720 fn read_register(
724 vcpu: &dyn VcpuArch,
725 reg_id: <GdbArch as Arch>::RegId,
726 ) -> Result<Vec<u8>, Self::Error>;
727
728 fn write_register(
730 vcpu: &dyn VcpuArch,
731 reg_id: <GdbArch as Arch>::RegId,
732 data: &[u8],
733 ) -> Result<(), Self::Error>;
734
735 fn enable_singlestep(vcpu: &dyn VcpuArch) -> Result<(), Self::Error>;
737
738 fn get_max_hw_breakpoints(vcpu: &dyn VcpuArch) -> Result<usize, Self::Error>;
740
741 fn set_hw_breakpoints(
743 vcpu: &dyn VcpuArch,
744 breakpoints: &[GuestAddress],
745 ) -> Result<(), Self::Error>;
746}
747
748#[sorted]
750#[derive(Error, Debug)]
751pub enum DeviceRegistrationError {
752 #[error("no more addresses are available")]
754 AddrsExhausted,
755 #[error("Allocating device addresses: {0}")]
757 AllocateDeviceAddrs(PciDeviceError),
758 #[error("Allocating IO addresses: {0}")]
760 AllocateIoAddrs(PciDeviceError),
761 #[error("Allocating IO resource: {0}")]
763 AllocateIoResource(resources::Error),
764 #[error("Allocating IRQ number")]
766 AllocateIrq,
767 #[cfg(any(target_os = "android", target_os = "linux"))]
769 #[error("Allocating IRQ resource: {0}")]
770 AllocateIrqResource(devices::vfio::VfioError),
771 #[error("failed to attach the device to its power domain: {0}")]
772 AttachDevicePowerDomain(anyhow::Error),
773 #[error("pci topology is broken")]
775 BrokenPciTopology,
776 #[cfg(any(target_os = "android", target_os = "linux"))]
778 #[error("failed to clone jail: {0}")]
779 CloneJail(minijail::Error),
780 #[error("unable to add device to kernel command line: {0}")]
782 Cmdline(kernel_cmdline::Error),
783 #[error("failed to configure window size: {0}")]
785 ConfigureWindowSize(PciDeviceError),
786 #[error("failed to create pipe: {0}")]
788 CreatePipe(base::Error),
789 #[error("failed to create pci root: {0}")]
791 CreateRoot(anyhow::Error),
792 #[error("failed to create serial device: {0}")]
794 CreateSerialDevice(devices::SerialError),
795 #[error("failed to create tube: {0}")]
797 CreateTube(base::TubeError),
798 #[error("failed to clone event: {0}")]
800 EventClone(base::Error),
801 #[error("failed to create event: {0}")]
803 EventCreate(base::Error),
804 #[error("failed to generate ACPI content")]
806 GenerateAcpi,
807 #[error("no more IRQs are available")]
809 IrqsExhausted,
810 #[error("cannot match VFIO device to DT node due to a missing symbol")]
812 MissingDeviceTreeSymbol,
813 #[error("missing required serial device {0}")]
815 MissingRequiredSerialDevice(u8),
816 #[error("failed to add to mmio bus: {0}")]
818 MmioInsert(BusError),
819 #[error("failed to insert device into PCI root: {0}")]
821 PciRootAddDevice(PciDeviceError),
822 #[cfg(any(target_os = "android", target_os = "linux"))]
823 #[error("failed to create proxy device: {0}")]
825 ProxyDeviceCreation(devices::ProxyError),
826 #[cfg(any(target_os = "android", target_os = "linux"))]
827 #[error("failed to register battery device to VM: {0}")]
829 RegisterBattery(devices::BatteryError),
830 #[error("failed to register PCI device to pci root bus")]
832 RegisterDevice(SendError<PciRootCommand>),
833 #[error("could not register PCI device capabilities: {0}")]
835 RegisterDeviceCapabilities(PciDeviceError),
836 #[error("failed to register ioevent to VM: {0}")]
838 RegisterIoevent(base::Error),
839 #[error("failed to register irq event to VM: {0}")]
841 RegisterIrqfd(base::Error),
842 #[error("Setting up VFIO platform IRQ: {0}")]
844 SetupVfioPlatformIrq(anyhow::Error),
845}
846
847pub fn configure_pci_device(
849 linux: &mut RunnableLinuxVm,
850 mut device: Box<dyn PciDevice>,
851 #[cfg(any(target_os = "android", target_os = "linux"))] jail: Option<Minijail>,
852 resources: &mut SystemAllocator,
853 hp_control_tube: &mpsc::Sender<PciRootCommand>,
854 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
855) -> Result<PciAddress, DeviceRegistrationError> {
856 let pci_address = device
858 .allocate_address(resources)
859 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
860
861 let mmio_ranges = device
863 .allocate_io_bars(resources)
864 .map_err(DeviceRegistrationError::AllocateIoAddrs)?;
865
866 let device_ranges = device
868 .allocate_device_bars(resources)
869 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
870
871 if let Some(pci_bus) = device.get_new_pci_bus() {
873 hp_control_tube
874 .send(PciRootCommand::AddBridge(pci_bus))
875 .map_err(DeviceRegistrationError::RegisterDevice)?;
876 let bar_ranges = Vec::new();
877 device
878 .configure_bridge_window(resources, &bar_ranges)
879 .map_err(DeviceRegistrationError::ConfigureWindowSize)?;
880 }
881
882 let intx_event = devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
884
885 if let PreferredIrq::Fixed { pin, gsi } = device.preferred_irq() {
886 resources.reserve_irq(gsi);
887
888 device.assign_irq(
889 intx_event
890 .try_clone()
891 .map_err(DeviceRegistrationError::EventClone)?,
892 pin,
893 gsi,
894 );
895
896 linux
897 .irq_chip
898 .register_level_irq_event(gsi, &intx_event, IrqEventSource::from_device(&device))
899 .map_err(DeviceRegistrationError::RegisterIrqfd)?;
900 }
901
902 let mut keep_rds = device.keep_rds();
903 syslog::push_descriptors(&mut keep_rds);
904 cros_tracing::push_descriptors!(&mut keep_rds);
905 metrics::push_descriptors(&mut keep_rds);
906
907 device
908 .register_device_capabilities()
909 .map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?;
910
911 #[cfg(any(target_os = "android", target_os = "linux"))]
912 let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
913 let proxy = ProxyDevice::new(
914 device,
915 jail,
916 keep_rds,
917 #[cfg(feature = "swap")]
918 swap_controller,
919 )
920 .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
921 linux
922 .pid_debug_label_map
923 .insert(proxy.pid() as u32, proxy.debug_label());
924 Arc::new(Mutex::new(proxy))
925 } else {
926 device.on_sandboxed();
927 Arc::new(Mutex::new(device))
928 };
929
930 #[cfg(windows)]
931 let arced_dev = {
932 device.on_sandboxed();
933 Arc::new(Mutex::new(device))
934 };
935
936 #[cfg(any(target_os = "android", target_os = "linux"))]
937 hp_control_tube
938 .send(PciRootCommand::Add(pci_address, arced_dev.clone()))
939 .map_err(DeviceRegistrationError::RegisterDevice)?;
940
941 for range in &mmio_ranges {
942 linux
943 .mmio_bus
944 .insert(arced_dev.clone(), range.addr, range.size)
945 .map_err(DeviceRegistrationError::MmioInsert)?;
946 }
947
948 for range in &device_ranges {
949 linux
950 .mmio_bus
951 .insert(arced_dev.clone(), range.addr, range.size)
952 .map_err(DeviceRegistrationError::MmioInsert)?;
953 }
954
955 Ok(pci_address)
956}
957
958fn generate_pci_topology(
960 parent_bus: Arc<Mutex<PciBus>>,
961 resources: &mut SystemAllocator,
962 io_ranges: &mut BTreeMap<usize, Vec<BarRange>>,
963 device_ranges: &mut BTreeMap<usize, Vec<BarRange>>,
964 device_addrs: &[PciAddress],
965 devices: &mut Vec<(Box<dyn PciDevice>, Option<Minijail>)>,
966) -> Result<(Vec<BarRange>, u8), DeviceRegistrationError> {
967 let mut bar_ranges = Vec::new();
968 let bus_num = parent_bus.lock().get_bus_num();
969 let mut subordinate_bus = bus_num;
970 for (dev_idx, addr) in device_addrs.iter().enumerate() {
971 if addr.bus == bus_num {
973 if let Some(child_bus) = devices[dev_idx].0.get_new_pci_bus() {
976 let (child_bar_ranges, child_sub_bus) = generate_pci_topology(
977 child_bus.clone(),
978 resources,
979 io_ranges,
980 device_ranges,
981 device_addrs,
982 devices,
983 )?;
984 let device = &mut devices[dev_idx].0;
985 parent_bus
986 .lock()
987 .add_child_bus(child_bus.clone())
988 .map_err(|_| DeviceRegistrationError::BrokenPciTopology)?;
989 let bridge_window = device
990 .configure_bridge_window(resources, &child_bar_ranges)
991 .map_err(DeviceRegistrationError::ConfigureWindowSize)?;
992 bar_ranges.extend(bridge_window);
993
994 let ranges = device
995 .allocate_io_bars(resources)
996 .map_err(DeviceRegistrationError::AllocateIoAddrs)?;
997 io_ranges.insert(dev_idx, ranges.clone());
998 bar_ranges.extend(ranges);
999
1000 let ranges = device
1001 .allocate_device_bars(resources)
1002 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1003 device_ranges.insert(dev_idx, ranges.clone());
1004 bar_ranges.extend(ranges);
1005
1006 device.set_subordinate_bus(child_sub_bus);
1007
1008 subordinate_bus = std::cmp::max(subordinate_bus, child_sub_bus);
1009 }
1010 }
1011 }
1012
1013 for (dev_idx, addr) in device_addrs.iter().enumerate() {
1014 if addr.bus == bus_num {
1015 let device = &mut devices[dev_idx].0;
1016 if device.get_new_pci_bus().is_none() {
1018 let ranges = device
1019 .allocate_io_bars(resources)
1020 .map_err(DeviceRegistrationError::AllocateIoAddrs)?;
1021 io_ranges.insert(dev_idx, ranges.clone());
1022 bar_ranges.extend(ranges);
1023
1024 let ranges = device
1025 .allocate_device_bars(resources)
1026 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1027 device_ranges.insert(dev_idx, ranges.clone());
1028 bar_ranges.extend(ranges);
1029 }
1030 }
1031 }
1032 Ok((bar_ranges, subordinate_bus))
1033}
1034
1035pub fn assign_pci_addresses(
1037 devices: &mut [(Box<dyn BusDeviceObj>, Option<Minijail>)],
1038 resources: &mut SystemAllocator,
1039) -> Result<(), DeviceRegistrationError> {
1040 for pci_device in devices
1042 .iter_mut()
1043 .filter_map(|(device, _jail)| device.as_pci_device_mut())
1044 .filter(|pci_device| pci_device.preferred_address().is_some())
1045 {
1046 let _ = pci_device
1047 .allocate_address(resources)
1048 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1049 }
1050
1051 for pci_device in devices
1053 .iter_mut()
1054 .filter_map(|(device, _jail)| device.as_pci_device_mut())
1055 .filter(|pci_device| pci_device.preferred_address().is_none())
1056 {
1057 let _ = pci_device
1058 .allocate_address(resources)
1059 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1060 }
1061
1062 Ok(())
1063}
1064
1065pub fn generate_pci_root(
1067 mut devices: Vec<(Box<dyn PciDevice>, Option<Minijail>)>,
1068 irq_chip: &dyn IrqChip,
1069 mmio_bus: Arc<Bus>,
1070 mmio_base: GuestAddress,
1071 mmio_register_bit_num: usize,
1072 io_bus: Arc<Bus>,
1073 resources: &mut SystemAllocator,
1074 mut vm: &dyn Vm,
1075 max_irqs: usize,
1076 vcfg_base: Option<u64>,
1077 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
1078) -> Result<
1079 (
1080 PciRoot,
1081 Vec<(PciAddress, u32, PciInterruptPin)>,
1082 BTreeMap<u32, String>,
1083 BTreeMap<PciAddress, Vec<u8>>,
1084 BTreeMap<PciAddress, Vec<u8>>,
1085 ),
1086 DeviceRegistrationError,
1087> {
1088 let mut device_addrs = Vec::new();
1089
1090 for (device, _jail) in devices.iter_mut() {
1091 let address = device
1092 .allocate_address(resources)
1093 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1094 device_addrs.push(address);
1095 }
1096
1097 let mut device_ranges = BTreeMap::new();
1098 let mut io_ranges = BTreeMap::new();
1099 let root_bus = Arc::new(Mutex::new(PciBus::new(0, 0, false)));
1100
1101 generate_pci_topology(
1102 root_bus.clone(),
1103 resources,
1104 &mut io_ranges,
1105 &mut device_ranges,
1106 &device_addrs,
1107 &mut devices,
1108 )?;
1109
1110 let mut root = PciRoot::new(
1111 vm,
1112 Arc::downgrade(&mmio_bus),
1113 mmio_base,
1114 mmio_register_bit_num,
1115 Arc::downgrade(&io_bus),
1116 root_bus,
1117 )
1118 .map_err(DeviceRegistrationError::CreateRoot)?;
1119 #[cfg_attr(windows, allow(unused_mut))]
1120 let mut pid_labels = BTreeMap::new();
1121
1122 let mut pci_irqs = Vec::new();
1124 let mut irqs: Vec<u32> = Vec::new();
1125
1126 let mut dev_pin_irq = BTreeMap::new();
1128
1129 for (dev_idx, (device, _jail)) in devices.iter_mut().enumerate() {
1130 let pci_address = device_addrs[dev_idx];
1131
1132 let irq = match device.preferred_irq() {
1133 PreferredIrq::Fixed { pin, gsi } => {
1134 resources.reserve_irq(gsi);
1136 Some((pin, gsi))
1137 }
1138 PreferredIrq::Any => {
1139 let pin = match pci_address.func % 4 {
1146 0 => PciInterruptPin::IntA,
1147 1 => PciInterruptPin::IntB,
1148 2 => PciInterruptPin::IntC,
1149 _ => PciInterruptPin::IntD,
1150 };
1151
1152 let pin_key = (pci_address.bus, pci_address.dev, pin);
1156 let irq_num = if let Some(irq_num) = dev_pin_irq.get(&pin_key) {
1157 *irq_num
1158 } else {
1159 let irq_num = if irqs.len() < max_irqs {
1162 let irq_num = resources
1163 .allocate_irq()
1164 .ok_or(DeviceRegistrationError::AllocateIrq)?;
1165 irqs.push(irq_num);
1166 irq_num
1167 } else {
1168 irqs[dev_idx % max_irqs]
1171 };
1172
1173 dev_pin_irq.insert(pin_key, irq_num);
1174 irq_num
1175 };
1176 Some((pin, irq_num))
1177 }
1178 PreferredIrq::None => {
1179 None
1181 }
1182 };
1183
1184 if let Some((pin, gsi)) = irq {
1185 let intx_event =
1186 devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
1187
1188 device.assign_irq(
1189 intx_event
1190 .try_clone()
1191 .map_err(DeviceRegistrationError::EventClone)?,
1192 pin,
1193 gsi,
1194 );
1195
1196 irq_chip
1197 .register_level_irq_event(gsi, &intx_event, IrqEventSource::from_device(device))
1198 .map_err(DeviceRegistrationError::RegisterIrqfd)?;
1199
1200 pci_irqs.push((pci_address, gsi, pin));
1201 }
1202 }
1203
1204 let devices = {
1209 let (sandboxed, non_sandboxed): (Vec<_>, Vec<_>) = devices
1210 .into_iter()
1211 .enumerate()
1212 .partition(|(_, (_, jail))| jail.is_some());
1213 sandboxed.into_iter().chain(non_sandboxed)
1214 };
1215
1216 let mut amls = BTreeMap::new();
1217 let mut gpe_scope_amls = BTreeMap::new();
1218 for (dev_idx, dev_value) in devices {
1219 #[cfg(any(target_os = "android", target_os = "linux"))]
1220 let (mut device, jail) = dev_value;
1221 #[cfg(windows)]
1222 let (mut device, _) = dev_value;
1223 let address = device_addrs[dev_idx];
1224
1225 let mut keep_rds = device.keep_rds();
1226 syslog::push_descriptors(&mut keep_rds);
1227 cros_tracing::push_descriptors!(&mut keep_rds);
1228 metrics::push_descriptors(&mut keep_rds);
1229 keep_rds.append(&mut vm.get_memory().as_raw_descriptors());
1230
1231 let ranges = io_ranges.remove(&dev_idx).unwrap_or_default();
1232 let device_ranges = device_ranges.remove(&dev_idx).unwrap_or_default();
1233 device
1234 .register_device_capabilities()
1235 .map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?;
1236
1237 if let Some(vcfg_base) = vcfg_base {
1238 let (methods, shm) = device.generate_acpi_methods();
1239 if !methods.is_empty() {
1240 amls.insert(address, methods);
1241 }
1242 if let Some((offset, mmap)) = shm {
1243 let _ = vm.add_memory_region(
1244 GuestAddress(vcfg_base + offset as u64),
1245 Box::new(mmap),
1246 false,
1247 false,
1248 MemCacheType::CacheCoherent,
1249 );
1250 }
1251 }
1252 let gpe_nr = device.set_gpe(resources);
1253
1254 #[cfg(any(target_os = "android", target_os = "linux"))]
1255 let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
1256 let proxy = ProxyDevice::new(
1257 device,
1258 jail,
1259 keep_rds,
1260 #[cfg(feature = "swap")]
1261 swap_controller,
1262 )
1263 .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
1264 pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
1265 Arc::new(Mutex::new(proxy))
1266 } else {
1267 device.on_sandboxed();
1268 Arc::new(Mutex::new(device))
1269 };
1270 #[cfg(windows)]
1271 let arced_dev = {
1272 device.on_sandboxed();
1273 Arc::new(Mutex::new(device))
1274 };
1275 root.add_device(address, arced_dev.clone(), &mut vm)
1276 .map_err(DeviceRegistrationError::PciRootAddDevice)?;
1277 for range in &ranges {
1278 mmio_bus
1279 .insert(arced_dev.clone(), range.addr, range.size)
1280 .map_err(DeviceRegistrationError::MmioInsert)?;
1281 }
1282
1283 for range in &device_ranges {
1284 mmio_bus
1285 .insert(arced_dev.clone(), range.addr, range.size)
1286 .map_err(DeviceRegistrationError::MmioInsert)?;
1287 }
1288
1289 if let Some(gpe_nr) = gpe_nr {
1290 if let Some(acpi_path) = root.acpi_path(&address) {
1291 let mut gpe_aml = Vec::new();
1292
1293 GpeScope {}.cast_to_aml_bytes(
1294 &mut gpe_aml,
1295 gpe_nr,
1296 format!("\\{acpi_path}").as_str(),
1297 );
1298 if !gpe_aml.is_empty() {
1299 gpe_scope_amls.insert(address, gpe_aml);
1300 }
1301 }
1302 }
1303 }
1304
1305 Ok((root, pci_irqs, pid_labels, amls, gpe_scope_amls))
1306}
1307
1308#[sorted]
1310#[derive(Error, Debug)]
1311pub enum LoadImageError {
1312 #[error("Alignment not a power of two: {0}")]
1313 BadAlignment(u64),
1314 #[error("Getting image size failed: {0}")]
1315 GetLen(io::Error),
1316 #[error("GuestMemory get slice failed: {0}")]
1317 GuestMemorySlice(GuestMemoryError),
1318 #[error("Image size too large: {0}")]
1319 ImageSizeTooLarge(u64),
1320 #[error("No suitable memory region found")]
1321 NoSuitableMemoryRegion,
1322 #[error("Reading image into memory failed: {0}")]
1323 ReadToMemory(io::Error),
1324 #[error("Cannot load zero-sized image")]
1325 ZeroSizedImage,
1326}
1327
1328pub fn load_image<F>(
1339 guest_mem: &GuestMemory,
1340 image: &mut F,
1341 guest_addr: GuestAddress,
1342 max_size: u64,
1343) -> Result<u32, LoadImageError>
1344where
1345 F: FileReadWriteAtVolatile + FileGetLen,
1346{
1347 let size = image.get_len().map_err(LoadImageError::GetLen)?;
1348
1349 if size > u32::MAX as u64 || size > max_size {
1350 return Err(LoadImageError::ImageSizeTooLarge(size));
1351 }
1352
1353 let size = size as u32;
1355
1356 let guest_slice = guest_mem
1357 .get_slice_at_addr(guest_addr, size as usize)
1358 .map_err(LoadImageError::GuestMemorySlice)?;
1359 image
1360 .read_exact_at_volatile(guest_slice, 0)
1361 .map_err(LoadImageError::ReadToMemory)?;
1362
1363 Ok(size)
1364}
1365
1366pub fn load_image_high<F>(
1381 guest_mem: &GuestMemory,
1382 image: &mut F,
1383 min_guest_addr: GuestAddress,
1384 max_guest_addr: GuestAddress,
1385 region_filter: Option<fn(&MemoryRegionInformation) -> bool>,
1386 align: u64,
1387) -> Result<(GuestAddress, u32), LoadImageError>
1388where
1389 F: FileReadWriteAtVolatile + FileGetLen,
1390{
1391 if !align.is_power_of_two() {
1392 return Err(LoadImageError::BadAlignment(align));
1393 }
1394
1395 let max_size = max_guest_addr.offset_from(min_guest_addr) & !(align - 1);
1396 let size = image.get_len().map_err(LoadImageError::GetLen)?;
1397
1398 if size == 0 {
1399 return Err(LoadImageError::ZeroSizedImage);
1400 }
1401
1402 if size > u32::MAX as u64 || size > max_size {
1403 return Err(LoadImageError::ImageSizeTooLarge(size));
1404 }
1405
1406 let mut regions: Vec<_> = guest_mem
1409 .regions()
1410 .filter(region_filter.unwrap_or(|_| true))
1411 .collect();
1412 regions.sort_unstable_by(|a, b| a.guest_addr.cmp(&b.guest_addr));
1413
1414 let guest_addr = regions
1417 .into_iter()
1418 .rev()
1419 .filter_map(|r| {
1420 let rgn_max_addr = r
1422 .guest_addr
1423 .checked_add((r.size as u64).checked_sub(1)?)?
1424 .min(max_guest_addr);
1425 let rgn_start_aligned = r.guest_addr.align(align)?;
1427 let image_addr = rgn_max_addr.checked_sub(size - 1)? & !(align - 1);
1429
1430 if image_addr >= rgn_start_aligned {
1432 Some(image_addr)
1433 } else {
1434 None
1435 }
1436 })
1437 .find(|&addr| addr >= min_guest_addr)
1438 .ok_or(LoadImageError::NoSuitableMemoryRegion)?;
1439
1440 let size = size as u32;
1442
1443 let guest_slice = guest_mem
1444 .get_slice_at_addr(guest_addr, size as usize)
1445 .map_err(LoadImageError::GuestMemorySlice)?;
1446 image
1447 .read_exact_at_volatile(guest_slice, 0)
1448 .map_err(LoadImageError::ReadToMemory)?;
1449
1450 Ok((guest_addr, size))
1451}
1452
1453#[derive(Clone, Debug, Default, Serialize, Deserialize, FromKeyValues, PartialEq, Eq)]
1455#[serde(deny_unknown_fields, rename_all = "kebab-case")]
1456pub struct SmbiosOptions {
1457 pub bios_vendor: Option<String>,
1459
1460 pub bios_version: Option<String>,
1462
1463 pub manufacturer: Option<String>,
1465
1466 pub product_name: Option<String>,
1468
1469 pub serial_number: Option<String>,
1471
1472 pub uuid: Option<Uuid>,
1474
1475 #[serde(default)]
1477 pub oem_strings: Vec<String>,
1478}
1479
1480#[cfg(test)]
1481mod tests {
1482 use serde_keyvalue::from_key_values;
1483 use tempfile::tempfile;
1484
1485 use super::*;
1486
1487 #[test]
1488 fn parse_pstore() {
1489 let res: Pstore = from_key_values("path=/some/path,size=16384").unwrap();
1490 assert_eq!(
1491 res,
1492 Pstore {
1493 path: "/some/path".into(),
1494 size: 16384,
1495 }
1496 );
1497
1498 let res = from_key_values::<Pstore>("path=/some/path");
1499 assert!(res.is_err());
1500
1501 let res = from_key_values::<Pstore>("size=16384");
1502 assert!(res.is_err());
1503
1504 let res = from_key_values::<Pstore>("");
1505 assert!(res.is_err());
1506 }
1507
1508 #[test]
1509 fn deserialize_cpuset_serde_kv() {
1510 let res: CpuSet = from_key_values("[0,4,7]").unwrap();
1511 assert_eq!(res, CpuSet::new(vec![0, 4, 7]));
1512
1513 let res: CpuSet = from_key_values("[9-12]").unwrap();
1514 assert_eq!(res, CpuSet::new(vec![9, 10, 11, 12]));
1515
1516 let res: CpuSet = from_key_values("[0,4,7,9-12,15]").unwrap();
1517 assert_eq!(res, CpuSet::new(vec![0, 4, 7, 9, 10, 11, 12, 15]));
1518 }
1519
1520 #[test]
1521 fn deserialize_serialize_cpuset_json() {
1522 let json_str = "[0,4,7]";
1523 let cpuset = CpuSet::new(vec![0, 4, 7]);
1524 let res: CpuSet = serde_json::from_str(json_str).unwrap();
1525 assert_eq!(res, cpuset);
1526 assert_eq!(serde_json::to_string(&cpuset).unwrap(), json_str);
1527
1528 let json_str = r#"["9-12"]"#;
1529 let cpuset = CpuSet::new(vec![9, 10, 11, 12]);
1530 let res: CpuSet = serde_json::from_str(json_str).unwrap();
1531 assert_eq!(res, cpuset);
1532 assert_eq!(serde_json::to_string(&cpuset).unwrap(), json_str);
1533
1534 let json_str = r#"[0,4,7,"9-12",15]"#;
1535 let cpuset = CpuSet::new(vec![0, 4, 7, 9, 10, 11, 12, 15]);
1536 let res: CpuSet = serde_json::from_str(json_str).unwrap();
1537 assert_eq!(res, cpuset);
1538 assert_eq!(serde_json::to_string(&cpuset).unwrap(), json_str);
1539 }
1540
1541 #[test]
1542 fn load_image_high_max_4g() {
1543 let mem = GuestMemory::new(&[
1544 (GuestAddress(0x0000_0000), 0x4000_0000), (GuestAddress(0x8000_0000), 0x4000_0000), ])
1547 .unwrap();
1548
1549 const TEST_IMAGE_SIZE: u64 = 1234;
1550 let mut test_image = tempfile().unwrap();
1551 test_image.set_len(TEST_IMAGE_SIZE).unwrap();
1552
1553 const TEST_ALIGN: u64 = 0x8000;
1554 let (addr, size) = load_image_high(
1555 &mem,
1556 &mut test_image,
1557 GuestAddress(0x8000),
1558 GuestAddress(0xFFFF_FFFF), None,
1560 TEST_ALIGN,
1561 )
1562 .unwrap();
1563
1564 assert_eq!(addr, GuestAddress(0xBFFF_8000));
1565 assert_eq!(addr.offset() % TEST_ALIGN, 0);
1566 assert_eq!(size, TEST_IMAGE_SIZE as u32);
1567 }
1568}