• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use std::collections::BTreeMap;
6 
7 use base::error;
8 use base::Error;
9 use base::Result;
10 use cros_fdt::Fdt;
11 use cros_fdt::FdtNode;
12 use libc::ENOENT;
13 use libc::ENOTSUP;
14 use vm_memory::GuestAddress;
15 use vm_memory::MemoryRegionPurpose;
16 
17 use super::GunyahVcpu;
18 use super::GunyahVm;
19 use crate::Hypervisor;
20 use crate::PsciVersion;
21 use crate::VcpuAArch64;
22 use crate::VcpuRegAArch64;
23 use crate::VmAArch64;
24 use crate::PSCI_0_2;
25 
26 const GIC_FDT_IRQ_TYPE_SPI: u32 = 0;
27 
28 const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001;
29 const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004;
30 
fdt_create_shm_device( parent: &mut FdtNode, index: u32, guest_addr: GuestAddress, ) -> cros_fdt::Result<()>31 fn fdt_create_shm_device(
32     parent: &mut FdtNode,
33     index: u32,
34     guest_addr: GuestAddress,
35 ) -> cros_fdt::Result<()> {
36     let shm_name = format!("shm-{:x}", index);
37     let shm_node = parent.subnode_mut(&shm_name)?;
38     shm_node.set_prop("vdevice-type", "shm")?;
39     shm_node.set_prop("peer-default", ())?;
40     shm_node.set_prop("dma_base", 0u64)?;
41     let mem_node = shm_node.subnode_mut("memory")?;
42     // We have to add the shm device for RM to accept the swiotlb memparcel.
43     // Memparcel is only used on android14-6.1. Once android14-6.1 is EOL
44     // we should be able to remove all the times we call fdt_create_shm_device()
45     mem_node.set_prop("optional", ())?;
46     mem_node.set_prop("label", index)?;
47     mem_node.set_prop("#address-cells", 2u32)?;
48     mem_node.set_prop("base", guest_addr.offset())
49 }
50 
51 impl VmAArch64 for GunyahVm {
get_hypervisor(&self) -> &dyn Hypervisor52     fn get_hypervisor(&self) -> &dyn Hypervisor {
53         &self.gh
54     }
55 
load_protected_vm_firmware( &mut self, fw_addr: GuestAddress, fw_max_size: u64, ) -> Result<()>56     fn load_protected_vm_firmware(
57         &mut self,
58         fw_addr: GuestAddress,
59         fw_max_size: u64,
60     ) -> Result<()> {
61         self.set_protected_vm_firmware_ipa(fw_addr, fw_max_size)
62     }
63 
create_vcpu(&self, id: usize) -> Result<Box<dyn VcpuAArch64>>64     fn create_vcpu(&self, id: usize) -> Result<Box<dyn VcpuAArch64>> {
65         Ok(Box::new(GunyahVm::create_vcpu(self, id)?))
66     }
67 
create_fdt(&self, fdt: &mut Fdt, phandles: &BTreeMap<&str, u32>) -> cros_fdt::Result<()>68     fn create_fdt(&self, fdt: &mut Fdt, phandles: &BTreeMap<&str, u32>) -> cros_fdt::Result<()> {
69         let top_node = fdt.root_mut().subnode_mut("gunyah-vm-config")?;
70 
71         top_node.set_prop("image-name", "crosvm-vm")?;
72         top_node.set_prop("os-type", "linux")?;
73 
74         let memory_node = top_node.subnode_mut("memory")?;
75         memory_node.set_prop("#address-cells", 2u32)?;
76         memory_node.set_prop("#size-cells", 2u32)?;
77 
78         let mut base_set = false;
79         let mut firmware_set = false;
80         for region in self.guest_mem.regions() {
81             match region.options.purpose {
82                 MemoryRegionPurpose::GuestMemoryRegion => {
83                     // Assume first GuestMemoryRegion contains the payload
84                     if !base_set {
85                         base_set = true;
86                         memory_node.set_prop("base-address", region.guest_addr.offset())?;
87                     }
88                 }
89                 MemoryRegionPurpose::ProtectedFirmwareRegion => {
90                     if firmware_set {
91                         // Should only have one protected firmware memory region.
92                         error!("Multiple ProtectedFirmwareRegions unexpected.");
93                         unreachable!()
94                     }
95                     firmware_set = true;
96                     memory_node.set_prop("firmware-address", region.guest_addr.offset())?;
97                 }
98                 _ => {}
99             }
100         }
101 
102         let interrupts_node = top_node.subnode_mut("interrupts")?;
103         interrupts_node.set_prop("config", *phandles.get("intc").unwrap())?;
104 
105         let vcpus_node = top_node.subnode_mut("vcpus")?;
106         vcpus_node.set_prop("affinity", "proxy")?;
107 
108         let vdev_node = top_node.subnode_mut("vdevices")?;
109         vdev_node.set_prop("generate", "/hypervisor")?;
110 
111         for irq in self.routes.lock().iter() {
112             let bell_name = format!("bell-{:x}", irq.irq);
113             let bell_node = vdev_node.subnode_mut(&bell_name)?;
114             bell_node.set_prop("vdevice-type", "doorbell")?;
115             let path_name = format!("/hypervisor/bell-{:x}", irq.irq);
116             bell_node.set_prop("generate", path_name)?;
117             bell_node.set_prop("label", irq.irq)?;
118             bell_node.set_prop("peer-default", ())?;
119             bell_node.set_prop("source-can-clear", ())?;
120 
121             let interrupt_type = if irq.level {
122                 IRQ_TYPE_LEVEL_HIGH
123             } else {
124                 IRQ_TYPE_EDGE_RISING
125             };
126             let interrupts = [GIC_FDT_IRQ_TYPE_SPI, irq.irq, interrupt_type];
127             bell_node.set_prop("interrupts", &interrupts)?;
128         }
129 
130         let mut base_set = false;
131         for region in self.guest_mem.regions() {
132             let create_shm_node = match region.options.purpose {
133                 MemoryRegionPurpose::GuestMemoryRegion => {
134                     // Assume first GuestMemoryRegion contains the payload
135                     // This memory region is described by the "base-address" property
136                     // and doesn't get re-described as a separate shm node.
137                     let ret = base_set;
138                     base_set = true;
139                     ret
140                 }
141                 // Described by the "firmware-address" property
142                 MemoryRegionPurpose::ProtectedFirmwareRegion => false,
143                 MemoryRegionPurpose::StaticSwiotlbRegion => true,
144             };
145 
146             if create_shm_node {
147                 fdt_create_shm_device(
148                     vdev_node,
149                     region.index.try_into().unwrap(),
150                     region.guest_addr,
151                 )?;
152             }
153         }
154 
155         Ok(())
156     }
157 
init_arch( &self, payload_entry_address: GuestAddress, fdt_address: GuestAddress, fdt_size: usize, ) -> Result<()>158     fn init_arch(
159         &self,
160         payload_entry_address: GuestAddress,
161         fdt_address: GuestAddress,
162         fdt_size: usize,
163     ) -> Result<()> {
164         // Gunyah initializes the PC to be the payload entry (except for protected VMs)
165         // and assumes that the image is loaded at the beginning of the "primary"
166         // memory parcel (region). This parcel contains both DTB and kernel Image, so
167         // make sure that DTB and payload are in the same memory region and that
168         // payload is at the start of that region.
169 
170         let (dtb_mapping, _, dtb_obj_offset) = self
171             .guest_mem
172             .find_region(fdt_address)
173             .map_err(|_| Error::new(ENOENT))?;
174         let (payload_mapping, payload_offset, payload_obj_offset) = self
175             .guest_mem
176             .find_region(payload_entry_address)
177             .map_err(|_| Error::new(ENOENT))?;
178 
179         if !std::ptr::eq(dtb_mapping, payload_mapping) || dtb_obj_offset != payload_obj_offset {
180             panic!("DTB and payload are not part of same memory region.");
181         }
182 
183         if payload_offset != 0 {
184             panic!("Payload offset must be zero");
185         }
186 
187         self.set_dtb_config(fdt_address, fdt_size)?;
188 
189         self.start()?;
190 
191         Ok(())
192     }
193 }
194 
195 impl VcpuAArch64 for GunyahVcpu {
init(&self, _features: &[crate::VcpuFeature]) -> Result<()>196     fn init(&self, _features: &[crate::VcpuFeature]) -> Result<()> {
197         Ok(())
198     }
199 
init_pmu(&self, _irq: u64) -> Result<()>200     fn init_pmu(&self, _irq: u64) -> Result<()> {
201         Err(Error::new(ENOTSUP))
202     }
203 
has_pvtime_support(&self) -> bool204     fn has_pvtime_support(&self) -> bool {
205         false
206     }
207 
init_pvtime(&self, _pvtime_ipa: u64) -> Result<()>208     fn init_pvtime(&self, _pvtime_ipa: u64) -> Result<()> {
209         Err(Error::new(ENOTSUP))
210     }
211 
set_one_reg(&self, _reg_id: VcpuRegAArch64, _data: u64) -> Result<()>212     fn set_one_reg(&self, _reg_id: VcpuRegAArch64, _data: u64) -> Result<()> {
213         unimplemented!()
214     }
215 
get_one_reg(&self, _reg_id: VcpuRegAArch64) -> Result<u64>216     fn get_one_reg(&self, _reg_id: VcpuRegAArch64) -> Result<u64> {
217         Err(Error::new(ENOTSUP))
218     }
219 
set_vector_reg(&self, _reg_num: u8, _data: u128) -> Result<()>220     fn set_vector_reg(&self, _reg_num: u8, _data: u128) -> Result<()> {
221         unimplemented!()
222     }
223 
get_vector_reg(&self, _reg_num: u8) -> Result<u128>224     fn get_vector_reg(&self, _reg_num: u8) -> Result<u128> {
225         unimplemented!()
226     }
227 
get_psci_version(&self) -> Result<PsciVersion>228     fn get_psci_version(&self) -> Result<PsciVersion> {
229         Ok(PSCI_0_2)
230     }
231 
232     #[cfg(feature = "gdb")]
set_guest_debug(&self, _addrs: &[GuestAddress], _enable_singlestep: bool) -> Result<()>233     fn set_guest_debug(&self, _addrs: &[GuestAddress], _enable_singlestep: bool) -> Result<()> {
234         Err(Error::new(ENOTSUP))
235     }
236 
237     #[cfg(feature = "gdb")]
set_gdb_registers( &self, _regs: &<gdbstub_arch::aarch64::AArch64 as gdbstub::arch::Arch>::Registers, ) -> Result<()>238     fn set_gdb_registers(
239         &self,
240         _regs: &<gdbstub_arch::aarch64::AArch64 as gdbstub::arch::Arch>::Registers,
241     ) -> Result<()> {
242         Err(Error::new(ENOTSUP))
243     }
244 
245     #[cfg(feature = "gdb")]
get_gdb_registers( &self, _regs: &mut <gdbstub_arch::aarch64::AArch64 as gdbstub::arch::Arch>::Registers, ) -> Result<()>246     fn get_gdb_registers(
247         &self,
248         _regs: &mut <gdbstub_arch::aarch64::AArch64 as gdbstub::arch::Arch>::Registers,
249     ) -> Result<()> {
250         Err(Error::new(ENOTSUP))
251     }
252 
253     #[cfg(feature = "gdb")]
get_max_hw_bps(&self) -> Result<usize>254     fn get_max_hw_bps(&self) -> Result<usize> {
255         Err(Error::new(ENOTSUP))
256     }
257 
258     #[cfg(feature = "gdb")]
set_gdb_register( &self, _reg: <gdbstub_arch::aarch64::AArch64 as gdbstub::arch::Arch>::RegId, _data: &[u8], ) -> Result<()>259     fn set_gdb_register(
260         &self,
261         _reg: <gdbstub_arch::aarch64::AArch64 as gdbstub::arch::Arch>::RegId,
262         _data: &[u8],
263     ) -> Result<()> {
264         Err(Error::new(ENOTSUP))
265     }
266 
267     #[cfg(feature = "gdb")]
get_gdb_register( &self, _reg: <gdbstub_arch::aarch64::AArch64 as gdbstub::arch::Arch>::RegId, _data: &mut [u8], ) -> Result<usize>268     fn get_gdb_register(
269         &self,
270         _reg: <gdbstub_arch::aarch64::AArch64 as gdbstub::arch::Arch>::RegId,
271         _data: &mut [u8],
272     ) -> Result<usize> {
273         Err(Error::new(ENOTSUP))
274     }
275 }
276