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 mte_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 MteConfig {
169 #[serde(default = "mte_auto_default")]
171 pub auto: bool,
172}
173
174#[cfg(target_arch = "aarch64")]
175impl Default for MteConfig {
176 fn default() -> Self {
177 MteConfig {
178 auto: mte_auto_default(),
179 }
180 }
181}
182
183#[cfg(target_arch = "aarch64")]
184fn sve_auto_default() -> bool {
185 true
186}
187
188#[cfg(target_arch = "aarch64")]
190#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
191#[serde(deny_unknown_fields, rename_all = "kebab-case")]
192pub struct SveConfig {
193 #[serde(default = "sve_auto_default")]
195 pub auto: bool,
196}
197
198#[cfg(target_arch = "aarch64")]
199impl Default for SveConfig {
200 fn default() -> Self {
201 SveConfig {
202 auto: sve_auto_default(),
203 }
204 }
205}
206
207#[cfg(all(target_os = "android", target_arch = "aarch64"))]
211#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize, FromKeyValues)]
212#[serde(deny_unknown_fields, rename_all = "kebab-case")]
213pub struct FfaConfig {
214 #[serde(default)]
216 pub auto: bool,
217}
218
219fn parse_cpu_range(s: &str, cpuset: &mut Vec<usize>) -> Result<(), String> {
220 fn parse_cpu(s: &str) -> Result<usize, String> {
221 s.parse()
222 .map_err(|_| format!("invalid CPU index {s} - index must be a non-negative integer"))
223 }
224
225 let (first_cpu, last_cpu) = match s.split_once('-') {
226 Some((first_cpu, last_cpu)) => {
227 let first_cpu = parse_cpu(first_cpu)?;
228 let last_cpu = parse_cpu(last_cpu)?;
229
230 if last_cpu < first_cpu {
231 return Err(format!(
232 "invalid CPU range {s} - ranges must be from low to high"
233 ));
234 }
235 (first_cpu, last_cpu)
236 }
237 None => {
238 let cpu = parse_cpu(s)?;
239 (cpu, cpu)
240 }
241 };
242
243 cpuset.extend(first_cpu..=last_cpu);
244
245 Ok(())
246}
247
248impl FromStr for CpuSet {
249 type Err = String;
250
251 fn from_str(s: &str) -> Result<Self, Self::Err> {
252 let mut cpuset = Vec::new();
253 for part in s.split(',') {
254 parse_cpu_range(part, &mut cpuset)?;
255 }
256 Ok(CpuSet::new(cpuset))
257 }
258}
259
260impl Deref for CpuSet {
261 type Target = Vec<usize>;
262
263 fn deref(&self) -> &Self::Target {
264 &self.0
265 }
266}
267
268impl IntoIterator for CpuSet {
269 type Item = usize;
270 type IntoIter = std::vec::IntoIter<Self::Item>;
271
272 fn into_iter(self) -> Self::IntoIter {
273 self.0.into_iter()
274 }
275}
276
277#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
279pub enum DevicePowerManagerConfig {
280 PkvmHvc,
283}
284
285impl FromStr for DevicePowerManagerConfig {
286 type Err = String;
287
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 match s {
290 "pkvm-hvc" => Ok(Self::PkvmHvc),
291 _ => Err(format!("DevicePowerManagerConfig '{s}' not supported")),
292 }
293 }
294}
295
296impl<'de> Deserialize<'de> for CpuSet {
299 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
300 where
301 D: serde::Deserializer<'de>,
302 {
303 struct CpuSetVisitor;
304 impl<'de> Visitor<'de> for CpuSetVisitor {
305 type Value = CpuSet;
306
307 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
308 formatter.write_str("CpuSet")
309 }
310
311 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
312 where
313 A: serde::de::SeqAccess<'de>,
314 {
315 #[derive(Deserialize)]
316 #[serde(untagged)]
317 enum CpuSetValue<'a> {
318 Single(usize),
319 Range(&'a str),
320 }
321
322 let mut cpus = Vec::new();
323 while let Some(cpuset) = seq.next_element::<CpuSetValue>()? {
324 match cpuset {
325 CpuSetValue::Single(cpu) => cpus.push(cpu),
326 CpuSetValue::Range(range) => {
327 parse_cpu_range(range, &mut cpus).map_err(serde::de::Error::custom)?;
328 }
329 }
330 }
331
332 Ok(CpuSet::new(cpus))
333 }
334 }
335
336 deserializer.deserialize_seq(CpuSetVisitor)
337 }
338}
339
340impl Serialize for CpuSet {
342 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
343 where
344 S: serde::Serializer,
345 {
346 use serde::ser::SerializeSeq;
347
348 let mut seq = serializer.serialize_seq(None)?;
349
350 let mut serialize_range = |start: usize, end: usize| -> Result<(), S::Error> {
352 if start == end {
353 seq.serialize_element(&start)?;
354 } else {
355 seq.serialize_element(&format!("{start}-{end}"))?;
356 }
357
358 Ok(())
359 };
360
361 let mut range = None;
363 for core in &self.0 {
364 range = match range {
365 None => Some((core, core)),
366 Some((start, end)) if *end == *core - 1 => Some((start, core)),
367 Some((start, end)) => {
368 serialize_range(*start, *end)?;
369 Some((core, core))
370 }
371 };
372 }
373
374 if let Some((start, end)) = range {
375 serialize_range(*start, *end)?;
376 }
377
378 seq.end()
379 }
380}
381
382#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
384pub enum VcpuAffinity {
385 Global(CpuSet),
387 PerVcpu(BTreeMap<usize, CpuSet>),
392}
393
394#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
396pub struct MemoryRegionConfig {
397 pub start: u64,
398 pub size: Option<u64>,
399}
400
401#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
403pub struct PciConfig {
404 #[cfg(target_arch = "aarch64")]
406 pub cam: Option<MemoryRegionConfig>,
407 #[cfg(target_arch = "x86_64")]
409 pub ecam: Option<MemoryRegionConfig>,
410 pub mem: Option<MemoryRegionConfig>,
412}
413
414pub const DEFAULT_CPU_CAPACITY: u32 = 1024;
415
416#[sorted]
417#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
418pub struct VcpuProperties {
419 pub capacity: Option<u32>,
420 pub dynamic_power_coefficient: Option<u32>,
421 pub frequencies: Vec<u32>,
422 #[cfg(all(
423 target_arch = "aarch64",
424 any(target_os = "android", target_os = "linux")
425 ))]
426 pub normalized_cpu_ipc_ratio: Option<u32>,
427 #[cfg(all(
428 target_arch = "aarch64",
429 any(target_os = "android", target_os = "linux")
430 ))]
431 pub vcpu_domain: Option<u32>,
432 #[cfg(all(
433 target_arch = "aarch64",
434 any(target_os = "android", target_os = "linux")
435 ))]
436 pub vcpu_domain_path: Option<PathBuf>,
437}
438
439pub fn derive_vcpu_properties(
441 vcpu_count: usize,
442 vcpu_capacity: &std::collections::BTreeMap<usize, u32>,
443 dynamic_power_coefficient: &std::collections::BTreeMap<usize, u32>,
444 vcpu_frequencies: &std::collections::BTreeMap<usize, Vec<u32>>,
445 #[cfg(all(
446 target_arch = "aarch64",
447 any(target_os = "android", target_os = "linux")
448 ))]
449 normalized_cpu_ipc_ratio: &std::collections::BTreeMap<usize, u32>,
450 #[cfg(all(
451 target_arch = "aarch64",
452 any(target_os = "android", target_os = "linux")
453 ))]
454 vcpu_domain: &std::collections::BTreeMap<usize, u32>,
455 #[cfg(all(
456 target_arch = "aarch64",
457 any(target_os = "android", target_os = "linux")
458 ))]
459 vcpu_domain_path: &std::collections::BTreeMap<usize, std::path::PathBuf>,
460) -> std::collections::BTreeMap<usize, VcpuProperties> {
461 let mut vcpu_properties = std::collections::BTreeMap::new();
462 for vcpu_id in 0..vcpu_count {
463 let vcpu_prop_capacity = vcpu_capacity.get(&vcpu_id).copied();
464
465 vcpu_properties.insert(
466 vcpu_id,
467 VcpuProperties {
468 capacity: vcpu_prop_capacity,
469 frequencies: vcpu_frequencies.get(&vcpu_id).cloned().unwrap_or_default(),
470 dynamic_power_coefficient: dynamic_power_coefficient.get(&vcpu_id).copied(),
471 #[cfg(all(
472 target_arch = "aarch64",
473 any(target_os = "android", target_os = "linux")
474 ))]
475 normalized_cpu_ipc_ratio: normalized_cpu_ipc_ratio.get(&vcpu_id).copied(),
476 #[cfg(all(
477 target_arch = "aarch64",
478 any(target_os = "android", target_os = "linux")
479 ))]
480 vcpu_domain: vcpu_domain.get(&vcpu_id).copied(),
481 #[cfg(all(
482 target_arch = "aarch64",
483 any(target_os = "android", target_os = "linux")
484 ))]
485 vcpu_domain_path: vcpu_domain_path.get(&vcpu_id).cloned(),
486 },
487 );
488 }
489 vcpu_properties
490}
491
492#[sorted]
495pub struct VmComponents {
496 pub acpi_sdts: Vec<SDT>,
497 pub android_fstab: Option<File>,
498 pub boot_cpu: usize,
499 pub bootorder_fw_cfg_blob: Vec<u8>,
500 #[cfg(target_arch = "x86_64")]
501 pub break_linux_pci_config_io: bool,
502
503 pub delay_rt: bool,
504 pub dev_pm: Option<DevicePowerManagerConfig>,
505 pub extra_kernel_params: Vec<String>,
506 #[cfg(target_arch = "x86_64")]
507 pub force_s2idle: bool,
508 pub fw_cfg_enable: bool,
509 pub fw_cfg_parameters: Vec<FwCfgParameters>,
510 pub host_cpu_topology: bool,
511 pub hugepages: bool,
512 pub hv_cfg: hypervisor::Config,
513 pub initrd_image: Option<File>,
514 pub itmt: bool,
515 pub memory_size: u64,
516 #[cfg(target_arch = "aarch64")]
517 pub nested: hypervisor::NestedMode,
518 pub no_i8042: bool,
519 pub no_rtc: bool,
520 pub no_smt: bool,
521
522 pub pci_config: PciConfig,
523 pub pflash_block_size: u32,
524 pub pflash_image: Option<File>,
525 pub pstore: Option<Pstore>,
526 pub pvm_fw: Option<File>,
529 pub rt_cpus: CpuSet,
530 #[cfg(target_arch = "x86_64")]
531 pub smbios: SmbiosOptions,
532 pub smccc_trng: bool,
533 #[cfg(target_arch = "aarch64")]
534 pub sve_config: SveConfig,
535 pub swiotlb: Option<u64>,
536 pub vcpu_affinity: Option<VcpuAffinity>,
537 pub vcpu_clusters: Vec<CpuSet>,
539 pub vcpu_properties: BTreeMap<usize, VcpuProperties>,
540 #[cfg(any(target_os = "android", target_os = "linux"))]
541 pub vfio_platform_pm: bool,
542 #[cfg(all(
543 target_arch = "aarch64",
544 any(target_os = "android", target_os = "linux")
545 ))]
546 pub virt_cpufreq_v2: bool,
547 pub vm_image: VmImage,
548}
549
550#[sorted]
552pub struct RunnableLinuxVm {
553 pub bat_control: Option<BatControl>,
554 pub delay_rt: bool,
555 pub devices_thread: Option<std::thread::JoinHandle<()>>,
556 pub hotplug_bus: BTreeMap<u8, Arc<Mutex<dyn HotPlugBus>>>,
557 pub hypercall_bus: Arc<Bus>,
558 pub io_bus: Arc<Bus>,
559 pub irq_chip: Arc<dyn IrqChipArch>,
560 pub mmio_bus: Arc<Bus>,
561 pub no_smt: bool,
562 pub pid_debug_label_map: BTreeMap<u32, String>,
563 #[cfg(any(target_os = "android", target_os = "linux"))]
564 pub platform_devices: Vec<Arc<Mutex<dyn BusDevice>>>,
565 pub pm: Option<Arc<Mutex<dyn PmResource + Send>>>,
566 pub resume_notify_devices: Vec<Arc<Mutex<dyn BusResumeDevice>>>,
568 pub root_config: Arc<Mutex<PciRoot>>,
569 pub rt_cpus: CpuSet,
570 pub suspend_tube: (Arc<Mutex<SendTube>>, RecvTube),
571 pub vcpu_affinity: Option<VcpuAffinity>,
572 pub vcpu_count: usize,
573 pub vcpu_init: Vec<VcpuInitArch>,
574 pub vcpus: Option<Vec<Arc<dyn VcpuArch>>>,
577 pub vm: Arc<dyn VmArch>,
578 pub vm_request_tubes: Vec<Tube>,
579}
580
581pub struct VirtioDeviceStub {
583 pub dev: Box<dyn VirtioDevice>,
584 pub jail: Option<Minijail>,
585}
586
587pub trait LinuxArch {
590 type Error: StdError;
591 type ArchMemoryLayout;
592
593 fn arch_memory_layout(
596 components: &VmComponents,
597 ) -> std::result::Result<Self::ArchMemoryLayout, Self::Error>;
598
599 fn guest_memory_layout(
606 components: &VmComponents,
607 arch_memory_layout: &Self::ArchMemoryLayout,
608 hypervisor: &impl hypervisor::Hypervisor,
609 ) -> std::result::Result<Vec<(GuestAddress, u64, MemoryRegionOptions)>, Self::Error>;
610
611 fn get_system_allocator_config(
621 vm: &dyn Vm,
622 arch_memory_layout: &Self::ArchMemoryLayout,
623 ) -> SystemAllocatorConfig;
624
625 fn build_vm(
646 components: VmComponents,
647 arch_memory_layout: &Self::ArchMemoryLayout,
648 vm_evt_wrtube: &SendTube,
649 system_allocator: &mut SystemAllocator,
650 serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
651 serial_jail: Option<Minijail>,
652 battery: (Option<BatteryType>, Option<Minijail>),
653 vm: Arc<dyn VmArch>,
654 ramoops_region: Option<pstore::RamoopsRegion>,
655 devices: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
656 irq_chip: Arc<dyn IrqChipArch>,
657 vcpu_ids: &mut Vec<usize>,
658 dump_device_tree_blob: Option<PathBuf>,
659 debugcon_jail: Option<Minijail>,
660 #[cfg(target_arch = "x86_64")] pflash_jail: Option<Minijail>,
661 #[cfg(target_arch = "x86_64")] fw_cfg_jail: Option<Minijail>,
662 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
663 guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
664 device_tree_overlays: Vec<DtbOverlay>,
665 fdt_position: Option<FdtPosition>,
666 no_pmu: bool,
667 ) -> std::result::Result<RunnableLinuxVm, Self::Error>;
668
669 fn configure_vcpu(
682 vm: &dyn Vm,
683 hypervisor: &dyn HypervisorArch,
684 irq_chip: &dyn IrqChipArch,
685 vcpu: &dyn VcpuArch,
686 vcpu_init: VcpuInitArch,
687 vcpu_id: usize,
688 num_vcpus: usize,
689 cpu_config: Option<CpuConfigArch>,
690 ) -> Result<(), Self::Error>;
691
692 fn register_pci_device(
694 linux: &mut RunnableLinuxVm,
695 device: Box<dyn PciDevice>,
696 #[cfg(any(target_os = "android", target_os = "linux"))] minijail: Option<Minijail>,
697 resources: &mut SystemAllocator,
698 hp_control_tube: &mpsc::Sender<PciRootCommand>,
699 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
700 ) -> Result<PciAddress, Self::Error>;
701
702 fn get_host_cpu_frequencies_khz() -> Result<BTreeMap<usize, Vec<u32>>, Self::Error>;
704
705 fn get_host_cpu_max_freq_khz() -> Result<BTreeMap<usize, u32>, Self::Error>;
707
708 fn get_host_cpu_capacity(per_mhz: bool) -> Result<BTreeMap<usize, u32>, Self::Error>;
714
715 fn get_host_cpu_clusters() -> Result<Vec<CpuSet>, Self::Error>;
717}
718
719#[cfg(feature = "gdb")]
720pub trait GdbOps {
721 type Error: StdError;
722
723 fn read_registers(vcpu: &dyn VcpuArch) -> Result<<GdbArch as Arch>::Registers, Self::Error>;
725
726 fn write_registers(
728 vcpu: &dyn VcpuArch,
729 regs: &<GdbArch as Arch>::Registers,
730 ) -> Result<(), Self::Error>;
731
732 fn read_memory(
734 vcpu: &dyn VcpuArch,
735 guest_mem: &GuestMemory,
736 vaddr: GuestAddress,
737 len: usize,
738 ) -> Result<Vec<u8>, Self::Error>;
739
740 fn write_memory(
742 vcpu: &dyn VcpuArch,
743 guest_mem: &GuestMemory,
744 vaddr: GuestAddress,
745 buf: &[u8],
746 ) -> Result<(), Self::Error>;
747
748 fn read_register(
752 vcpu: &dyn VcpuArch,
753 reg_id: <GdbArch as Arch>::RegId,
754 ) -> Result<Vec<u8>, Self::Error>;
755
756 fn write_register(
758 vcpu: &dyn VcpuArch,
759 reg_id: <GdbArch as Arch>::RegId,
760 data: &[u8],
761 ) -> Result<(), Self::Error>;
762
763 fn enable_singlestep(vcpu: &dyn VcpuArch) -> Result<(), Self::Error>;
765
766 fn get_max_hw_breakpoints(vcpu: &dyn VcpuArch) -> Result<usize, Self::Error>;
768
769 fn set_hw_breakpoints(
771 vcpu: &dyn VcpuArch,
772 breakpoints: &[GuestAddress],
773 ) -> Result<(), Self::Error>;
774}
775
776#[sorted]
778#[derive(Error, Debug)]
779pub enum DeviceRegistrationError {
780 #[error("no more addresses are available")]
782 AddrsExhausted,
783 #[error("Allocating device addresses: {0}")]
785 AllocateDeviceAddrs(PciDeviceError),
786 #[error("Allocating IO addresses: {0}")]
788 AllocateIoAddrs(PciDeviceError),
789 #[error("Allocating IO resource: {0}")]
791 AllocateIoResource(resources::Error),
792 #[error("Allocating IRQ number")]
794 AllocateIrq,
795 #[cfg(any(target_os = "android", target_os = "linux"))]
797 #[error("Allocating IRQ resource: {0}")]
798 AllocateIrqResource(devices::vfio::VfioError),
799 #[error("failed to attach the device to its power domain: {0}")]
800 AttachDevicePowerDomain(anyhow::Error),
801 #[error("pci topology is broken")]
803 BrokenPciTopology,
804 #[cfg(any(target_os = "android", target_os = "linux"))]
806 #[error("failed to clone jail: {0}")]
807 CloneJail(minijail::Error),
808 #[error("unable to add device to kernel command line: {0}")]
810 Cmdline(kernel_cmdline::Error),
811 #[error("failed to configure window size: {0}")]
813 ConfigureWindowSize(PciDeviceError),
814 #[error("failed to create pipe: {0}")]
816 CreatePipe(base::Error),
817 #[error("failed to create pci root: {0}")]
819 CreateRoot(anyhow::Error),
820 #[error("failed to create serial device: {0}")]
822 CreateSerialDevice(devices::SerialError),
823 #[error("failed to create tube: {0}")]
825 CreateTube(base::TubeError),
826 #[error("failed to clone event: {0}")]
828 EventClone(base::Error),
829 #[error("failed to create event: {0}")]
831 EventCreate(base::Error),
832 #[error("failed to generate ACPI content")]
834 GenerateAcpi,
835 #[error("no more IRQs are available")]
837 IrqsExhausted,
838 #[error("cannot match VFIO device to DT node due to a missing symbol")]
840 MissingDeviceTreeSymbol,
841 #[error("missing required serial device {0}")]
843 MissingRequiredSerialDevice(u8),
844 #[error("failed to add to mmio bus: {0}")]
846 MmioInsert(BusError),
847 #[error("failed to insert device into PCI root: {0}")]
849 PciRootAddDevice(PciDeviceError),
850 #[cfg(any(target_os = "android", target_os = "linux"))]
851 #[error("failed to create proxy device: {0}")]
853 ProxyDeviceCreation(devices::ProxyError),
854 #[cfg(any(target_os = "android", target_os = "linux"))]
855 #[error("failed to register battery device to VM: {0}")]
857 RegisterBattery(devices::BatteryError),
858 #[error("failed to register PCI device to pci root bus")]
860 RegisterDevice(SendError<PciRootCommand>),
861 #[error("could not register PCI device capabilities: {0}")]
863 RegisterDeviceCapabilities(PciDeviceError),
864 #[error("failed to register ioevent to VM: {0}")]
866 RegisterIoevent(base::Error),
867 #[error("failed to register irq event to VM: {0}")]
869 RegisterIrqfd(base::Error),
870 #[error("Setting up VFIO platform IRQ: {0}")]
872 SetupVfioPlatformIrq(anyhow::Error),
873}
874
875pub fn configure_pci_device(
877 linux: &mut RunnableLinuxVm,
878 mut device: Box<dyn PciDevice>,
879 #[cfg(any(target_os = "android", target_os = "linux"))] jail: Option<Minijail>,
880 resources: &mut SystemAllocator,
881 hp_control_tube: &mpsc::Sender<PciRootCommand>,
882 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
883) -> Result<PciAddress, DeviceRegistrationError> {
884 let pci_address = device
886 .allocate_address(resources)
887 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
888
889 let mmio_ranges = device
891 .allocate_io_bars(resources)
892 .map_err(DeviceRegistrationError::AllocateIoAddrs)?;
893
894 let device_ranges = device
896 .allocate_device_bars(resources)
897 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
898
899 if let Some(pci_bus) = device.get_new_pci_bus() {
901 hp_control_tube
902 .send(PciRootCommand::AddBridge(pci_bus))
903 .map_err(DeviceRegistrationError::RegisterDevice)?;
904 let bar_ranges = Vec::new();
905 device
906 .configure_bridge_window(resources, &bar_ranges)
907 .map_err(DeviceRegistrationError::ConfigureWindowSize)?;
908 }
909
910 let intx_event = devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
912
913 if let PreferredIrq::Fixed { pin, gsi } = device.preferred_irq() {
914 resources.reserve_irq(gsi);
915
916 device.assign_irq(
917 intx_event
918 .try_clone()
919 .map_err(DeviceRegistrationError::EventClone)?,
920 pin,
921 gsi,
922 );
923
924 linux
925 .irq_chip
926 .register_level_irq_event(gsi, &intx_event, IrqEventSource::from_device(&device))
927 .map_err(DeviceRegistrationError::RegisterIrqfd)?;
928 }
929
930 let mut keep_rds = device.keep_rds();
931 syslog::push_descriptors(&mut keep_rds);
932 cros_tracing::push_descriptors!(&mut keep_rds);
933 metrics::push_descriptors(&mut keep_rds);
934
935 device
936 .register_device_capabilities()
937 .map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?;
938
939 #[cfg(any(target_os = "android", target_os = "linux"))]
940 let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
941 let proxy = ProxyDevice::new(
942 device,
943 jail,
944 keep_rds,
945 #[cfg(feature = "swap")]
946 swap_controller,
947 )
948 .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
949 linux
950 .pid_debug_label_map
951 .insert(proxy.pid() as u32, proxy.debug_label());
952 Arc::new(Mutex::new(proxy))
953 } else {
954 device.on_sandboxed();
955 Arc::new(Mutex::new(device))
956 };
957
958 #[cfg(windows)]
959 let arced_dev = {
960 device.on_sandboxed();
961 Arc::new(Mutex::new(device))
962 };
963
964 #[cfg(any(target_os = "android", target_os = "linux"))]
965 hp_control_tube
966 .send(PciRootCommand::Add(pci_address, arced_dev.clone()))
967 .map_err(DeviceRegistrationError::RegisterDevice)?;
968
969 for range in &mmio_ranges {
970 linux
971 .mmio_bus
972 .insert(arced_dev.clone(), range.addr, range.size)
973 .map_err(DeviceRegistrationError::MmioInsert)?;
974 }
975
976 for range in &device_ranges {
977 linux
978 .mmio_bus
979 .insert(arced_dev.clone(), range.addr, range.size)
980 .map_err(DeviceRegistrationError::MmioInsert)?;
981 }
982
983 Ok(pci_address)
984}
985
986fn generate_pci_topology(
988 parent_bus: Arc<Mutex<PciBus>>,
989 resources: &mut SystemAllocator,
990 io_ranges: &mut BTreeMap<usize, Vec<BarRange>>,
991 device_ranges: &mut BTreeMap<usize, Vec<BarRange>>,
992 device_addrs: &[PciAddress],
993 devices: &mut Vec<(Box<dyn PciDevice>, Option<Minijail>)>,
994) -> Result<(Vec<BarRange>, u8), DeviceRegistrationError> {
995 let mut bar_ranges = Vec::new();
996 let bus_num = parent_bus.lock().get_bus_num();
997 let mut subordinate_bus = bus_num;
998 for (dev_idx, addr) in device_addrs.iter().enumerate() {
999 if addr.bus == bus_num {
1001 if let Some(child_bus) = devices[dev_idx].0.get_new_pci_bus() {
1004 let (child_bar_ranges, child_sub_bus) = generate_pci_topology(
1005 child_bus.clone(),
1006 resources,
1007 io_ranges,
1008 device_ranges,
1009 device_addrs,
1010 devices,
1011 )?;
1012 let device = &mut devices[dev_idx].0;
1013 parent_bus
1014 .lock()
1015 .add_child_bus(child_bus.clone())
1016 .map_err(|_| DeviceRegistrationError::BrokenPciTopology)?;
1017 let bridge_window = device
1018 .configure_bridge_window(resources, &child_bar_ranges)
1019 .map_err(DeviceRegistrationError::ConfigureWindowSize)?;
1020 bar_ranges.extend(bridge_window);
1021
1022 let ranges = device
1023 .allocate_io_bars(resources)
1024 .map_err(DeviceRegistrationError::AllocateIoAddrs)?;
1025 io_ranges.insert(dev_idx, ranges.clone());
1026 bar_ranges.extend(ranges);
1027
1028 let ranges = device
1029 .allocate_device_bars(resources)
1030 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1031 device_ranges.insert(dev_idx, ranges.clone());
1032 bar_ranges.extend(ranges);
1033
1034 device.set_subordinate_bus(child_sub_bus);
1035
1036 subordinate_bus = std::cmp::max(subordinate_bus, child_sub_bus);
1037 }
1038 }
1039 }
1040
1041 for (dev_idx, addr) in device_addrs.iter().enumerate() {
1042 if addr.bus == bus_num {
1043 let device = &mut devices[dev_idx].0;
1044 if device.get_new_pci_bus().is_none() {
1046 let ranges = device
1047 .allocate_io_bars(resources)
1048 .map_err(DeviceRegistrationError::AllocateIoAddrs)?;
1049 io_ranges.insert(dev_idx, ranges.clone());
1050 bar_ranges.extend(ranges);
1051
1052 let ranges = device
1053 .allocate_device_bars(resources)
1054 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1055 device_ranges.insert(dev_idx, ranges.clone());
1056 bar_ranges.extend(ranges);
1057 }
1058 }
1059 }
1060 Ok((bar_ranges, subordinate_bus))
1061}
1062
1063pub fn assign_pci_addresses(
1065 devices: &mut [(Box<dyn BusDeviceObj>, Option<Minijail>)],
1066 resources: &mut SystemAllocator,
1067) -> Result<(), DeviceRegistrationError> {
1068 for pci_device in devices
1070 .iter_mut()
1071 .filter_map(|(device, _jail)| device.as_pci_device_mut())
1072 .filter(|pci_device| pci_device.preferred_address().is_some())
1073 {
1074 let _ = pci_device
1075 .allocate_address(resources)
1076 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1077 }
1078
1079 for pci_device in devices
1081 .iter_mut()
1082 .filter_map(|(device, _jail)| device.as_pci_device_mut())
1083 .filter(|pci_device| pci_device.preferred_address().is_none())
1084 {
1085 let _ = pci_device
1086 .allocate_address(resources)
1087 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1088 }
1089
1090 Ok(())
1091}
1092
1093pub fn generate_pci_root(
1095 mut devices: Vec<(Box<dyn PciDevice>, Option<Minijail>)>,
1096 irq_chip: &dyn IrqChip,
1097 mmio_bus: Arc<Bus>,
1098 mmio_base: GuestAddress,
1099 mmio_register_bit_num: usize,
1100 io_bus: Arc<Bus>,
1101 resources: &mut SystemAllocator,
1102 mut vm: &dyn Vm,
1103 max_irqs: usize,
1104 vcfg_base: Option<u64>,
1105 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
1106) -> Result<
1107 (
1108 PciRoot,
1109 Vec<(PciAddress, u32, PciInterruptPin)>,
1110 BTreeMap<u32, String>,
1111 BTreeMap<PciAddress, Vec<u8>>,
1112 BTreeMap<PciAddress, Vec<u8>>,
1113 ),
1114 DeviceRegistrationError,
1115> {
1116 let mut device_addrs = Vec::new();
1117
1118 for (device, _jail) in devices.iter_mut() {
1119 let address = device
1120 .allocate_address(resources)
1121 .map_err(DeviceRegistrationError::AllocateDeviceAddrs)?;
1122 device_addrs.push(address);
1123 }
1124
1125 let mut device_ranges = BTreeMap::new();
1126 let mut io_ranges = BTreeMap::new();
1127 let root_bus = Arc::new(Mutex::new(PciBus::new(0, 0, false)));
1128
1129 generate_pci_topology(
1130 root_bus.clone(),
1131 resources,
1132 &mut io_ranges,
1133 &mut device_ranges,
1134 &device_addrs,
1135 &mut devices,
1136 )?;
1137
1138 let mut root = PciRoot::new(
1139 vm,
1140 Arc::downgrade(&mmio_bus),
1141 mmio_base,
1142 mmio_register_bit_num,
1143 Arc::downgrade(&io_bus),
1144 root_bus,
1145 )
1146 .map_err(DeviceRegistrationError::CreateRoot)?;
1147 #[cfg_attr(windows, allow(unused_mut))]
1148 let mut pid_labels = BTreeMap::new();
1149
1150 let mut pci_irqs = Vec::new();
1152 let mut irqs: Vec<u32> = Vec::new();
1153
1154 let mut dev_pin_irq = BTreeMap::new();
1156
1157 for (dev_idx, (device, _jail)) in devices.iter_mut().enumerate() {
1158 let pci_address = device_addrs[dev_idx];
1159
1160 let irq = match device.preferred_irq() {
1161 PreferredIrq::Fixed { pin, gsi } => {
1162 resources.reserve_irq(gsi);
1164 Some((pin, gsi))
1165 }
1166 PreferredIrq::Any => {
1167 let pin = match pci_address.func % 4 {
1174 0 => PciInterruptPin::IntA,
1175 1 => PciInterruptPin::IntB,
1176 2 => PciInterruptPin::IntC,
1177 _ => PciInterruptPin::IntD,
1178 };
1179
1180 let pin_key = (pci_address.bus, pci_address.dev, pin);
1184 let irq_num = if let Some(irq_num) = dev_pin_irq.get(&pin_key) {
1185 *irq_num
1186 } else {
1187 let irq_num = if irqs.len() < max_irqs {
1190 let irq_num = resources
1191 .allocate_irq()
1192 .ok_or(DeviceRegistrationError::AllocateIrq)?;
1193 irqs.push(irq_num);
1194 irq_num
1195 } else {
1196 irqs[dev_idx % max_irqs]
1199 };
1200
1201 dev_pin_irq.insert(pin_key, irq_num);
1202 irq_num
1203 };
1204 Some((pin, irq_num))
1205 }
1206 PreferredIrq::None => {
1207 None
1209 }
1210 };
1211
1212 if let Some((pin, gsi)) = irq {
1213 let intx_event =
1214 devices::IrqLevelEvent::new().map_err(DeviceRegistrationError::EventCreate)?;
1215
1216 device.assign_irq(
1217 intx_event
1218 .try_clone()
1219 .map_err(DeviceRegistrationError::EventClone)?,
1220 pin,
1221 gsi,
1222 );
1223
1224 irq_chip
1225 .register_level_irq_event(gsi, &intx_event, IrqEventSource::from_device(device))
1226 .map_err(DeviceRegistrationError::RegisterIrqfd)?;
1227
1228 pci_irqs.push((pci_address, gsi, pin));
1229 }
1230 }
1231
1232 let devices = {
1237 let (sandboxed, non_sandboxed): (Vec<_>, Vec<_>) = devices
1238 .into_iter()
1239 .enumerate()
1240 .partition(|(_, (_, jail))| jail.is_some());
1241 sandboxed.into_iter().chain(non_sandboxed)
1242 };
1243
1244 let mut amls = BTreeMap::new();
1245 let mut gpe_scope_amls = BTreeMap::new();
1246 for (dev_idx, dev_value) in devices {
1247 #[cfg(any(target_os = "android", target_os = "linux"))]
1248 let (mut device, jail) = dev_value;
1249 #[cfg(windows)]
1250 let (mut device, _) = dev_value;
1251 let address = device_addrs[dev_idx];
1252
1253 let mut keep_rds = device.keep_rds();
1254 syslog::push_descriptors(&mut keep_rds);
1255 cros_tracing::push_descriptors!(&mut keep_rds);
1256 metrics::push_descriptors(&mut keep_rds);
1257 keep_rds.append(&mut vm.get_memory().as_raw_descriptors());
1258
1259 let ranges = io_ranges.remove(&dev_idx).unwrap_or_default();
1260 let device_ranges = device_ranges.remove(&dev_idx).unwrap_or_default();
1261 device
1262 .register_device_capabilities()
1263 .map_err(DeviceRegistrationError::RegisterDeviceCapabilities)?;
1264
1265 if let Some(vcfg_base) = vcfg_base {
1266 let (methods, shm) = device.generate_acpi_methods();
1267 if !methods.is_empty() {
1268 amls.insert(address, methods);
1269 }
1270 if let Some((offset, mmap)) = shm {
1271 let _ = vm.add_memory_region(
1272 GuestAddress(vcfg_base + offset as u64),
1273 Box::new(mmap),
1274 false,
1275 false,
1276 MemCacheType::CacheCoherent,
1277 );
1278 }
1279 }
1280 let gpe_nr = device.set_gpe(resources);
1281
1282 #[cfg(any(target_os = "android", target_os = "linux"))]
1283 let arced_dev: Arc<Mutex<dyn BusDevice>> = if let Some(jail) = jail {
1284 let proxy = ProxyDevice::new(
1285 device,
1286 jail,
1287 keep_rds,
1288 #[cfg(feature = "swap")]
1289 swap_controller,
1290 )
1291 .map_err(DeviceRegistrationError::ProxyDeviceCreation)?;
1292 pid_labels.insert(proxy.pid() as u32, proxy.debug_label());
1293 Arc::new(Mutex::new(proxy))
1294 } else {
1295 device.on_sandboxed();
1296 Arc::new(Mutex::new(device))
1297 };
1298 #[cfg(windows)]
1299 let arced_dev = {
1300 device.on_sandboxed();
1301 Arc::new(Mutex::new(device))
1302 };
1303 root.add_device(address, arced_dev.clone(), &mut vm)
1304 .map_err(DeviceRegistrationError::PciRootAddDevice)?;
1305 for range in &ranges {
1306 mmio_bus
1307 .insert(arced_dev.clone(), range.addr, range.size)
1308 .map_err(DeviceRegistrationError::MmioInsert)?;
1309 }
1310
1311 for range in &device_ranges {
1312 mmio_bus
1313 .insert(arced_dev.clone(), range.addr, range.size)
1314 .map_err(DeviceRegistrationError::MmioInsert)?;
1315 }
1316
1317 if let Some(gpe_nr) = gpe_nr {
1318 if let Some(acpi_path) = root.acpi_path(&address) {
1319 let mut gpe_aml = Vec::new();
1320
1321 GpeScope {}.cast_to_aml_bytes(
1322 &mut gpe_aml,
1323 gpe_nr,
1324 format!("\\{acpi_path}").as_str(),
1325 );
1326 if !gpe_aml.is_empty() {
1327 gpe_scope_amls.insert(address, gpe_aml);
1328 }
1329 }
1330 }
1331 }
1332
1333 Ok((root, pci_irqs, pid_labels, amls, gpe_scope_amls))
1334}
1335
1336#[sorted]
1338#[derive(Error, Debug)]
1339pub enum LoadImageError {
1340 #[error("Alignment not a power of two: {0}")]
1341 BadAlignment(u64),
1342 #[error("Getting image size failed: {0}")]
1343 GetLen(io::Error),
1344 #[error("GuestMemory get slice failed: {0}")]
1345 GuestMemorySlice(GuestMemoryError),
1346 #[error("Image size too large: {0}")]
1347 ImageSizeTooLarge(u64),
1348 #[error("No suitable memory region found")]
1349 NoSuitableMemoryRegion,
1350 #[error("Reading image into memory failed: {0}")]
1351 ReadToMemory(io::Error),
1352 #[error("Cannot load zero-sized image")]
1353 ZeroSizedImage,
1354}
1355
1356pub fn load_image<F>(
1367 guest_mem: &GuestMemory,
1368 image: &mut F,
1369 guest_addr: GuestAddress,
1370 max_size: u64,
1371) -> Result<u32, LoadImageError>
1372where
1373 F: FileReadWriteAtVolatile + FileGetLen,
1374{
1375 let size = image.get_len().map_err(LoadImageError::GetLen)?;
1376
1377 if size > u32::MAX as u64 || size > max_size {
1378 return Err(LoadImageError::ImageSizeTooLarge(size));
1379 }
1380
1381 let size = size as u32;
1383
1384 let guest_slice = guest_mem
1385 .get_slice_at_addr(guest_addr, size as usize)
1386 .map_err(LoadImageError::GuestMemorySlice)?;
1387 image
1388 .read_exact_at_volatile(guest_slice, 0)
1389 .map_err(LoadImageError::ReadToMemory)?;
1390
1391 Ok(size)
1392}
1393
1394pub fn load_image_high<F>(
1409 guest_mem: &GuestMemory,
1410 image: &mut F,
1411 min_guest_addr: GuestAddress,
1412 max_guest_addr: GuestAddress,
1413 region_filter: Option<fn(&MemoryRegionInformation) -> bool>,
1414 align: u64,
1415) -> Result<(GuestAddress, u32), LoadImageError>
1416where
1417 F: FileReadWriteAtVolatile + FileGetLen,
1418{
1419 if !align.is_power_of_two() {
1420 return Err(LoadImageError::BadAlignment(align));
1421 }
1422
1423 let max_size = max_guest_addr.offset_from(min_guest_addr) & !(align - 1);
1424 let size = image.get_len().map_err(LoadImageError::GetLen)?;
1425
1426 if size == 0 {
1427 return Err(LoadImageError::ZeroSizedImage);
1428 }
1429
1430 if size > u32::MAX as u64 || size > max_size {
1431 return Err(LoadImageError::ImageSizeTooLarge(size));
1432 }
1433
1434 let mut regions: Vec<_> = guest_mem
1437 .regions()
1438 .filter(region_filter.unwrap_or(|_| true))
1439 .collect();
1440 regions.sort_unstable_by(|a, b| a.guest_addr.cmp(&b.guest_addr));
1441
1442 let guest_addr = regions
1445 .into_iter()
1446 .rev()
1447 .filter_map(|r| {
1448 let rgn_max_addr = r
1450 .guest_addr
1451 .checked_add((r.size as u64).checked_sub(1)?)?
1452 .min(max_guest_addr);
1453 let rgn_start_aligned = r.guest_addr.align(align)?;
1455 let image_addr = rgn_max_addr.checked_sub(size - 1)? & !(align - 1);
1457
1458 if image_addr >= rgn_start_aligned {
1460 Some(image_addr)
1461 } else {
1462 None
1463 }
1464 })
1465 .find(|&addr| addr >= min_guest_addr)
1466 .ok_or(LoadImageError::NoSuitableMemoryRegion)?;
1467
1468 let size = size as u32;
1470
1471 let guest_slice = guest_mem
1472 .get_slice_at_addr(guest_addr, size as usize)
1473 .map_err(LoadImageError::GuestMemorySlice)?;
1474 image
1475 .read_exact_at_volatile(guest_slice, 0)
1476 .map_err(LoadImageError::ReadToMemory)?;
1477
1478 Ok((guest_addr, size))
1479}
1480
1481#[derive(Clone, Debug, Default, Serialize, Deserialize, FromKeyValues, PartialEq, Eq)]
1483#[serde(deny_unknown_fields, rename_all = "kebab-case")]
1484pub struct SmbiosOptions {
1485 pub bios_vendor: Option<String>,
1487
1488 pub bios_version: Option<String>,
1490
1491 pub manufacturer: Option<String>,
1493
1494 pub product_name: Option<String>,
1496
1497 pub serial_number: Option<String>,
1499
1500 pub uuid: Option<Uuid>,
1502
1503 #[serde(default)]
1505 pub oem_strings: Vec<String>,
1506}
1507
1508#[cfg(test)]
1509mod tests {
1510 use serde_keyvalue::from_key_values;
1511 use tempfile::tempfile;
1512
1513 use super::*;
1514
1515 #[test]
1516 fn parse_pstore() {
1517 let res: Pstore = from_key_values("path=/some/path,size=16384").unwrap();
1518 assert_eq!(
1519 res,
1520 Pstore {
1521 path: "/some/path".into(),
1522 size: 16384,
1523 }
1524 );
1525
1526 let res = from_key_values::<Pstore>("path=/some/path");
1527 assert!(res.is_err());
1528
1529 let res = from_key_values::<Pstore>("size=16384");
1530 assert!(res.is_err());
1531
1532 let res = from_key_values::<Pstore>("");
1533 assert!(res.is_err());
1534 }
1535
1536 #[test]
1537 fn deserialize_cpuset_serde_kv() {
1538 let res: CpuSet = from_key_values("[0,4,7]").unwrap();
1539 assert_eq!(res, CpuSet::new(vec![0, 4, 7]));
1540
1541 let res: CpuSet = from_key_values("[9-12]").unwrap();
1542 assert_eq!(res, CpuSet::new(vec![9, 10, 11, 12]));
1543
1544 let res: CpuSet = from_key_values("[0,4,7,9-12,15]").unwrap();
1545 assert_eq!(res, CpuSet::new(vec![0, 4, 7, 9, 10, 11, 12, 15]));
1546 }
1547
1548 #[test]
1549 fn deserialize_serialize_cpuset_json() {
1550 let json_str = "[0,4,7]";
1551 let cpuset = CpuSet::new(vec![0, 4, 7]);
1552 let res: CpuSet = serde_json::from_str(json_str).unwrap();
1553 assert_eq!(res, cpuset);
1554 assert_eq!(serde_json::to_string(&cpuset).unwrap(), json_str);
1555
1556 let json_str = r#"["9-12"]"#;
1557 let cpuset = CpuSet::new(vec![9, 10, 11, 12]);
1558 let res: CpuSet = serde_json::from_str(json_str).unwrap();
1559 assert_eq!(res, cpuset);
1560 assert_eq!(serde_json::to_string(&cpuset).unwrap(), json_str);
1561
1562 let json_str = r#"[0,4,7,"9-12",15]"#;
1563 let cpuset = CpuSet::new(vec![0, 4, 7, 9, 10, 11, 12, 15]);
1564 let res: CpuSet = serde_json::from_str(json_str).unwrap();
1565 assert_eq!(res, cpuset);
1566 assert_eq!(serde_json::to_string(&cpuset).unwrap(), json_str);
1567 }
1568
1569 #[test]
1570 fn load_image_high_max_4g() {
1571 let mem = GuestMemory::new(&[
1572 (GuestAddress(0x0000_0000), 0x4000_0000), (GuestAddress(0x8000_0000), 0x4000_0000), ])
1575 .unwrap();
1576
1577 const TEST_IMAGE_SIZE: u64 = 1234;
1578 let mut test_image = tempfile().unwrap();
1579 test_image.set_len(TEST_IMAGE_SIZE).unwrap();
1580
1581 const TEST_ALIGN: u64 = 0x8000;
1582 let (addr, size) = load_image_high(
1583 &mem,
1584 &mut test_image,
1585 GuestAddress(0x8000),
1586 GuestAddress(0xFFFF_FFFF), None,
1588 TEST_ALIGN,
1589 )
1590 .unwrap();
1591
1592 assert_eq!(addr, GuestAddress(0xBFFF_8000));
1593 assert_eq!(addr.offset() % TEST_ALIGN, 0);
1594 assert_eq!(size, TEST_IMAGE_SIZE as u32);
1595 }
1596}