1use std::collections::btree_map;
8use std::collections::BTreeMap;
9
10use base::pagesize;
11
12use crate::address_allocator::AddressAllocator;
13use crate::address_allocator::AddressAllocatorSet;
14use crate::AddressRange;
15use crate::Alloc;
16use crate::Error;
17use crate::PciAddress;
18use crate::Result;
19
20#[derive(Copy, Clone, PartialEq, Eq)]
24pub enum MmioType {
25 Low,
26 High,
27}
28
29#[derive(Copy, Clone, Debug)]
31pub struct AllocOptions {
32 prefetchable: bool,
33 max_address: u64,
34 alignment: Option<u64>,
35 top_down: bool,
36}
37
38impl Default for AllocOptions {
39 fn default() -> Self {
40 AllocOptions::new()
41 }
42}
43
44impl AllocOptions {
45 pub fn new() -> Self {
46 AllocOptions {
47 prefetchable: false,
48 max_address: u64::MAX,
49 alignment: None,
50 top_down: false,
51 }
52 }
53
54 pub fn prefetchable(&mut self, prefetchable: bool) -> &mut Self {
59 self.prefetchable = prefetchable;
60 self
61 }
62
63 pub fn max_address(&mut self, max_address: u64) -> &mut Self {
68 self.max_address = max_address;
69 self
70 }
71
72 pub fn align(&mut self, alignment: u64) -> &mut Self {
75 self.alignment = Some(alignment);
76 self
77 }
78
79 pub fn top_down(&mut self, top_down: bool) -> &mut Self {
82 self.top_down = top_down;
83 self
84 }
85}
86
87pub struct SystemAllocatorConfig {
88 pub io: Option<AddressRange>,
90 pub low_mmio: AddressRange,
96 pub high_mmio: AddressRange,
102 pub platform_mmio: Option<AddressRange>,
104 pub first_irq: u32,
106}
107
108#[derive(Debug)]
109pub struct SystemAllocator {
110 io_address_space: Option<AddressAllocator>,
111
112 mmio_address_spaces: [AddressAllocator; 2],
114 mmio_platform_address_spaces: Option<AddressAllocator>,
115
116 reserved_region: Option<AddressRange>,
117
118 pci_allocator: BTreeMap<u8, AddressAllocator>,
120 irq_allocator: AddressAllocator,
121 gpe_allocator: AddressAllocator,
122 next_anon_id: usize,
123}
124
125impl SystemAllocator {
126 pub fn new(
136 config: SystemAllocatorConfig,
137 reserve_region_size: Option<u64>,
138 mmio_address_ranges: &[AddressRange],
139 ) -> Result<Self> {
140 let page_size = pagesize() as u64;
141
142 let (high_mmio, reserved_region) = match reserve_region_size {
143 Some(reserved_len) => {
144 let high_mmio_len = config.high_mmio.len().ok_or(Error::OutOfBounds)?;
145 if reserved_len > high_mmio_len {
146 return Err(Error::OutOfSpace);
147 }
148 let reserved_start = config.high_mmio.start;
149 let reserved_region =
150 AddressRange::from_start_and_size(reserved_start, reserved_len)
151 .ok_or(Error::OutOfBounds)?;
152 let high_mmio = if reserved_len == high_mmio_len {
153 AddressRange::empty()
154 } else {
155 AddressRange {
156 start: reserved_start + reserved_len,
157 end: config.high_mmio.end,
158 }
159 };
160 (high_mmio, Some(reserved_region))
161 }
162 None => (config.high_mmio, None),
163 };
164
165 let intersect_mmio_range = |src_range: AddressRange| -> Result<Vec<AddressRange>> {
166 Ok(if mmio_address_ranges.is_empty() {
167 vec![src_range]
168 } else {
169 mmio_address_ranges
170 .iter()
171 .map(|r| r.intersect(src_range))
172 .collect()
173 })
174 };
175
176 Ok(SystemAllocator {
177 io_address_space: if let Some(io) = config.io {
178 if io.end > 0xffff {
181 return Err(Error::IOPortOutOfRange(io));
182 }
183 Some(AddressAllocator::new(io, Some(0x400), None)?)
184 } else {
185 None
186 },
187 mmio_address_spaces: [
188 AddressAllocator::new_from_list(
190 intersect_mmio_range(config.low_mmio)?,
191 Some(page_size),
192 None,
193 )?,
194 AddressAllocator::new_from_list(
196 intersect_mmio_range(high_mmio)?,
197 Some(page_size),
198 None,
199 )?,
200 ],
201
202 pci_allocator: BTreeMap::new(),
203
204 mmio_platform_address_spaces: if let Some(platform) = config.platform_mmio {
205 Some(AddressAllocator::new(platform, Some(page_size), None)?)
206 } else {
207 None
208 },
209
210 reserved_region,
211
212 irq_allocator: AddressAllocator::new(
213 AddressRange {
214 start: config.first_irq as u64,
215 end: 1023,
216 },
217 Some(1),
218 None,
219 )?,
220
221 gpe_allocator: AddressAllocator::new(
227 AddressRange { start: 0, end: 255 },
228 Some(1),
229 None,
230 )?,
231 next_anon_id: 0,
232 })
233 }
234
235 pub fn allocate_irq(&mut self) -> Option<u32> {
237 let id = self.get_anon_alloc();
238 self.irq_allocator
239 .allocate(1, id, "irq-auto".to_string())
240 .map(|v| v as u32)
241 .ok()
242 }
243
244 pub fn release_irq(&mut self, irq: u32) {
246 let _ = self.irq_allocator.release_containing(irq.into());
247 }
248
249 pub fn reserve_irq(&mut self, irq: u32) -> bool {
251 let id = self.get_anon_alloc();
252 self.irq_allocator
253 .allocate_at(
254 AddressRange {
255 start: irq.into(),
256 end: irq.into(),
257 },
258 id,
259 "irq-fixed".to_string(),
260 )
261 .is_ok()
262 }
263
264 pub fn allocate_gpe(&mut self) -> Option<u32> {
266 let id = self.get_anon_alloc();
267 self.gpe_allocator
268 .allocate(1, id, "gpe-auto".to_string())
269 .map(|v| v as u32)
270 .ok()
271 }
272
273 fn get_pci_allocator_mut(&mut self, bus: u8) -> Option<&mut AddressAllocator> {
274 match self.pci_allocator.entry(bus) {
275 btree_map::Entry::Occupied(entry) => Some(entry.into_mut()),
276 btree_map::Entry::Vacant(entry) => {
277 let base = if bus == 0 { 8 } else { 0 };
280
281 let pci_alloc = AddressAllocator::new(
285 AddressRange {
286 start: base,
287 end: (32 * 8) - 1,
288 },
289 Some(1),
290 Some(8),
291 )
292 .ok()?;
293
294 Some(entry.insert(pci_alloc))
295 }
296 }
297 }
298
299 pub fn pci_bus_empty(&self, bus: u8) -> bool {
301 !self.pci_allocator.contains_key(&bus)
302 }
303
304 pub fn allocate_pci(&mut self, bus: u8, tag: String) -> Option<PciAddress> {
306 let id = self.get_anon_alloc();
307 let allocator = self.get_pci_allocator_mut(bus)?;
308 allocator
309 .allocate(1, id, tag)
310 .map(|v| PciAddress {
311 bus,
312 dev: (v >> 3) as u8,
313 func: (v & 7) as u8,
314 })
315 .ok()
316 }
317
318 pub fn reserve_pci(&mut self, pci_addr: PciAddress, tag: String) -> bool {
320 let id = self.get_anon_alloc();
321
322 let allocator = match self.get_pci_allocator_mut(pci_addr.bus) {
323 Some(v) => v,
324 None => return false,
325 };
326 let df = ((pci_addr.dev as u64) << 3) | (pci_addr.func as u64);
327 allocator
328 .allocate_at(AddressRange { start: df, end: df }, id, tag)
329 .is_ok()
330 }
331
332 pub fn release_pci(&mut self, pci_addr: PciAddress) -> bool {
334 let allocator = match self.get_pci_allocator_mut(pci_addr.bus) {
335 Some(v) => v,
336 None => return false,
337 };
338 let df = ((pci_addr.dev as u64) << 3) | (pci_addr.func as u64);
339 allocator.release_containing(df).is_ok()
340 }
341
342 pub fn allocate_mmio(
344 &mut self,
345 size: u64,
346 alloc: Alloc,
347 tag: String,
348 opts: &AllocOptions,
349 ) -> Result<u64> {
350 if opts.max_address < u32::MAX as u64 {
353 return Err(Error::OutOfSpace);
354 }
355
356 let mut mmio_type = MmioType::High;
357 if opts.max_address < u64::MAX || !opts.prefetchable {
358 mmio_type = MmioType::Low;
359 }
360
361 let res = self.allocate_mmio_internal(size, alloc, tag.clone(), opts, mmio_type);
362 if mmio_type == MmioType::High && matches!(res, Err(Error::OutOfSpace)) {
365 self.allocate_mmio_internal(size, alloc, tag, opts, MmioType::Low)
366 } else {
367 res
368 }
369 }
370
371 fn allocate_mmio_internal(
372 &mut self,
373 size: u64,
374 alloc: Alloc,
375 tag: String,
376 opts: &AllocOptions,
377 mmio_type: MmioType,
378 ) -> Result<u64> {
379 let allocator = &mut self.mmio_address_spaces[mmio_type as usize];
380 match (opts.alignment, opts.top_down) {
381 (Some(align), true) => allocator.reverse_allocate_with_align(size, alloc, tag, align),
382 (Some(align), false) => allocator.allocate_with_align(size, alloc, tag, align),
383 (None, true) => allocator.reverse_allocate(size, alloc, tag),
384 (None, false) => allocator.allocate(size, alloc, tag),
385 }
386 }
387
388 pub fn reserve_mmio(&mut self, range: AddressRange) -> Result<()> {
393 let mut pools = Vec::new();
394 for pool in self.mmio_pools() {
395 pools.push(*pool);
396 }
397 pools.sort_by(|a, b| a.start.cmp(&b.start));
398 for pool in &pools {
399 if pool.start > range.end {
400 break;
401 }
402
403 let overlap = pool.intersect(range);
404 if !overlap.is_empty() {
405 let id = self.get_anon_alloc();
406 self.mmio_allocator_any().allocate_at(
407 overlap,
408 id,
409 "pci mmio reserve".to_string(),
410 )?;
411 }
412 }
413
414 Ok(())
415 }
416
417 pub fn mmio_platform_allocator(&mut self) -> Option<&mut AddressAllocator> {
419 self.mmio_platform_address_spaces.as_mut()
420 }
421
422 pub fn io_allocator(&mut self) -> Option<&mut AddressAllocator> {
424 self.io_address_space.as_mut()
425 }
426
427 pub fn mmio_allocator(&mut self, mmio_type: MmioType) -> &mut AddressAllocator {
431 &mut self.mmio_address_spaces[mmio_type as usize]
432 }
433
434 pub fn mmio_allocator_any(&mut self) -> AddressAllocatorSet {
437 AddressAllocatorSet::new(&mut self.mmio_address_spaces)
438 }
439
440 pub fn mmio_pools(&self) -> Vec<&AddressRange> {
442 self.mmio_address_spaces
443 .iter()
444 .flat_map(|mmio_as| mmio_as.pools())
445 .collect()
446 }
447
448 pub fn reserved_region(&self) -> Option<AddressRange> {
450 self.reserved_region
451 }
452
453 pub fn get_anon_alloc(&mut self) -> Alloc {
455 self.next_anon_id += 1;
456 Alloc::Anon(self.next_anon_id)
457 }
458}
459
460#[cfg(test)]
461mod tests {
462 use super::*;
463
464 #[test]
465 fn example() {
466 let mut a = SystemAllocator::new(
467 SystemAllocatorConfig {
468 io: Some(AddressRange {
469 start: 0x1000,
470 end: 0xffff,
471 }),
472 low_mmio: AddressRange {
473 start: 0x3000_0000,
474 end: 0x3000_ffff,
475 },
476 high_mmio: AddressRange {
477 start: 0x1000_0000,
478 end: 0x1fffffff,
479 },
480 platform_mmio: None,
481 first_irq: 5,
482 },
483 None,
484 &[],
485 )
486 .unwrap();
487
488 assert_eq!(a.allocate_irq(), Some(5));
489 assert_eq!(a.allocate_irq(), Some(6));
490 assert_eq!(a.allocate_gpe(), Some(0));
491 assert_eq!(a.allocate_gpe(), Some(1));
492 assert_eq!(
493 a.mmio_allocator(MmioType::High).allocate(
494 0x100,
495 Alloc::PciBar {
496 bus: 0,
497 dev: 0,
498 func: 0,
499 bar: 0
500 },
501 "bar0".to_string()
502 ),
503 Ok(0x10000000)
504 );
505 assert_eq!(
506 a.mmio_allocator(MmioType::High).get(&Alloc::PciBar {
507 bus: 0,
508 dev: 0,
509 func: 0,
510 bar: 0
511 }),
512 Some(&(
513 AddressRange {
514 start: 0x10000000,
515 end: 0x100000ff
516 },
517 "bar0".to_string()
518 ))
519 );
520
521 let id = a.get_anon_alloc();
522 assert_eq!(
523 a.mmio_allocator(MmioType::Low).allocate_at(
524 AddressRange {
525 start: 0x3000_5000,
526 end: 0x30009fff
527 },
528 id,
529 "Test".to_string()
530 ),
531 Ok(())
532 );
533 assert_eq!(
534 a.mmio_allocator(MmioType::Low).release(id),
535 Ok(AddressRange {
536 start: 0x3000_5000,
537 end: 0x30009fff
538 })
539 );
540 assert_eq!(
541 a.reserve_mmio(AddressRange {
542 start: 0x3000_2000,
543 end: 0x30005fff
544 }),
545 Ok(())
546 );
547 assert_eq!(
548 a.mmio_allocator(MmioType::Low)
549 .allocate_at(
550 AddressRange {
551 start: 0x3000_5000,
552 end: 0x3000_9fff
553 },
554 id,
555 "Test".to_string()
556 )
557 .is_err(),
558 true
559 );
560 }
561
562 #[test]
563 fn reserve_region_boundaries() {
564 let mut a = SystemAllocator::new(
565 SystemAllocatorConfig {
566 io: None,
567 low_mmio: AddressRange {
568 start: 0x1000,
569 end: 0x1fff,
570 },
571 high_mmio: AddressRange {
572 start: 0,
573 end: 0xffff_ffff,
574 },
575 platform_mmio: None,
576 first_irq: 5,
577 },
578 Some(0),
579 &[],
580 )
581 .unwrap();
582 assert_eq!(a.reserved_region(), Some(AddressRange::empty()));
583 assert_eq!(
584 a.mmio_allocator(MmioType::High)
585 .allocate(1, Alloc::Anon(0), "high".to_string()),
586 Ok(0)
587 );
588
589 let mut a = SystemAllocator::new(
590 SystemAllocatorConfig {
591 io: None,
592 low_mmio: AddressRange {
593 start: 0x1000,
594 end: 0x1fff,
595 },
596 high_mmio: AddressRange {
597 start: u64::MAX - 0xfff,
598 end: u64::MAX,
599 },
600 platform_mmio: None,
601 first_irq: 5,
602 },
603 Some(0x1000),
604 &[],
605 )
606 .unwrap();
607 assert_eq!(
608 a.reserved_region(),
609 Some(AddressRange {
610 start: u64::MAX - 0xfff,
611 end: u64::MAX,
612 })
613 );
614 assert_eq!(
615 a.mmio_allocator(MmioType::High)
616 .allocate(1, Alloc::Anon(0), "high".to_string()),
617 Err(Error::OutOfSpace)
618 );
619 }
620}