1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! RISC-V 64-bit architecture support.
6
7 #![cfg(target_arch = "riscv64")]
8
9 use std::collections::BTreeMap;
10 use std::io::{self};
11 use std::path::PathBuf;
12 use std::sync::mpsc;
13 use std::sync::Arc;
14
15 use arch::get_serial_cmdline;
16 use arch::CpuSet;
17 use arch::DtbOverlay;
18 use arch::GetSerialCmdlineError;
19 use arch::RunnableLinuxVm;
20 use arch::VmComponents;
21 use arch::VmImage;
22 use base::Event;
23 use base::SendTube;
24 use devices::serial_device::SerialHardware;
25 use devices::serial_device::SerialParameters;
26 use devices::Bus;
27 use devices::BusDeviceObj;
28 use devices::BusError;
29 use devices::BusType;
30 use devices::IrqChipRiscv64;
31 use devices::PciAddress;
32 use devices::PciConfigMmio;
33 use devices::PciDevice;
34 use devices::PciRootCommand;
35 #[cfg(feature = "gdb")]
36 use gdbstub::arch::Arch;
37 #[cfg(feature = "gdb")]
38 use gdbstub_arch::riscv::Riscv64 as GdbArch;
39 use hypervisor::CoreRegister;
40 use hypervisor::CpuConfigRiscv64;
41 use hypervisor::Hypervisor;
42 use hypervisor::ProtectionType;
43 use hypervisor::TimerRegister;
44 use hypervisor::VcpuInitRiscv64;
45 use hypervisor::VcpuRegister;
46 use hypervisor::VcpuRiscv64;
47 use hypervisor::Vm;
48 use hypervisor::VmRiscv64;
49 #[cfg(windows)]
50 use jail::FakeMinijailStub as Minijail;
51 #[cfg(any(target_os = "android", target_os = "linux"))]
52 use minijail::Minijail;
53 use remain::sorted;
54 use resources::AddressRange;
55 use resources::SystemAllocator;
56 use resources::SystemAllocatorConfig;
57 #[cfg(any(target_os = "android", target_os = "linux"))]
58 use sync::Condvar;
59 use sync::Mutex;
60 use thiserror::Error;
61 use vm_control::BatteryType;
62 use vm_memory::GuestAddress;
63 #[cfg(feature = "gdb")]
64 use vm_memory::GuestMemory;
65 use vm_memory::MemoryRegionOptions;
66
67 mod fdt;
68
69 // We place the kernel at offset 8MB
70 const RISCV64_KERNEL_OFFSET: u64 = 0x20_0000;
71 const RISCV64_INITRD_ALIGN: u64 = 8;
72 const RISCV64_FDT_ALIGN: u64 = 0x40_0000;
73
74 // This indicates the start of DRAM inside the physical address space.
75 const RISCV64_PHYS_MEM_START: u64 = 0x8000_0000;
76
77 // PCI MMIO configuration region base address.
78 const RISCV64_PCI_CFG_BASE: u64 = 0x1_0000;
79 // PCI MMIO configuration region size.
80 const RISCV64_PCI_CFG_SIZE: u64 = 0x100_0000;
81 // This is the base address of MMIO devices.
82 const RISCV64_MMIO_BASE: u64 = 0x0300_0000;
83 // Size of the whole MMIO region.
84 const RISCV64_MMIO_SIZE: u64 = 0x10_0000;
85
86 const RISCV64_FDT_MAX_SIZE: u64 = 0x1_0000;
87
get_kernel_addr() -> GuestAddress88 fn get_kernel_addr() -> GuestAddress {
89 GuestAddress(RISCV64_PHYS_MEM_START + RISCV64_KERNEL_OFFSET)
90 }
91
92 const RISCV64_IRQ_BASE: u32 = 1;
93
94 #[sorted]
95 #[derive(Error, Debug)]
96 pub enum Error {
97 #[error("unable to clone an Event: {0}")]
98 CloneEvent(base::Error),
99 #[error("failed to clone IRQ chip: {0}")]
100 CloneIrqChip(base::Error),
101 #[error("the given kernel command line was invalid: {0}")]
102 Cmdline(kernel_cmdline::Error),
103 #[error("unable to make an Event: {0}")]
104 CreateEvent(base::Error),
105 #[error("FDT could not be created: {0}")]
106 CreateFdt(cros_fdt::Error),
107 #[error("failed to create a PCI root hub: {0}")]
108 CreatePciRoot(arch::DeviceRegistrationError),
109 #[error("failed to create platform bus: {0}")]
110 CreatePlatformBus(arch::DeviceRegistrationError),
111 #[error("unable to create serial devices: {0}")]
112 CreateSerialDevices(arch::DeviceRegistrationError),
113 #[error("failed to create socket: {0}")]
114 CreateSocket(io::Error),
115 #[error("failed to create VCPU: {0}")]
116 CreateVcpu(base::Error),
117 #[error("vm created wrong kind of vcpu")]
118 DowncastVcpu,
119 #[error("failed to finalize devices: {0}")]
120 FinalizeDevices(base::Error),
121 #[error("failed to finalize IRQ chip: {0}")]
122 FinalizeIrqChip(base::Error),
123 #[error("failed to get serial cmdline: {0}")]
124 GetSerialCmdline(GetSerialCmdlineError),
125 #[error("Failed to get the timer base frequency: {0}")]
126 GetTimebase(base::Error),
127 #[error("Image type not supported on riscv")]
128 ImageTypeUnsupported,
129 #[error("initrd could not be loaded: {0}")]
130 InitrdLoadFailure(arch::LoadImageError),
131 #[error("kernel could not be loaded: {0}")]
132 KernelLoadFailure(arch::LoadImageError),
133 #[error("protected vms not supported on riscv(yet)")]
134 ProtectedVmUnsupported,
135 #[error("ramoops address is different from high_mmio_base: {0} vs {1}")]
136 RamoopsAddress(u64, u64),
137 #[error("failed to register irq fd: {0}")]
138 RegisterIrqfd(base::Error),
139 #[error("error registering PCI bus: {0}")]
140 RegisterPci(BusError),
141 #[error("error registering virtual socket device: {0}")]
142 RegisterVsock(arch::DeviceRegistrationError),
143 #[error("failed to set device attr: {0}")]
144 SetDeviceAttr(base::Error),
145 #[error("failed to set register: {0}")]
146 SetReg(base::Error),
147 #[error("Timebase frequency too large")]
148 TimebaseTooLarge,
149 #[error("this function isn't supported")]
150 Unsupported,
151 #[error("failed to initialize VCPU: {0}")]
152 VcpuInit(base::Error),
153 }
154
155 pub type Result<T> = std::result::Result<T, Error>;
156
157 pub struct Riscv64;
158
159 impl arch::LinuxArch for Riscv64 {
160 type Error = Error;
161
162 /// Returns a Vec of the valid memory addresses.
163 /// These should be used to configure the GuestMemory structure for the platfrom.
guest_memory_layout( components: &VmComponents, _hypervisor: &impl Hypervisor, ) -> std::result::Result<Vec<(GuestAddress, u64, MemoryRegionOptions)>, Self::Error>164 fn guest_memory_layout(
165 components: &VmComponents,
166 _hypervisor: &impl Hypervisor,
167 ) -> std::result::Result<Vec<(GuestAddress, u64, MemoryRegionOptions)>, Self::Error> {
168 Ok(vec![(
169 GuestAddress(RISCV64_PHYS_MEM_START),
170 components.memory_size,
171 Default::default(),
172 )])
173 }
174
get_system_allocator_config<V: Vm>(vm: &V) -> SystemAllocatorConfig175 fn get_system_allocator_config<V: Vm>(vm: &V) -> SystemAllocatorConfig {
176 get_resource_allocator_config(vm.get_memory().memory_size(), vm.get_guest_phys_addr_bits())
177 }
178
build_vm<V, Vcpu>( mut components: VmComponents, _vm_evt_wrtube: &SendTube, system_allocator: &mut SystemAllocator, serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>, serial_jail: Option<Minijail>, (_bat_type, _bat_jail): (Option<BatteryType>, Option<Minijail>), mut vm: V, ramoops_region: Option<arch::pstore::RamoopsRegion>, devices: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>, irq_chip: &mut dyn IrqChipRiscv64, vcpu_ids: &mut Vec<usize>, _dump_device_tree_blob: Option<PathBuf>, _debugcon_jail: Option<Minijail>, #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>, #[cfg(any(target_os = "android", target_os = "linux"))] _guest_suspended_cvar: Option< Arc<(Mutex<bool>, Condvar)>, >, device_tree_overlays: Vec<DtbOverlay>, ) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error> where V: VmRiscv64, Vcpu: VcpuRiscv64,179 fn build_vm<V, Vcpu>(
180 mut components: VmComponents,
181 _vm_evt_wrtube: &SendTube,
182 system_allocator: &mut SystemAllocator,
183 serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
184 serial_jail: Option<Minijail>,
185 (_bat_type, _bat_jail): (Option<BatteryType>, Option<Minijail>),
186 mut vm: V,
187 ramoops_region: Option<arch::pstore::RamoopsRegion>,
188 devices: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
189 irq_chip: &mut dyn IrqChipRiscv64,
190 vcpu_ids: &mut Vec<usize>,
191 _dump_device_tree_blob: Option<PathBuf>,
192 _debugcon_jail: Option<Minijail>,
193 #[cfg(feature = "swap")] swap_controller: &mut Option<swap::SwapController>,
194 #[cfg(any(target_os = "android", target_os = "linux"))] _guest_suspended_cvar: Option<
195 Arc<(Mutex<bool>, Condvar)>,
196 >,
197 device_tree_overlays: Vec<DtbOverlay>,
198 ) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
199 where
200 V: VmRiscv64,
201 Vcpu: VcpuRiscv64,
202 {
203 if components.hv_cfg.protection_type == ProtectionType::Protected {
204 return Err(Error::ProtectedVmUnsupported);
205 }
206
207 let mem = vm.get_memory().clone();
208
209 let mmio_bus = Arc::new(Bus::new(BusType::Mmio));
210
211 // Riscv doesn't really use the io bus like x86, so just create an empty bus.
212 let io_bus = Arc::new(Bus::new(BusType::Io));
213
214 let com_evt_1_3 = Event::new().map_err(Error::CreateEvent)?;
215 let com_evt_2_4 = Event::new().map_err(Error::CreateEvent)?;
216 let serial_devices = arch::add_serial_devices(
217 components.hv_cfg.protection_type,
218 &mmio_bus,
219 // TODO: the IRQ numbers are bogus since the events aren't actually wired up
220 (0, &com_evt_1_3),
221 (0, &com_evt_2_4),
222 serial_parameters,
223 serial_jail,
224 #[cfg(feature = "swap")]
225 swap_controller,
226 )
227 .map_err(Error::CreateSerialDevices)?;
228
229 let (pci_devices, others): (Vec<_>, Vec<_>) = devices
230 .into_iter()
231 .partition(|(dev, _)| dev.as_pci_device().is_some());
232 let pci_devices = pci_devices
233 .into_iter()
234 .map(|(dev, jail_orig)| (dev.into_pci_device().unwrap(), jail_orig))
235 .collect();
236 let (pci, pci_irqs, mut pid_debug_label_map, _amls, _gpe_scope_amls) =
237 arch::generate_pci_root(
238 pci_devices,
239 irq_chip.as_irq_chip_mut(),
240 Arc::clone(&mmio_bus),
241 GuestAddress(RISCV64_PCI_CFG_BASE),
242 8,
243 Arc::clone(&io_bus),
244 system_allocator,
245 &mut vm,
246 devices::IMSIC_MAX_INT_IDS as usize,
247 None,
248 #[cfg(feature = "swap")]
249 swap_controller,
250 )
251 .map_err(Error::CreatePciRoot)?;
252
253 let pci_root = Arc::new(Mutex::new(pci));
254 let pci_bus = Arc::new(Mutex::new(PciConfigMmio::new(pci_root.clone(), 8)));
255 let (platform_devices, _others): (Vec<_>, Vec<_>) = others
256 .into_iter()
257 .partition(|(dev, _)| dev.as_platform_device().is_some());
258
259 let platform_devices = platform_devices
260 .into_iter()
261 .map(|(dev, jail_orig)| (*(dev.into_platform_device().unwrap()), jail_orig))
262 .collect();
263 let (platform_devices, mut platform_pid_debug_label_map, dev_resources) =
264 arch::sys::linux::generate_platform_bus(
265 platform_devices,
266 irq_chip.as_irq_chip_mut(),
267 &mmio_bus,
268 system_allocator,
269 &mut vm,
270 #[cfg(feature = "swap")]
271 swap_controller,
272 components.hv_cfg.protection_type,
273 )
274 .map_err(Error::CreatePlatformBus)?;
275 pid_debug_label_map.append(&mut platform_pid_debug_label_map);
276
277 let mut cmdline = get_base_linux_cmdline();
278
279 if let Some(ramoops_region) = ramoops_region {
280 arch::pstore::add_ramoops_kernel_cmdline(&mut cmdline, &ramoops_region)
281 .map_err(Error::Cmdline)?;
282 }
283
284 mmio_bus
285 .insert(pci_bus, RISCV64_PCI_CFG_BASE, RISCV64_PCI_CFG_SIZE)
286 .map_err(Error::RegisterPci)?;
287
288 get_serial_cmdline(&mut cmdline, serial_parameters, "mmio", &serial_devices)
289 .map_err(Error::GetSerialCmdline)?;
290 for param in components.extra_kernel_params {
291 cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
292 }
293
294 // Event used by PMDevice to notify crosvm that guest OS is trying to suspend.
295 let suspend_evt = Event::new().map_err(Error::CreateEvent)?;
296
297 // separate out image loading from other setup to get a specific error for
298 // image loading
299 let initrd;
300 let kernel_initrd_end = match components.vm_image {
301 VmImage::Bios(ref _bios) => {
302 return Err(Error::ImageTypeUnsupported);
303 }
304 VmImage::Kernel(ref mut kernel_image) => {
305 let kernel_size =
306 arch::load_image(&mem, kernel_image, get_kernel_addr(), u64::max_value())
307 .map_err(Error::KernelLoadFailure)?;
308 let kernel_end = get_kernel_addr().offset() + kernel_size as u64;
309 initrd = match components.initrd_image {
310 Some(initrd_file) => {
311 let mut initrd_file = initrd_file;
312 let initrd_addr =
313 (kernel_end + (RISCV64_INITRD_ALIGN - 1)) & !(RISCV64_INITRD_ALIGN - 1);
314 let initrd_max_size =
315 components.memory_size - (initrd_addr - RISCV64_PHYS_MEM_START);
316 let initrd_addr = GuestAddress(initrd_addr);
317 let initrd_size =
318 arch::load_image(&mem, &mut initrd_file, initrd_addr, initrd_max_size)
319 .map_err(Error::InitrdLoadFailure)?;
320 Some((initrd_addr, initrd_size))
321 }
322 None => None,
323 };
324 if let Some((initrd_addr, initrd_size)) = initrd {
325 initrd_addr.offset() + initrd_size as u64 - RISCV64_PHYS_MEM_START
326 } else {
327 kernel_end - RISCV64_PHYS_MEM_START
328 }
329 }
330 };
331
332 // Creates vcpus early as the irqchip needs them created to attach interrupts.
333 let vcpu_count = components.vcpu_count;
334 let mut vcpus = Vec::with_capacity(vcpu_count);
335 for vcpu_id in 0..vcpu_count {
336 let vcpu: Vcpu = *vm
337 .create_vcpu(vcpu_id)
338 .map_err(Error::CreateVcpu)?
339 .downcast::<Vcpu>()
340 .map_err(|_| Error::DowncastVcpu)?;
341 vcpus.push(vcpu);
342 vcpu_ids.push(vcpu_id);
343 }
344
345 irq_chip.finalize().map_err(Error::FinalizeIrqChip)?;
346
347 irq_chip
348 .finalize_devices(system_allocator, &io_bus, &mmio_bus)
349 .map_err(Error::FinalizeDevices)?;
350 let (aia_num_ids, aia_num_sources) = irq_chip.get_num_ids_sources();
351
352 let pci_cfg = fdt::PciConfigRegion {
353 base: RISCV64_PCI_CFG_BASE,
354 size: RISCV64_PCI_CFG_SIZE,
355 };
356
357 let pci_ranges: Vec<fdt::PciRange> = system_allocator
358 .mmio_pools()
359 .iter()
360 .map(|range| fdt::PciRange {
361 space: fdt::PciAddressSpace::Memory64,
362 bus_address: range.start,
363 cpu_physical_address: range.start,
364 size: range.len().unwrap(),
365 prefetchable: false,
366 })
367 .collect();
368
369 let fdt_offset = (kernel_initrd_end + (RISCV64_FDT_ALIGN - 1)) & !(RISCV64_FDT_ALIGN - 1);
370
371 let timebase_freq: u32 = vcpus[0]
372 .get_one_reg(VcpuRegister::Timer(TimerRegister::TimebaseFrequency))
373 .map_err(Error::GetTimebase)?
374 .try_into()
375 .map_err(|_| Error::TimebaseTooLarge)?;
376
377 fdt::create_fdt(
378 RISCV64_FDT_MAX_SIZE as usize,
379 &mem,
380 pci_irqs,
381 pci_cfg,
382 &pci_ranges,
383 dev_resources,
384 components.vcpu_count as u32,
385 fdt_offset,
386 aia_num_ids,
387 aia_num_sources,
388 cmdline.as_str(),
389 initrd,
390 timebase_freq,
391 device_tree_overlays,
392 )
393 .map_err(Error::CreateFdt)?;
394
395 let vcpu_init = vec![
396 VcpuInitRiscv64::new(GuestAddress(fdt_offset + RISCV64_PHYS_MEM_START));
397 vcpu_count
398 ];
399
400 Ok(RunnableLinuxVm {
401 vm,
402 vcpu_count: components.vcpu_count,
403 vcpus: Some(vcpus),
404 vcpu_init,
405 vcpu_affinity: components.vcpu_affinity,
406 no_smt: false,
407 irq_chip: irq_chip.try_box_clone().map_err(Error::CloneIrqChip)?,
408 io_bus,
409 mmio_bus,
410 pid_debug_label_map,
411 resume_notify_devices: Vec::new(),
412 root_config: pci_root,
413 platform_devices,
414 hotplug_bus: BTreeMap::new(),
415 rt_cpus: components.rt_cpus,
416 delay_rt: components.delay_rt,
417 suspend_evt,
418 bat_control: None,
419 #[cfg(feature = "gdb")]
420 gdb: components.gdb,
421 pm: None,
422 devices_thread: None,
423 vm_request_tube: None,
424 })
425 }
426
configure_vcpu<V: Vm>( _vm: &V, _hypervisor: &dyn Hypervisor, _irq_chip: &mut dyn IrqChipRiscv64, vcpu: &mut dyn VcpuRiscv64, _vcpu_init: VcpuInitRiscv64, vcpu_id: usize, _num_cpus: usize, cpu_config: Option<CpuConfigRiscv64>, ) -> std::result::Result<(), Self::Error>427 fn configure_vcpu<V: Vm>(
428 _vm: &V,
429 _hypervisor: &dyn Hypervisor,
430 _irq_chip: &mut dyn IrqChipRiscv64,
431 vcpu: &mut dyn VcpuRiscv64,
432 _vcpu_init: VcpuInitRiscv64,
433 vcpu_id: usize,
434 _num_cpus: usize,
435 cpu_config: Option<CpuConfigRiscv64>,
436 ) -> std::result::Result<(), Self::Error> {
437 vcpu.set_one_reg(VcpuRegister::Core(CoreRegister::Pc), get_kernel_addr().0)
438 .map_err(Self::Error::SetReg)?;
439 vcpu.set_one_reg(VcpuRegister::Core(CoreRegister::A0), vcpu_id as u64)
440 .map_err(Self::Error::SetReg)?;
441 vcpu.set_one_reg(
442 VcpuRegister::Core(CoreRegister::A1),
443 cpu_config.unwrap().fdt_address.0,
444 )
445 .map_err(Self::Error::SetReg)?;
446
447 Ok(())
448 }
449
register_pci_device<V: VmRiscv64, Vcpu: VcpuRiscv64>( _linux: &mut RunnableLinuxVm<V, Vcpu>, _device: Box<dyn PciDevice>, _minijail: Option<Minijail>, _resources: &mut SystemAllocator, _tube: &mpsc::Sender<PciRootCommand>, #[cfg(feature = "swap")] _swap_controller: &mut Option<swap::SwapController>, ) -> std::result::Result<PciAddress, Self::Error>450 fn register_pci_device<V: VmRiscv64, Vcpu: VcpuRiscv64>(
451 _linux: &mut RunnableLinuxVm<V, Vcpu>,
452 _device: Box<dyn PciDevice>,
453 _minijail: Option<Minijail>,
454 _resources: &mut SystemAllocator,
455 _tube: &mpsc::Sender<PciRootCommand>,
456 #[cfg(feature = "swap")] _swap_controller: &mut Option<swap::SwapController>,
457 ) -> std::result::Result<PciAddress, Self::Error> {
458 // hotplug function isn't verified on Riscv64, so set it unsupported here.
459 Err(Error::Unsupported)
460 }
461
get_host_cpu_frequencies_khz() -> Result<BTreeMap<usize, Vec<u32>>>462 fn get_host_cpu_frequencies_khz() -> Result<BTreeMap<usize, Vec<u32>>> {
463 Ok(BTreeMap::new())
464 }
465
get_host_cpu_capacity() -> Result<BTreeMap<usize, u32>>466 fn get_host_cpu_capacity() -> Result<BTreeMap<usize, u32>> {
467 Ok(BTreeMap::new())
468 }
469
get_host_cpu_clusters() -> Result<Vec<CpuSet>>470 fn get_host_cpu_clusters() -> Result<Vec<CpuSet>> {
471 Ok(Vec::new())
472 }
473 }
474
475 #[cfg(feature = "gdb")]
476 impl<T: VcpuRiscv64> arch::GdbOps<T> for Riscv64 {
477 type Error = Error;
478
read_memory( _vcpu: &T, _guest_mem: &GuestMemory, _vaddr: GuestAddress, _len: usize, ) -> Result<Vec<u8>>479 fn read_memory(
480 _vcpu: &T,
481 _guest_mem: &GuestMemory,
482 _vaddr: GuestAddress,
483 _len: usize,
484 ) -> Result<Vec<u8>> {
485 unimplemented!();
486 }
487
write_memory( _vcpu: &T, _guest_mem: &GuestMemory, _vaddr: GuestAddress, _buf: &[u8], ) -> Result<()>488 fn write_memory(
489 _vcpu: &T,
490 _guest_mem: &GuestMemory,
491 _vaddr: GuestAddress,
492 _buf: &[u8],
493 ) -> Result<()> {
494 unimplemented!();
495 }
496
read_registers(_vcpu: &T) -> Result<<GdbArch as Arch>::Registers>497 fn read_registers(_vcpu: &T) -> Result<<GdbArch as Arch>::Registers> {
498 unimplemented!();
499 }
500
write_registers(_vcpu: &T, _regs: &<GdbArch as Arch>::Registers) -> Result<()>501 fn write_registers(_vcpu: &T, _regs: &<GdbArch as Arch>::Registers) -> Result<()> {
502 unimplemented!();
503 }
504
read_register(_vcpu: &T, _reg_id: <GdbArch as Arch>::RegId) -> Result<Vec<u8>>505 fn read_register(_vcpu: &T, _reg_id: <GdbArch as Arch>::RegId) -> Result<Vec<u8>> {
506 unimplemented!();
507 }
508
write_register(_vcpu: &T, _reg_id: <GdbArch as Arch>::RegId, _data: &[u8]) -> Result<()>509 fn write_register(_vcpu: &T, _reg_id: <GdbArch as Arch>::RegId, _data: &[u8]) -> Result<()> {
510 unimplemented!();
511 }
512
enable_singlestep(_vcpu: &T) -> Result<()>513 fn enable_singlestep(_vcpu: &T) -> Result<()> {
514 unimplemented!();
515 }
516
get_max_hw_breakpoints(_vcpu: &T) -> Result<usize>517 fn get_max_hw_breakpoints(_vcpu: &T) -> Result<usize> {
518 unimplemented!();
519 }
520
set_hw_breakpoints(_vcpu: &T, _breakpoints: &[GuestAddress]) -> Result<()>521 fn set_hw_breakpoints(_vcpu: &T, _breakpoints: &[GuestAddress]) -> Result<()> {
522 unimplemented!();
523 }
524 }
525
get_high_mmio_base_size(mem_size: u64, guest_phys_addr_bits: u8) -> (u64, u64)526 fn get_high_mmio_base_size(mem_size: u64, guest_phys_addr_bits: u8) -> (u64, u64) {
527 let guest_phys_end = 1u64 << guest_phys_addr_bits;
528 let high_mmio_base = RISCV64_PHYS_MEM_START + mem_size;
529 let size = guest_phys_end
530 .checked_sub(high_mmio_base)
531 .unwrap_or_else(|| {
532 panic!(
533 "guest_phys_end {:#x} < high_mmio_base {:#x}",
534 guest_phys_end, high_mmio_base,
535 );
536 });
537 (high_mmio_base, size)
538 }
539
get_base_linux_cmdline() -> kernel_cmdline::Cmdline540 fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
541 let mut cmdline = kernel_cmdline::Cmdline::new(base::pagesize());
542 cmdline.insert_str("panic=-1").unwrap();
543 cmdline
544 }
545
546 /// Returns a system resource allocator coniguration.
547 ///
548 /// # Arguments
549 ///
550 /// * `mem_size` - Size of guest memory (RAM) in bytes.
551 /// * `guest_phys_addr_bits` - Size of guest physical addresses (IPA) in bits.
get_resource_allocator_config(mem_size: u64, guest_phys_addr_bits: u8) -> SystemAllocatorConfig552 fn get_resource_allocator_config(mem_size: u64, guest_phys_addr_bits: u8) -> SystemAllocatorConfig {
553 let (high_mmio_base, high_mmio_size) = get_high_mmio_base_size(mem_size, guest_phys_addr_bits);
554 SystemAllocatorConfig {
555 io: None,
556 low_mmio: AddressRange::from_start_and_size(RISCV64_MMIO_BASE, RISCV64_MMIO_SIZE)
557 .expect("invalid mmio region"),
558 high_mmio: AddressRange::from_start_and_size(high_mmio_base, high_mmio_size)
559 .expect("invalid high mmio region"),
560 platform_mmio: None,
561 first_irq: RISCV64_IRQ_BASE,
562 }
563 }
564