veridian_kernel/virt/hypervisor/
passthrough.rs1#[cfg(feature = "alloc")]
7use alloc::vec::Vec;
8
9use super::MAX_MSIX_VECTORS;
10use crate::virt::VmError;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum PassthroughDeviceType {
19 VirtioNet,
21 VirtioBlk,
23 VirtioGpu,
25 VirtioSound,
27 GenericPci,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
33pub struct MsixRemap {
34 pub host_vector: u16,
36 pub guest_vector: u16,
38 pub target_vcpu: u8,
40 pub active: bool,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct MmioRegion {
47 pub host_phys: u64,
49 pub guest_phys: u64,
51 pub size: u64,
53 pub mapped: bool,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub struct PciBar {
60 pub index: u8,
62 pub address: u64,
64 pub size: u64,
66 pub is_memory: bool,
68 pub is_64bit: bool,
70 pub prefetchable: bool,
72}
73
74#[derive(Debug, Clone)]
76pub struct PciConfigPassthrough {
77 pub vendor_id: u16,
79 pub device_id: u16,
81 pub bdf: u32,
83 #[cfg(feature = "alloc")]
85 pub bars: Vec<PciBar>,
86 pub config_space: [u8; 256],
88 pub writable_mask: [u8; 256],
90}
91
92impl PciConfigPassthrough {
93 #[cfg(feature = "alloc")]
94 pub fn new(vendor_id: u16, device_id: u16, bdf: u32) -> Self {
95 let mut config_space = [0u8; 256];
96 config_space[0] = vendor_id as u8;
98 config_space[1] = (vendor_id >> 8) as u8;
99 config_space[2] = device_id as u8;
100 config_space[3] = (device_id >> 8) as u8;
101
102 let mut writable_mask = [0u8; 256];
103 writable_mask[4] = 0xFF;
105 writable_mask[5] = 0xFF;
106 for item in writable_mask.iter_mut().take(0x27 + 1).skip(0x10) {
108 *item = 0xFF;
109 }
110
111 Self {
112 vendor_id,
113 device_id,
114 bdf,
115 bars: Vec::new(),
116 config_space,
117 writable_mask,
118 }
119 }
120
121 pub fn read_config(&self, offset: u8) -> u8 {
123 self.config_space[offset as usize]
124 }
125
126 pub fn write_config(&mut self, offset: u8, value: u8) {
128 let mask = self.writable_mask[offset as usize];
129 let idx = offset as usize;
130 self.config_space[idx] = (self.config_space[idx] & !mask) | (value & mask);
131 }
132
133 pub fn read_config32(&self, offset: u8) -> u32 {
135 let idx = (offset & 0xFC) as usize;
136 u32::from_le_bytes([
137 self.config_space[idx],
138 self.config_space[idx + 1],
139 self.config_space[idx + 2],
140 self.config_space[idx + 3],
141 ])
142 }
143}
144
145#[cfg(feature = "alloc")]
147pub struct PassthroughDevice {
148 pub device_type: PassthroughDeviceType,
150 pub pci_config: PciConfigPassthrough,
152 pub mmio_regions: Vec<MmioRegion>,
154 pub msix_remaps: Vec<MsixRemap>,
156 pub assigned: bool,
158 pub owner_vm_id: u64,
160}
161
162#[cfg(feature = "alloc")]
163impl PassthroughDevice {
164 pub fn new(
165 device_type: PassthroughDeviceType,
166 vendor_id: u16,
167 device_id: u16,
168 bdf: u32,
169 ) -> Self {
170 Self {
171 device_type,
172 pci_config: PciConfigPassthrough::new(vendor_id, device_id, bdf),
173 mmio_regions: Vec::new(),
174 msix_remaps: Vec::new(),
175 assigned: false,
176 owner_vm_id: 0,
177 }
178 }
179
180 pub fn assign_to_vm(&mut self, vm_id: u64) -> Result<(), VmError> {
182 if self.assigned {
183 return Err(VmError::DeviceError);
184 }
185 self.assigned = true;
186 self.owner_vm_id = vm_id;
187 Ok(())
188 }
189
190 pub fn unassign(&mut self) {
192 self.assigned = false;
193 self.owner_vm_id = 0;
194 for remap in &mut self.msix_remaps {
196 remap.active = false;
197 }
198 for region in &mut self.mmio_regions {
200 region.mapped = false;
201 }
202 }
203
204 pub fn add_mmio_region(&mut self, host_phys: u64, guest_phys: u64, size: u64) {
206 self.mmio_regions.push(MmioRegion {
207 host_phys,
208 guest_phys,
209 size,
210 mapped: true,
211 });
212 }
213
214 pub fn add_msix_remap(&mut self, host_vector: u16, guest_vector: u16, target_vcpu: u8) {
216 if self.msix_remaps.len() < MAX_MSIX_VECTORS {
217 self.msix_remaps.push(MsixRemap {
218 host_vector,
219 guest_vector,
220 target_vcpu,
221 active: true,
222 });
223 }
224 }
225
226 pub fn remap_interrupt(&self, host_vector: u16) -> Option<(u16, u8)> {
228 for remap in &self.msix_remaps {
229 if remap.active && remap.host_vector == host_vector {
230 return Some((remap.guest_vector, remap.target_vcpu));
231 }
232 }
233 None
234 }
235
236 pub fn reset(&mut self) {
238 self.pci_config.config_space[4] = 0; self.pci_config.config_space[5] = 0;
240 for remap in &mut self.msix_remaps {
241 remap.active = false;
242 }
243 }
244
245 pub fn is_assigned(&self) -> bool {
246 self.assigned
247 }
248
249 pub fn mmio_region_count(&self) -> usize {
250 self.mmio_regions.len()
251 }
252
253 pub fn msix_remap_count(&self) -> usize {
254 self.msix_remaps.len()
255 }
256}