• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Google Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #![deny(unsafe_op_in_unsafe_fn)]
25 use core::ffi::c_int;
26 use core::ptr;
27 
28 use log::debug;
29 
30 use virtio_drivers_and_devices::device::socket::VirtIOSocket;
31 use virtio_drivers_and_devices::transport::pci::bus::Cam;
32 use virtio_drivers_and_devices::transport::pci::bus::Command;
33 use virtio_drivers_and_devices::transport::pci::bus::ConfigurationAccess;
34 use virtio_drivers_and_devices::transport::pci::bus::MmioCam;
35 use virtio_drivers_and_devices::transport::pci::bus::PciRoot;
36 use virtio_drivers_and_devices::transport::pci::virtio_device_type;
37 use virtio_drivers_and_devices::transport::pci::PciTransport;
38 use virtio_drivers_and_devices::transport::SomeTransport;
39 #[cfg(target_arch = "x86_64")]
40 use {
41     hypervisor_backends::get_mem_sharer,
42     virtio_drivers_and_devices::transport::x86_64::{HypCam, HypPciTransport},
43 };
44 
45 use virtio_drivers_and_devices::transport::DeviceType;
46 
47 use hypervisor::mmio_map_region;
48 
49 use rust_support::mmu::ARCH_MMU_FLAG_PERM_NO_EXECUTE;
50 use rust_support::mmu::ARCH_MMU_FLAG_UNCACHED_DEVICE;
51 use rust_support::paddr_t;
52 use rust_support::vmm::vmm_alloc_physical;
53 use rust_support::vmm::vmm_get_kernel_aspace;
54 use rust_support::Error as LkError;
55 
56 use crate::err::Error;
57 use crate::vsock::vsock_init;
58 use hal::TrustyHal;
59 
60 #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
61 mod arch;
62 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
63 #[path = "pci/unimplemented.rs"]
64 mod arch;
65 mod hal;
66 
67 impl TrustyHal {
init_all_vsocks( mut pci_root: PciRoot<impl ConfigurationAccess>, pci_size: usize, use_hyp_transport: bool, ) -> Result<(), Error>68     fn init_all_vsocks(
69         mut pci_root: PciRoot<impl ConfigurationAccess>,
70         pci_size: usize,
71         use_hyp_transport: bool,
72     ) -> Result<(), Error> {
73         for bus in u8::MIN..=u8::MAX {
74             // each bus can use up to one megabyte of address space, make sure we stay in range
75             if bus as usize * 0x100000 >= pci_size {
76                 break;
77             }
78             for (device_function, info) in pci_root.enumerate_bus(bus) {
79                 if virtio_device_type(&info) != Some(DeviceType::Socket) {
80                     continue;
81                 };
82 
83                 // Map the BARs of the device into virtual memory. Since the mappings must
84                 // outlive the `PciTransport` constructed in `vsock_init` we no make no
85                 // attempt to deallocate them.
86                 Self::mmio_alloc(&mut pci_root, device_function)?;
87 
88                 // Enable the device to use its BARs.
89                 pci_root.set_command(
90                     device_function,
91                     Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER,
92                 );
93 
94                 // In contrast to arm64, when Trusty runs as a protected VM on x86_64, emulated
95                 // MMIO accesses require special handling. On x86_64, the host needs to read and
96                 // decode guest instructions that caused the MMIO access trap to determine the
97                 // address and access type. However, due to pKVM nature, the host cannot read
98                 // protected VM memory. To address this, pKVM supports hypercalls for IOREAD and
99                 // IOWRITE, which the guest can use.
100                 //
101                 // The virtio-drivers' HypPciTransport is based on mentioned hypercalls and
102                 // therefore is used for x86 protected Trusty, while PciTransport is used
103                 // otherwise.
104                 let transport = if use_hyp_transport {
105                     #[cfg(target_arch = "x86_64")]
106                     {
107                         SomeTransport::HypPci(HypPciTransport::new::<_>(
108                             &mut pci_root,
109                             device_function,
110                         )?)
111                     }
112 
113                     #[cfg(not(target_arch = "x86_64"))]
114                     panic!("HypPciTransport is x86_64 specific");
115                 } else {
116                     SomeTransport::Pci(PciTransport::new::<Self, _>(
117                         &mut pci_root,
118                         device_function,
119                     )?)
120                 };
121 
122                 let driver: VirtIOSocket<TrustyHal, SomeTransport, 4096> =
123                     VirtIOSocket::new(transport)?;
124                 vsock_init(driver)?;
125             }
126         }
127         Ok(())
128     }
129 }
130 
131 /// # Safety
132 ///
133 /// `pci_paddr` must be a valid physical address with `'static` lifetime to the base of the MMIO region,
134 /// which must have a size of `pci_size`.
map_pci_root_and_init_vsock( pci_paddr: paddr_t, pci_size: usize, cfg_size: usize, ) -> Result<(), Error>135 unsafe fn map_pci_root_and_init_vsock(
136     pci_paddr: paddr_t,
137     pci_size: usize,
138     cfg_size: usize,
139 ) -> Result<(), Error> {
140     // The ECAM is defined in Section 7.2.2 of the PCI Express Base Specification, Revision 2.0.
141     // The ECAM size must be a power of two with the exponent between 1 and 8.
142     let cam = match cfg_size / /* device functions */ 8 {
143         256 => Cam::MmioCam,
144         4096 => Cam::Ecam,
145         _ => return Err(LkError::ERR_BAD_LEN.into()),
146     };
147 
148     if !pci_size.is_power_of_two() || pci_size > cam.size() as usize {
149         return Err(LkError::ERR_BAD_LEN.into());
150     }
151     // The ECAM base must be 2^(n + 20)-bit aligned.
152     if cam == Cam::Ecam && pci_paddr & (pci_size - 1) != 0 {
153         return Err(LkError::ERR_INVALID_ARGS.into());
154     }
155 
156     // Map the PCI configuration space.
157     let pci_vaddr = ptr::null_mut();
158     // Safety:
159     // `aspace` is `vmm_get_kernel_aspace()`.
160     // `name` is a `&'static CStr`.
161     // `pci_paddr` and `pci_size` are safe by this function's safety requirements.
162     let e = unsafe {
163         vmm_alloc_physical(
164             vmm_get_kernel_aspace(),
165             c"pci_config_space".as_ptr(),
166             pci_size,
167             &pci_vaddr,
168             0,
169             pci_paddr,
170             0,
171             ARCH_MMU_FLAG_PERM_NO_EXECUTE | ARCH_MMU_FLAG_UNCACHED_DEVICE,
172         )
173     };
174     LkError::from_lk(e)?;
175 
176     // Safety:
177     // `pci_paddr` and `pci_size` are safe by this function's safety requirements.
178     match unsafe { mmio_map_region(pci_paddr, pci_size) } {
179         // Ignore not supported which implies that guard is not used.
180         Ok(()) | Err(LkError::ERR_NOT_SUPPORTED) | Err(LkError::ERR_INVALID_ARGS) => {}
181         Err(err) => {
182             log::error!("mmio_map_region returned unexpected error: {:?}", err);
183             return Err(Error::Lk(err));
184         }
185     }
186 
187     #[cfg(not(target_arch = "x86_64"))]
188     let use_hyp_transport = false;
189 
190     // x86 when running in protected mode requires hyp transport
191     #[cfg(target_arch = "x86_64")]
192     let use_hyp_transport = get_mem_sharer().is_some();
193 
194     if use_hyp_transport {
195         #[cfg(target_arch = "x86_64")]
196         {
197             let pci_root = PciRoot::new(HypCam::new(pci_paddr, cam));
198             TrustyHal::init_all_vsocks(pci_root, pci_size, use_hyp_transport)?;
199         }
200     } else {
201         // Safety:
202         // `pci_paddr` is a valid physical address to the base of the MMIO region.
203         // `pci_vaddr` is the mapped virtual address of that.
204         // `pci_paddr` has `'static` lifetime, and `pci_vaddr` is never unmapped,
205         // so it, too, has `'static` lifetime.
206         // We also check that the `cam` size is valid.
207         let pci_root = PciRoot::new(unsafe { MmioCam::new(pci_vaddr.cast(), cam) });
208         TrustyHal::init_all_vsocks(pci_root, pci_size, use_hyp_transport)?;
209     }
210     Ok(())
211 }
212 
213 /// # Safety
214 ///
215 /// See [`map_pci_root_and_init_vsock`].
216 #[no_mangle]
pci_init_mmio( pci_paddr: paddr_t, pci_size: usize, cfg_size: usize, ) -> c_int217 pub unsafe extern "C" fn pci_init_mmio(
218     pci_paddr: paddr_t,
219     pci_size: usize,
220     cfg_size: usize,
221 ) -> c_int {
222     debug!("initializing vsock: pci_paddr 0x{pci_paddr:x}, pci_size 0x{pci_size:x}");
223     || -> Result<(), Error> {
224         // Safety: Delegated to `map_pci_root_and_init_vsock`.
225         unsafe { map_pci_root_and_init_vsock(pci_paddr, pci_size, cfg_size) }?;
226         Ok(())
227     }()
228     .err()
229     .unwrap_or(LkError::NO_ERROR.into())
230     .into_c()
231 }
232