xref: /cloud-hypervisor/vm-allocator/src/system.rs (revision 6f8bd27cf7629733582d930519e98d19e90afb16)
1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Copyright © 2019 Intel Corporation
3 //
4 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5 // Use of this source code is governed by a BSD-style license that can be
6 // found in the LICENSE-BSD-3-Clause file.
7 //
8 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
9 
10 use vm_memory::{GuestAddress, GuestUsize};
11 
12 use crate::address::AddressAllocator;
13 use crate::gsi::GsiAllocator;
14 #[cfg(target_arch = "x86_64")]
15 use crate::gsi::GsiApic;
16 
17 use libc::{sysconf, _SC_PAGESIZE};
18 
19 /// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
20 #[inline(always)]
21 fn pagesize() -> usize {
22     // SAFETY: FFI call. Trivially safe.
23     unsafe { sysconf(_SC_PAGESIZE) as usize }
24 }
25 
26 /// Manages allocating system resources such as address space and interrupt numbers.
27 ///
28 /// # Example - Use the `SystemAddress` builder.
29 ///
30 /// ```
31 /// # #[cfg(target_arch = "x86_64")]
32 /// # use vm_allocator::{GsiApic, SystemAllocator};
33 /// # #[cfg(target_arch = "aarch64")]
34 /// # use vm_allocator::SystemAllocator;
35 /// # use vm_memory::{Address, GuestAddress, GuestUsize};
36 ///   let mut allocator = SystemAllocator::new(
37 ///           #[cfg(target_arch = "x86_64")] GuestAddress(0x1000),
38 ///           #[cfg(target_arch = "x86_64")] 0x10000,
39 ///           GuestAddress(0x10000000), 0x10000000,
40 ///           GuestAddress(0x20000000), 0x100000,
41 ///           #[cfg(target_arch = "x86_64")] vec![GsiApic::new(5, 19)]).unwrap();
42 ///   #[cfg(target_arch = "x86_64")]
43 ///   assert_eq!(allocator.allocate_irq(), Some(5));
44 ///   #[cfg(target_arch = "aarch64")]
45 ///   assert_eq!(allocator.allocate_irq(), Some(32));
46 ///   #[cfg(target_arch = "x86_64")]
47 ///   assert_eq!(allocator.allocate_irq(), Some(6));
48 ///   #[cfg(target_arch = "aarch64")]
49 ///   assert_eq!(allocator.allocate_irq(), Some(33));
50 ///   assert_eq!(allocator.allocate_platform_mmio_addresses(None, 0x1000, Some(0x1000)), Some(GuestAddress(0x1fff_f000)));
51 ///
52 /// ```
53 pub struct SystemAllocator {
54     #[cfg(target_arch = "x86_64")]
55     io_address_space: AddressAllocator,
56     platform_mmio_address_space: AddressAllocator,
57     mmio_hole_address_space: AddressAllocator,
58     gsi_allocator: GsiAllocator,
59 }
60 
61 impl SystemAllocator {
62     /// Creates a new `SystemAllocator` for managing addresses and irq numvers.
63     /// Can return `None` if `base` + `size` overflows a u64
64     ///
65     /// * `io_base` - (X86) The starting address of IO memory.
66     /// * `io_size` - (X86) The size of IO memory.
67     /// * `platform_mmio_base` - The starting address of platform MMIO memory.
68     /// * `platform_mmio_size` - The size of platform MMIO memory.
69     /// * `mmio_hole_base` - The starting address of MMIO memory in 32-bit address space.
70     /// * `mmio_hole_size` - The size of MMIO memory in 32-bit address space.
71     /// * `apics` - (X86) Vector of APIC's.
72     ///
73     pub fn new(
74         #[cfg(target_arch = "x86_64")] io_base: GuestAddress,
75         #[cfg(target_arch = "x86_64")] io_size: GuestUsize,
76         platform_mmio_base: GuestAddress,
77         platform_mmio_size: GuestUsize,
78         mmio_hole_base: GuestAddress,
79         mmio_hole_size: GuestUsize,
80         #[cfg(target_arch = "x86_64")] apics: Vec<GsiApic>,
81     ) -> Option<Self> {
82         Some(SystemAllocator {
83             #[cfg(target_arch = "x86_64")]
84             io_address_space: AddressAllocator::new(io_base, io_size)?,
85             platform_mmio_address_space: AddressAllocator::new(
86                 platform_mmio_base,
87                 platform_mmio_size,
88             )?,
89             mmio_hole_address_space: AddressAllocator::new(mmio_hole_base, mmio_hole_size)?,
90             #[cfg(target_arch = "x86_64")]
91             gsi_allocator: GsiAllocator::new(apics),
92             #[cfg(target_arch = "aarch64")]
93             gsi_allocator: GsiAllocator::new(),
94         })
95     }
96 
97     /// Reserves the next available system irq number.
98     pub fn allocate_irq(&mut self) -> Option<u32> {
99         self.gsi_allocator.allocate_irq().ok()
100     }
101 
102     /// Reserves the next available GSI.
103     pub fn allocate_gsi(&mut self) -> Option<u32> {
104         self.gsi_allocator.allocate_gsi().ok()
105     }
106 
107     #[cfg(target_arch = "x86_64")]
108     /// Reserves a section of `size` bytes of IO address space.
109     pub fn allocate_io_addresses(
110         &mut self,
111         address: Option<GuestAddress>,
112         size: GuestUsize,
113         align_size: Option<GuestUsize>,
114     ) -> Option<GuestAddress> {
115         self.io_address_space
116             .allocate(address, size, Some(align_size.unwrap_or(0x1)))
117     }
118 
119     /// Reserves a section of `size` bytes of platform MMIO address space.
120     pub fn allocate_platform_mmio_addresses(
121         &mut self,
122         address: Option<GuestAddress>,
123         size: GuestUsize,
124         align_size: Option<GuestUsize>,
125     ) -> Option<GuestAddress> {
126         self.platform_mmio_address_space.allocate(
127             address,
128             size,
129             Some(align_size.unwrap_or(pagesize() as u64)),
130         )
131     }
132 
133     /// Reserves a section of `size` bytes of MMIO address space.
134     pub fn allocate_mmio_hole_addresses(
135         &mut self,
136         address: Option<GuestAddress>,
137         size: GuestUsize,
138         align_size: Option<GuestUsize>,
139     ) -> Option<GuestAddress> {
140         self.mmio_hole_address_space.allocate(
141             address,
142             size,
143             Some(align_size.unwrap_or(pagesize() as u64)),
144         )
145     }
146 
147     #[cfg(target_arch = "x86_64")]
148     /// Free an IO address range.
149     /// We can only free a range if it matches exactly an already allocated range.
150     pub fn free_io_addresses(&mut self, address: GuestAddress, size: GuestUsize) {
151         self.io_address_space.free(address, size)
152     }
153 
154     /// Free a platform MMIO address range.
155     /// We can only free a range if it matches exactly an already allocated range.
156     pub fn free_platform_mmio_addresses(&mut self, address: GuestAddress, size: GuestUsize) {
157         self.platform_mmio_address_space.free(address, size)
158     }
159 
160     /// Free an MMIO address range from the 32 bits hole.
161     /// We can only free a range if it matches exactly an already allocated range.
162     pub fn free_mmio_hole_addresses(&mut self, address: GuestAddress, size: GuestUsize) {
163         self.mmio_hole_address_space.free(address, size)
164     }
165 }
166