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