Skip to main content

axvm/
config.rs

1// Copyright 2025 The Axvisor Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The configuration structure for the VM.
16//! The `AxVMCrateConfig` is generated from toml file, and then converted to `AxVMConfig` for the VM creation.
17
18use alloc::{string::String, vec::Vec};
19
20use axaddrspace::GuestPhysAddr;
21pub use axvmconfig::{
22    AxVMCrateConfig, EmulatedDeviceConfig, PassThroughAddressConfig, PassThroughDeviceConfig,
23    VMInterruptMode, VMType, VmMemConfig, VmMemMappingType,
24};
25
26use crate::VMMemoryRegion;
27
28const BIOS_RESERVED_SIZE: usize = 2 * 1024 * 1024;
29
30// /// A part of `AxVCpuConfig`, which represents an architecture-dependent `VCpu`.
31// ///
32// /// The concrete type of configuration is defined in `AxArchVCpuImpl`.
33// #[derive(Clone, Copy, Debug, Default)]
34// pub struct AxArchVCpuConfig<H: AxVMHal> {
35//     pub create_config: <AxArchVCpuImpl<H> as AxArchVCpu>::CreateConfig,
36//     pub setup_config: <AxArchVCpuImpl<H> as AxArchVCpu>::SetupConfig,
37// }
38/// A part of `AxVMConfig`, which represents a `VCpu`.
39#[derive(Clone, Copy, Debug, Default)]
40pub struct AxVCpuConfig {
41    // pub arch_config: AxArchVCpuConfig,
42    /// The entry address in GPA for the Bootstrap Processor (BSP).
43    pub bsp_entry: GuestPhysAddr,
44    /// The entry address in GPA for the Application Processor (AP).
45    pub ap_entry: GuestPhysAddr,
46}
47
48/// Ramdisk image information.
49#[derive(Debug, Default, Clone)]
50pub struct RamdiskInfo {
51    /// The load address in GPA for the ramdisk image.
52    pub load_gpa: GuestPhysAddr,
53    /// The size in bytes of the ramdisk image, `None` if not known yet.
54    pub size: Option<usize>,
55}
56
57/// A part of `AxVMConfig`, which stores configuration attributes related to the load address of VM images.
58#[derive(Debug, Default, Clone)]
59pub struct VMImageConfig {
60    /// The load address in GPA for the kernel image.
61    pub kernel_load_gpa: GuestPhysAddr,
62    /// The load address in GPA for the BIOS image, `None` if not used.
63    pub bios_load_gpa: Option<GuestPhysAddr>,
64    /// The load address in GPA for the device tree blob (DTB), `None` if not used.
65    pub dtb_load_gpa: Option<GuestPhysAddr>,
66    /// Ramdisk image info, `None` if not used.
67    pub ramdisk: Option<RamdiskInfo>,
68}
69
70/// A part of `AxVMCrateConfig`, which represents a `VM`.
71#[derive(Debug, Default)]
72pub struct AxVMConfig {
73    id: usize,
74    name: String,
75    #[allow(dead_code)]
76    vm_type: VMType,
77    pub(crate) phys_cpu_ls: PhysCpuList,
78    /// vCPU configuration.
79    pub cpu_config: AxVCpuConfig,
80    /// VM image configuration.
81    pub image_config: VMImageConfig,
82    emu_devices: Vec<EmulatedDeviceConfig>,
83    pass_through_devices: Vec<PassThroughDeviceConfig>,
84    excluded_devices: Vec<Vec<String>>,
85    pass_through_addresses: Vec<PassThroughAddressConfig>,
86    // TODO: improve interrupt passthrough
87    spi_list: Vec<u32>,
88    interrupt_mode: VMInterruptMode,
89}
90
91impl From<AxVMCrateConfig> for AxVMConfig {
92    fn from(cfg: AxVMCrateConfig) -> Self {
93        Self {
94            id: cfg.base.id,
95            name: cfg.base.name,
96            vm_type: VMType::from(cfg.base.vm_type),
97            phys_cpu_ls: PhysCpuList {
98                cpu_num: cfg.base.cpu_num,
99                phys_cpu_ids: cfg.base.phys_cpu_ids,
100                phys_cpu_sets: cfg.base.phys_cpu_sets,
101            },
102            cpu_config: AxVCpuConfig {
103                bsp_entry: GuestPhysAddr::from(cfg.kernel.entry_point),
104                ap_entry: GuestPhysAddr::from(cfg.kernel.entry_point),
105            },
106            image_config: VMImageConfig {
107                kernel_load_gpa: GuestPhysAddr::from(cfg.kernel.kernel_load_addr),
108                bios_load_gpa: cfg.kernel.bios_load_addr.map(GuestPhysAddr::from),
109                dtb_load_gpa: cfg.kernel.dtb_load_addr.map(GuestPhysAddr::from),
110                ramdisk: cfg.kernel.ramdisk_load_addr.map(|addr| RamdiskInfo {
111                    load_gpa: GuestPhysAddr::from(addr),
112                    size: None,
113                }),
114            },
115            // memory_regions: cfg.kernel.memory_regions,
116            emu_devices: cfg.devices.emu_devices,
117            pass_through_devices: cfg.devices.passthrough_devices,
118            excluded_devices: cfg.devices.excluded_devices,
119            pass_through_addresses: cfg.devices.passthrough_addresses,
120            spi_list: Vec::new(),
121            interrupt_mode: cfg.devices.interrupt_mode,
122        }
123    }
124}
125
126pub fn adjusted_kernel_load_gpa(
127    main_memory: &VMMemoryRegion,
128    bios_load_gpa: Option<GuestPhysAddr>,
129) -> Option<GuestPhysAddr> {
130    if !main_memory.is_identical() {
131        return None;
132    }
133
134    let mut kernel_addr = main_memory.gpa;
135    if bios_load_gpa.is_some() {
136        kernel_addr += BIOS_RESERVED_SIZE;
137    }
138    Some(kernel_addr)
139}
140
141impl AxVMConfig {
142    /// Returns VM id.
143    pub fn id(&self) -> usize {
144        self.id
145    }
146
147    /// Returns VM name.
148    pub fn name(&self) -> String {
149        self.name.clone()
150    }
151
152    /// Returns configurations related to VM image load addresses.
153    pub fn image_config(&self) -> &VMImageConfig {
154        &self.image_config
155    }
156
157    /// Returns the entry address in GPA for the Bootstrap Processor (BSP).
158    pub fn bsp_entry(&self) -> GuestPhysAddr {
159        // Retrieves BSP entry from the CPU configuration.
160        self.cpu_config.bsp_entry
161    }
162
163    /// Returns the entry address in GPA for the Application Processor (AP).
164    pub fn ap_entry(&self) -> GuestPhysAddr {
165        // Retrieves AP entry from the CPU configuration.
166        self.cpu_config.ap_entry
167    }
168
169    /// Returns a mutable reference to the physical CPU list.
170    pub fn phys_cpu_ls_mut(&mut self) -> &mut PhysCpuList {
171        &mut self.phys_cpu_ls
172    }
173
174    /// Returns the list of excluded devices.
175    pub fn excluded_devices(&self) -> &Vec<Vec<String>> {
176        &self.excluded_devices
177    }
178
179    /// Returns the list of passthrough address configurations.
180    pub fn pass_through_addresses(&self) -> &Vec<PassThroughAddressConfig> {
181        &self.pass_through_addresses
182    }
183    // /// Returns configurations related to VM memory regions.
184    // pub fn memory_regions(&self) -> Vec<VmMemConfig> {
185    //     &self.memory_regions
186    // }
187
188    // /// Adds a new memory region to the VM configuration.
189    // pub fn add_memory_region(&mut self, region: VmMemConfig) {
190    //     self.memory_regions.push(region);
191    // }
192
193    // /// Checks if the VM memory regions contain a specific range.
194    // pub fn contains_memory_range(&self, range: &Range<usize>) -> bool {
195    //     self.memory_regions
196    //         .iter()
197    //         .any(|region| region.gpa <= range.start && region.gpa + region.size >= range.end)
198    // }
199
200    /// Returns configurations related to VM emulated devices.
201    pub fn emu_devices(&self) -> &Vec<EmulatedDeviceConfig> {
202        &self.emu_devices
203    }
204
205    /// Returns configurations related to VM passthrough devices.
206    pub fn pass_through_devices(&self) -> &Vec<PassThroughDeviceConfig> {
207        &self.pass_through_devices
208    }
209
210    /// Adds a new passthrough device to the VM configuration.
211    pub fn add_pass_through_device(&mut self, device: PassThroughDeviceConfig) {
212        self.pass_through_devices.push(device);
213    }
214
215    /// Removes passthrough device from the VM configuration.
216    pub fn remove_pass_through_device(&mut self, device: PassThroughDeviceConfig) {
217        self.pass_through_devices.retain(|d| d == &device);
218    }
219
220    /// Clears all passthrough devices from the VM configuration.
221    pub fn clear_pass_through_devices(&mut self) {
222        self.pass_through_devices.clear();
223    }
224
225    /// Adds a passthrough SPI to the VM configuration.
226    pub fn add_pass_through_spi(&mut self, spi: u32) {
227        self.spi_list.push(spi);
228    }
229
230    /// Returns the list of passthrough SPIs.
231    pub fn pass_through_spis(&self) -> &Vec<u32> {
232        &self.spi_list
233    }
234
235    /// Returns the interrupt mode of the VM.
236    pub fn interrupt_mode(&self) -> VMInterruptMode {
237        self.interrupt_mode
238    }
239
240    /// Relocate the guest kernel image while preserving the configured
241    /// entry-point offsets relative to the load address.
242    pub fn relocate_kernel_image(&mut self, kernel_load_gpa: GuestPhysAddr) {
243        let old_load = self.image_config.kernel_load_gpa.as_usize();
244        let new_load = kernel_load_gpa.as_usize();
245
246        let bsp_offset = self
247            .cpu_config
248            .bsp_entry
249            .as_usize()
250            .checked_sub(old_load)
251            .expect("BSP entry must not be below kernel load address");
252        let ap_offset = self
253            .cpu_config
254            .ap_entry
255            .as_usize()
256            .checked_sub(old_load)
257            .expect("AP entry must not be below kernel load address");
258
259        self.image_config.kernel_load_gpa = kernel_load_gpa;
260        self.cpu_config.bsp_entry = GuestPhysAddr::from(new_load + bsp_offset);
261        self.cpu_config.ap_entry = GuestPhysAddr::from(new_load + ap_offset);
262    }
263}
264
265/// Represents the list of physical CPUs available for the VM.
266#[derive(Debug, Default, Clone)]
267pub struct PhysCpuList {
268    cpu_num: usize,
269    phys_cpu_ids: Option<Vec<usize>>,
270    phys_cpu_sets: Option<Vec<usize>>,
271}
272
273impl PhysCpuList {
274    /// Returns vCpu id list and its corresponding pCpu affinity list, as well as its physical id.
275    /// If the pCpu affinity is None, it means the vCpu will be allocated to any available pCpu randomly.
276    /// if the pCPU id is not provided, the vCpu's physical id will be set as vCpu id.
277    ///
278    /// Returns a vector of tuples, each tuple contains:
279    /// - The vCpu id.
280    /// - The pCpu affinity mask, `None` if not set.
281    /// - The physical id of the vCpu, equal to vCpu id if not provided.
282    pub fn get_vcpu_affinities_pcpu_ids(&self) -> Vec<(usize, Option<usize>, usize)> {
283        let mut vcpu_pcpu_tuples = Vec::new();
284        #[cfg(target_arch = "riscv64")]
285        let mut pcpu_mask_flag = false;
286
287        if let Some(phys_cpu_ids) = &self.phys_cpu_ids
288            && self.cpu_num != phys_cpu_ids.len()
289        {
290            error!(
291                "ERROR!!!: cpu_num: {}, phys_cpu_ids: {:?}",
292                self.cpu_num, self.phys_cpu_ids
293            );
294        }
295
296        for vcpu_id in 0..self.cpu_num {
297            vcpu_pcpu_tuples.push((vcpu_id, None, vcpu_id));
298        }
299
300        #[cfg(target_arch = "riscv64")]
301        if let Some(phys_cpu_sets) = &self.phys_cpu_sets {
302            pcpu_mask_flag = true;
303            for (vcpu_id, pcpu_mask_bitmap) in phys_cpu_sets.iter().enumerate() {
304                vcpu_pcpu_tuples[vcpu_id].1 = Some(*pcpu_mask_bitmap);
305            }
306        }
307
308        #[cfg(not(target_arch = "riscv64"))]
309        if let Some(phys_cpu_sets) = &self.phys_cpu_sets {
310            for (vcpu_id, pcpu_mask_bitmap) in phys_cpu_sets.iter().enumerate() {
311                vcpu_pcpu_tuples[vcpu_id].1 = Some(*pcpu_mask_bitmap);
312            }
313        }
314
315        if let Some(phys_cpu_ids) = &self.phys_cpu_ids {
316            for (vcpu_id, phys_id) in phys_cpu_ids.iter().enumerate() {
317                vcpu_pcpu_tuples[vcpu_id].2 = *phys_id;
318                #[cfg(target_arch = "riscv64")]
319                {
320                    if !pcpu_mask_flag {
321                        // if don't assign pcpu mask yet, assign it manually
322                        vcpu_pcpu_tuples[vcpu_id].1 = Some(1 << (*phys_id));
323                    }
324                }
325            }
326        }
327        vcpu_pcpu_tuples
328    }
329
330    /// Returns the number of CPUs.
331    pub fn cpu_num(&self) -> usize {
332        self.cpu_num
333    }
334
335    /// Returns the physical CPU IDs.
336    pub fn phys_cpu_ids(&self) -> &Option<Vec<usize>> {
337        &self.phys_cpu_ids
338    }
339
340    /// Returns the physical CPU sets.
341    pub fn phys_cpu_sets(&self) -> &Option<Vec<usize>> {
342        &self.phys_cpu_sets
343    }
344
345    /// Sets the guest CPU sets.
346    pub fn set_guest_cpu_sets(&mut self, phys_cpu_sets: Vec<usize>) {
347        self.phys_cpu_sets = Some(phys_cpu_sets);
348    }
349}