1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
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 base::pagesize;
6 
7 use crate::address_allocator::{AddressAllocator, AddressAllocatorSet};
8 use crate::{Alloc, Error, Result};
9 
10 /// Manages allocating system resources such as address space and interrupt numbers.
11 ///
12 /// # Example - Use the `SystemAddress` builder.
13 ///
14 /// ```
15 /// # use resources::{Alloc, MmioType, SystemAllocator};
16 ///   if let Ok(mut a) = SystemAllocator::builder()
17 ///           .add_io_addresses(0x1000, 0x10000)
18 ///           .add_high_mmio_addresses(0x10000000, 0x10000000)
19 ///           .add_low_mmio_addresses(0x30000000, 0x10000)
20 ///           .create_allocator(5) {
21 ///       assert_eq!(a.allocate_irq(), Some(5));
22 ///       assert_eq!(a.allocate_irq(), Some(6));
23 ///       assert_eq!(
24 ///           a.mmio_allocator(MmioType::High)
25 ///              .allocate(
26 ///                  0x100,
27 ///                  Alloc::PciBar { bus: 0, dev: 0, func: 0, bar: 0 },
28 ///                  "bar0".to_string()
29 ///              ),
30 ///           Ok(0x10000000)
31 ///       );
32 ///       assert_eq!(
33 ///           a.mmio_allocator(MmioType::High)
34 ///              .get(&Alloc::PciBar { bus: 0, dev: 0, func: 0, bar: 0 }),
35 ///           Some(&(0x10000000, 0x100, "bar0".to_string()))
36 ///       );
37 ///   }
38 /// ```
39 
40 /// MMIO address Type
41 ///    Low: address allocated from low_address_space
42 ///    High: address allocated from high_address_space
43 pub enum MmioType {
44     Low,
45     High,
46 }
47 
48 #[derive(Debug)]
49 pub struct SystemAllocator {
50     io_address_space: Option<AddressAllocator>,
51 
52     // Indexed by MmioType::Low and MmioType::High.
53     mmio_address_spaces: [AddressAllocator; 2],
54 
55     pci_allocator: AddressAllocator,
56     irq_allocator: AddressAllocator,
57     next_anon_id: usize,
58 }
59 
60 impl SystemAllocator {
61     /// Creates a new `SystemAllocator` for managing addresses and irq numvers.
62     /// Can return `None` if `base` + `size` overflows a u64 or if alignment isn't a power
63     /// of two.
64     ///
65     /// * `io_base` - The starting address of IO memory.
66     /// * `io_size` - The size of IO memory.
67     /// * `high_base` - The starting address of high MMIO space.
68     /// * `high_size` - The size of high MMIO space.
69     /// * `low_base` - The starting address of low MMIO space.
70     /// * `low_size` - The size of low MMIO space.
71     /// * `first_irq` - The first irq number to give out.
new( io_base: Option<u64>, io_size: Option<u64>, high_base: u64, high_size: u64, low_base: u64, low_size: u64, first_irq: u32, ) -> Result<Self>72     fn new(
73         io_base: Option<u64>,
74         io_size: Option<u64>,
75         high_base: u64,
76         high_size: u64,
77         low_base: u64,
78         low_size: u64,
79         first_irq: u32,
80     ) -> Result<Self> {
81         let page_size = pagesize() as u64;
82         Ok(SystemAllocator {
83             io_address_space: if let (Some(b), Some(s)) = (io_base, io_size) {
84                 Some(AddressAllocator::new(b, s, Some(0x400))?)
85             } else {
86                 None
87             },
88             mmio_address_spaces: [
89                 // MmioType::Low
90                 AddressAllocator::new(low_base, low_size, Some(page_size))?,
91                 // MmioType::High
92                 AddressAllocator::new(high_base, high_size, Some(page_size))?,
93             ],
94             // Support up to 256(buses) x 32(devices) x 8(functions) with default
95             // alignment allocating device with mandatory function number zero.
96             pci_allocator: AddressAllocator::new(8, (256 * 32 * 8) - 8, Some(8))?,
97             irq_allocator: AddressAllocator::new(
98                 first_irq as u64,
99                 1024 - first_irq as u64,
100                 Some(1),
101             )?,
102             next_anon_id: 0,
103         })
104     }
105 
106     /// Returns a `SystemAllocatorBuilder` that can create a new `SystemAllocator`.
builder() -> SystemAllocatorBuilder107     pub fn builder() -> SystemAllocatorBuilder {
108         SystemAllocatorBuilder::new()
109     }
110 
111     /// Reserves the next available system irq number.
allocate_irq(&mut self) -> Option<u32>112     pub fn allocate_irq(&mut self) -> Option<u32> {
113         let id = self.get_anon_alloc();
114         self.irq_allocator
115             .allocate(1, id, "irq-auto".to_string())
116             .map(|v| v as u32)
117             .ok()
118     }
119 
120     /// Reserves the next available system irq number.
reserve_irq(&mut self, irq: u32) -> bool121     pub fn reserve_irq(&mut self, irq: u32) -> bool {
122         let id = self.get_anon_alloc();
123         self.irq_allocator
124             .allocate_at(irq as u64, 1, id, "irq-fixed".to_string())
125             .is_ok()
126     }
127 
128     /// Allocate PCI slot location.
allocate_pci(&mut self, tag: String) -> Option<Alloc>129     pub fn allocate_pci(&mut self, tag: String) -> Option<Alloc> {
130         let id = self.get_anon_alloc();
131         self.pci_allocator
132             .allocate(1, id, tag)
133             .map(|v| Alloc::PciBar {
134                 bus: ((v >> 8) & 255) as u8,
135                 dev: ((v >> 3) & 31) as u8,
136                 func: (v & 7) as u8,
137                 bar: 0,
138             })
139             .ok()
140     }
141 
142     /// Reserve PCI slot location.
reserve_pci(&mut self, alloc: Alloc, tag: String) -> bool143     pub fn reserve_pci(&mut self, alloc: Alloc, tag: String) -> bool {
144         let id = self.get_anon_alloc();
145         match alloc {
146             Alloc::PciBar {
147                 bus,
148                 dev,
149                 func,
150                 bar: _,
151             } => {
152                 let bdf = ((bus as u64) << 8) | ((dev as u64) << 3) | (func as u64);
153                 self.pci_allocator.allocate_at(bdf, 1, id, tag).is_ok()
154             }
155             _ => false,
156         }
157     }
158 
159     /// Gets an allocator to be used for IO memory.
io_allocator(&mut self) -> Option<&mut AddressAllocator>160     pub fn io_allocator(&mut self) -> Option<&mut AddressAllocator> {
161         self.io_address_space.as_mut()
162     }
163 
164     /// Gets an allocator to be used for MMIO allocation.
165     ///    MmioType::Low: low mmio allocator
166     ///    MmioType::High: high mmio allocator
mmio_allocator(&mut self, mmio_type: MmioType) -> &mut AddressAllocator167     pub fn mmio_allocator(&mut self, mmio_type: MmioType) -> &mut AddressAllocator {
168         &mut self.mmio_address_spaces[mmio_type as usize]
169     }
170 
171     /// Gets a set of allocators to be used for MMIO allocation.
172     /// The set of allocators will try the low and high MMIO allocators, in that order.
mmio_allocator_any(&mut self) -> AddressAllocatorSet173     pub fn mmio_allocator_any(&mut self) -> AddressAllocatorSet {
174         AddressAllocatorSet::new(&mut self.mmio_address_spaces)
175     }
176 
177     /// Gets a unique anonymous allocation
get_anon_alloc(&mut self) -> Alloc178     pub fn get_anon_alloc(&mut self) -> Alloc {
179         self.next_anon_id += 1;
180         Alloc::Anon(self.next_anon_id)
181     }
182 }
183 
184 /// Used to build a system address map for use in creating a `SystemAllocator`.
185 pub struct SystemAllocatorBuilder {
186     io_base: Option<u64>,
187     io_size: Option<u64>,
188     low_mmio_base: Option<u64>,
189     low_mmio_size: Option<u64>,
190     high_mmio_base: Option<u64>,
191     high_mmio_size: Option<u64>,
192 }
193 
194 impl SystemAllocatorBuilder {
new() -> Self195     pub fn new() -> Self {
196         SystemAllocatorBuilder {
197             io_base: None,
198             io_size: None,
199             low_mmio_base: None,
200             low_mmio_size: None,
201             high_mmio_base: None,
202             high_mmio_size: None,
203         }
204     }
205 
add_io_addresses(mut self, base: u64, size: u64) -> Self206     pub fn add_io_addresses(mut self, base: u64, size: u64) -> Self {
207         self.io_base = Some(base);
208         self.io_size = Some(size);
209         self
210     }
211 
add_low_mmio_addresses(mut self, base: u64, size: u64) -> Self212     pub fn add_low_mmio_addresses(mut self, base: u64, size: u64) -> Self {
213         self.low_mmio_base = Some(base);
214         self.low_mmio_size = Some(size);
215         self
216     }
217 
add_high_mmio_addresses(mut self, base: u64, size: u64) -> Self218     pub fn add_high_mmio_addresses(mut self, base: u64, size: u64) -> Self {
219         self.high_mmio_base = Some(base);
220         self.high_mmio_size = Some(size);
221         self
222     }
223 
create_allocator(&self, first_irq: u32) -> Result<SystemAllocator>224     pub fn create_allocator(&self, first_irq: u32) -> Result<SystemAllocator> {
225         SystemAllocator::new(
226             self.io_base,
227             self.io_size,
228             self.high_mmio_base.ok_or(Error::MissingHighMMIOAddresses)?,
229             self.high_mmio_size.ok_or(Error::MissingHighMMIOAddresses)?,
230             self.low_mmio_base.ok_or(Error::MissingLowMMIOAddresses)?,
231             self.low_mmio_size.ok_or(Error::MissingLowMMIOAddresses)?,
232             first_irq,
233         )
234     }
235 }
236