1use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
94use std::collections::btree_map::BTreeMap;
95use std::ops::Deref;
96use std::result;
97use std::sync::Arc;
98
99use thiserror::Error;
100
101use crate::resources::Resource;
102use crate::{DeviceIo, IoAddress, IoSize, PioAddress};
103
104#[derive(Error, Debug)]
106pub enum Error {
107 #[error("device address conflicts with existing devices")]
109 DeviceOverlap,
110 #[error("no such device")]
112 NoDevice,
113}
114
115pub type Result<T> = result::Result<T, Error>;
117
118#[derive(Debug, Copy, Clone, Eq)]
120pub struct IoRange {
121 base: IoAddress,
122 size: IoSize,
123}
124
125impl IoRange {
126 fn new_pio_range(base: u16, size: u16) -> Self {
127 IoRange {
128 base: IoAddress(base as u64),
129 size: IoSize(size as u64),
130 }
131 }
132
133 fn new_mmio_range(base: u64, size: u64) -> Self {
134 IoRange {
135 base: IoAddress(base),
136 size: IoSize(size),
137 }
138 }
139}
140
141impl PartialEq for IoRange {
142 fn eq(&self, other: &IoRange) -> bool {
143 self.base == other.base
144 }
145}
146
147impl Ord for IoRange {
148 fn cmp(&self, other: &IoRange) -> Ordering {
149 self.base.cmp(&other.base)
150 }
151}
152
153impl PartialOrd for IoRange {
154 fn partial_cmp(&self, other: &IoRange) -> Option<Ordering> {
155 self.base.partial_cmp(&other.base)
156 }
157}
158
159#[derive(Clone, Default)]
166pub struct IoManager {
167 pio_bus: BTreeMap<IoRange, Arc<dyn DeviceIo>>,
169 mmio_bus: BTreeMap<IoRange, Arc<dyn DeviceIo>>,
171}
172
173impl IoManager {
174 pub fn new() -> Self {
176 IoManager::default()
177 }
178
179 pub fn register_device_io(
188 &mut self,
189 device: Arc<dyn DeviceIo>,
190 resources: &[Resource],
191 ) -> Result<()> {
192 for (idx, res) in resources.iter().enumerate() {
193 match *res {
194 Resource::PioAddressRange { base, size } => {
195 if self
196 .pio_bus
197 .insert(IoRange::new_pio_range(base, size), device.clone())
198 .is_some()
199 {
200 self.unregister_device_io(&resources[0..idx])
202 .expect("failed to unregister devices");
203
204 return Err(Error::DeviceOverlap);
205 }
206 }
207 Resource::MmioAddressRange { base, size } => {
208 if self
209 .mmio_bus
210 .insert(IoRange::new_mmio_range(base, size), device.clone())
211 .is_some()
212 {
213 self.unregister_device_io(&resources[0..idx])
215 .expect("failed to unregister devices");
216
217 return Err(Error::DeviceOverlap);
218 }
219 }
220 _ => continue,
221 }
222 }
223 Ok(())
224 }
225
226 pub fn unregister_device_io(&mut self, resources: &[Resource]) -> Result<()> {
232 for res in resources.iter() {
233 match *res {
234 Resource::PioAddressRange { base, size } => {
235 self.pio_bus.remove(&IoRange::new_pio_range(base, size));
236 }
237 Resource::MmioAddressRange { base, size } => {
238 self.mmio_bus.remove(&IoRange::new_mmio_range(base, size));
239 }
240 _ => continue,
241 }
242 }
243 Ok(())
244 }
245
246 pub fn mmio_read(&self, addr: u64, data: &mut [u8]) -> Result<()> {
250 self.get_mmio_device(IoAddress(addr))
251 .map(|(device, base)| device.read(base, IoAddress(addr - base.raw_value()), data))
252 .ok_or(Error::NoDevice)
253 }
254
255 pub fn mmio_write(&self, addr: u64, data: &[u8]) -> Result<()> {
259 self.get_mmio_device(IoAddress(addr))
260 .map(|(device, base)| device.write(base, IoAddress(addr - base.raw_value()), data))
261 .ok_or(Error::NoDevice)
262 }
263
264 fn get_mmio_device(&self, addr: IoAddress) -> Option<(&Arc<dyn DeviceIo>, IoAddress)> {
266 let range = IoRange::new_mmio_range(addr.raw_value(), 0);
267 if let Some((range, dev)) = self.mmio_bus.range(..=&range).nth_back(0) {
268 if (addr.raw_value() - range.base.raw_value()) < range.size.raw_value() {
269 return Some((dev, range.base));
270 }
271 }
272 None
273 }
274}
275
276impl IoManager {
277 pub fn pio_read(&self, addr: u16, data: &mut [u8]) -> Result<()> {
281 self.get_pio_device(PioAddress(addr))
282 .map(|(device, base)| device.pio_read(base, PioAddress(addr - base.raw_value()), data))
283 .ok_or(Error::NoDevice)
284 }
285
286 pub fn pio_write(&self, addr: u16, data: &[u8]) -> Result<()> {
290 self.get_pio_device(PioAddress(addr))
291 .map(|(device, base)| device.pio_write(base, PioAddress(addr - base.raw_value()), data))
292 .ok_or(Error::NoDevice)
293 }
294
295 fn get_pio_device(&self, addr: PioAddress) -> Option<(&Arc<dyn DeviceIo>, PioAddress)> {
297 let range = IoRange::new_pio_range(addr.raw_value(), 0);
298 if let Some((range, dev)) = self.pio_bus.range(..=&range).nth_back(0) {
299 if (addr.raw_value() as u64 - range.base.raw_value()) < range.size.raw_value() {
300 return Some((dev, PioAddress(range.base.0 as u16)));
301 }
302 }
303 None
304 }
305}
306
307impl PartialEq for IoManager {
308 fn eq(&self, other: &IoManager) -> bool {
309 if self.pio_bus.len() != other.pio_bus.len() {
310 return false;
311 }
312 if self.mmio_bus.len() != other.mmio_bus.len() {
313 return false;
314 }
315
316 for (io_range, device_io) in self.pio_bus.iter() {
317 if !other.pio_bus.contains_key(io_range) {
318 return false;
319 }
320 let other_device_io = &other.pio_bus[io_range];
321 if device_io.get_trapped_io_resources() != other_device_io.get_trapped_io_resources() {
322 return false;
323 }
324 }
325
326 for (io_range, device_io) in self.mmio_bus.iter() {
327 if !other.mmio_bus.contains_key(io_range) {
328 return false;
329 }
330 let other_device_io = &other.mmio_bus[io_range];
331 if device_io.get_trapped_io_resources() != other_device_io.get_trapped_io_resources() {
332 return false;
333 }
334 }
335
336 true
337 }
338}
339
340pub trait IoManagerContext {
346 type Context;
348
349 fn begin_tx(&self) -> Self::Context;
353
354 fn commit_tx(&self, ctx: Self::Context);
356
357 fn cancel_tx(&self, ctx: Self::Context);
359
360 fn register_device_io(
370 &self,
371 ctx: &mut Self::Context,
372 device: Arc<dyn DeviceIo>,
373 resources: &[Resource],
374 ) -> Result<()>;
375
376 fn unregister_device_io(&self, ctx: &mut Self::Context, resources: &[Resource]) -> Result<()>;
383}
384
385impl<T: IoManagerContext> IoManagerContext for Arc<T> {
386 type Context = T::Context;
387
388 fn begin_tx(&self) -> Self::Context {
389 self.deref().begin_tx()
390 }
391
392 fn commit_tx(&self, ctx: Self::Context) {
393 self.deref().commit_tx(ctx)
394 }
395
396 fn cancel_tx(&self, ctx: Self::Context) {
397 self.deref().cancel_tx(ctx)
398 }
399
400 fn register_device_io(
401 &self,
402 ctx: &mut Self::Context,
403 device: Arc<dyn DeviceIo>,
404 resources: &[Resource],
405 ) -> std::result::Result<(), Error> {
406 self.deref().register_device_io(ctx, device, resources)
407 }
408
409 fn unregister_device_io(
410 &self,
411 ctx: &mut Self::Context,
412 resources: &[Resource],
413 ) -> std::result::Result<(), Error> {
414 self.deref().unregister_device_io(ctx, resources)
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use std::error::Error;
421 use std::sync::Mutex;
422
423 use super::*;
424 use crate::resources::DeviceResources;
425
426 const PIO_ADDRESS_SIZE: u16 = 4;
427 const PIO_ADDRESS_BASE: u16 = 0x40;
428 const MMIO_ADDRESS_SIZE: u64 = 0x8765_4321;
429 const MMIO_ADDRESS_BASE: u64 = 0x1234_5678;
430 const LEGACY_IRQ: u32 = 4;
431 const CONFIG_DATA: u32 = 0x1234;
432
433 struct DummyDevice {
434 config: Mutex<u32>,
435 }
436
437 impl DummyDevice {
438 fn new(config: u32) -> Self {
439 DummyDevice {
440 config: Mutex::new(config),
441 }
442 }
443 }
444
445 impl DeviceIo for DummyDevice {
446 fn read(&self, _base: IoAddress, _offset: IoAddress, data: &mut [u8]) {
447 if data.len() > 4 {
448 return;
449 }
450 for (idx, iter) in data.iter_mut().enumerate() {
451 let config = self.config.lock().expect("failed to acquire lock");
452 *iter = (*config >> (idx * 8) & 0xff) as u8;
453 }
454 }
455
456 fn write(&self, _base: IoAddress, _offset: IoAddress, data: &[u8]) {
457 let mut config = self.config.lock().expect("failed to acquire lock");
458 *config = u32::from(data[0]) & 0xff;
459 }
460
461 fn pio_read(&self, _base: PioAddress, _offset: PioAddress, data: &mut [u8]) {
462 if data.len() > 4 {
463 return;
464 }
465 for (idx, iter) in data.iter_mut().enumerate() {
466 let config = self.config.lock().expect("failed to acquire lock");
467 *iter = (*config >> (idx * 8) & 0xff) as u8;
468 }
469 }
470
471 fn pio_write(&self, _base: PioAddress, _offset: PioAddress, data: &[u8]) {
472 let mut config = self.config.lock().expect("failed to acquire lock");
473 *config = u32::from(data[0]) & 0xff;
474 }
475 fn as_any(&self) -> &dyn std::any::Any {
476 self
477 }
478 }
479
480 #[test]
481 fn test_clone_io_manager() {
482 let mut io_mgr = IoManager::new();
483 let dummy = DummyDevice::new(0);
484 let dum = Arc::new(dummy);
485
486 let mut resource: Vec<Resource> = Vec::new();
487 let mmio = Resource::MmioAddressRange {
488 base: MMIO_ADDRESS_BASE,
489 size: MMIO_ADDRESS_SIZE,
490 };
491 let irq = Resource::LegacyIrq(LEGACY_IRQ);
492
493 resource.push(mmio);
494 resource.push(irq);
495
496 let pio = Resource::PioAddressRange {
497 base: PIO_ADDRESS_BASE,
498 size: PIO_ADDRESS_SIZE,
499 };
500 resource.push(pio);
501
502 assert!(io_mgr.register_device_io(dum.clone(), &resource).is_ok());
503
504 let io_mgr2 = io_mgr.clone();
505 assert_eq!(io_mgr2.mmio_bus.len(), 1);
506
507 assert_eq!(io_mgr2.pio_bus.len(), 1);
508
509 let (dev, addr) = io_mgr2
510 .get_mmio_device(IoAddress(MMIO_ADDRESS_BASE + 1))
511 .unwrap();
512 assert_eq!(Arc::strong_count(dev), 5);
513
514 assert_eq!(addr, IoAddress(MMIO_ADDRESS_BASE));
515
516 drop(io_mgr);
517 assert_eq!(Arc::strong_count(dev), 3);
518
519 drop(io_mgr2);
520 assert_eq!(Arc::strong_count(&dum), 1);
521 }
522
523 #[test]
524 fn test_register_unregister_device_io() {
525 let mut io_mgr = IoManager::new();
526 let dummy = DummyDevice::new(0);
527 let dum = Arc::new(dummy);
528
529 let mut resources = DeviceResources::new();
530 let mmio = Resource::MmioAddressRange {
531 base: MMIO_ADDRESS_BASE,
532 size: MMIO_ADDRESS_SIZE,
533 };
534 let pio = Resource::PioAddressRange {
535 base: PIO_ADDRESS_BASE,
536 size: PIO_ADDRESS_SIZE,
537 };
538 let irq = Resource::LegacyIrq(LEGACY_IRQ);
539
540 resources.append(mmio);
541 resources.append(pio);
542 resources.append(irq);
543
544 assert!(io_mgr.register_device_io(dum.clone(), &resources).is_ok());
545 assert!(io_mgr.register_device_io(dum, &resources).is_err());
546 assert!(io_mgr.unregister_device_io(&resources).is_ok())
547 }
548
549 #[test]
550 fn test_mmio_read_write() {
551 let mut io_mgr: IoManager = Default::default();
552 let dum = Arc::new(DummyDevice::new(CONFIG_DATA));
553 let mut resource: Vec<Resource> = Vec::new();
554
555 let mmio = Resource::MmioAddressRange {
556 base: MMIO_ADDRESS_BASE,
557 size: MMIO_ADDRESS_SIZE,
558 };
559 resource.push(mmio);
560 assert!(io_mgr.register_device_io(dum.clone(), &resource).is_ok());
561
562 let mut data = [0; 4];
563 assert!(io_mgr.mmio_read(MMIO_ADDRESS_BASE, &mut data).is_ok());
564 assert_eq!(data, [0x34, 0x12, 0, 0]);
565
566 assert!(io_mgr
567 .mmio_read(MMIO_ADDRESS_BASE + MMIO_ADDRESS_SIZE, &mut data)
568 .is_err());
569
570 data = [0; 4];
571 assert!(io_mgr.mmio_write(MMIO_ADDRESS_BASE, &data).is_ok());
572 assert_eq!(*dum.config.lock().unwrap(), 0);
573
574 assert!(io_mgr
575 .mmio_write(MMIO_ADDRESS_BASE + MMIO_ADDRESS_SIZE, &data)
576 .is_err());
577 }
578
579 #[test]
580 fn test_pio_read_write() {
581 let mut io_mgr: IoManager = Default::default();
582 let dum = Arc::new(DummyDevice::new(CONFIG_DATA));
583 let mut resource: Vec<Resource> = Vec::new();
584
585 let pio = Resource::PioAddressRange {
586 base: PIO_ADDRESS_BASE,
587 size: PIO_ADDRESS_SIZE,
588 };
589 resource.push(pio);
590 assert!(io_mgr.register_device_io(dum.clone(), &resource).is_ok());
591
592 let mut data = [0; 4];
593 assert!(io_mgr.pio_read(PIO_ADDRESS_BASE, &mut data).is_ok());
594 assert_eq!(data, [0x34, 0x12, 0, 0]);
595
596 assert!(io_mgr
597 .pio_read(PIO_ADDRESS_BASE + PIO_ADDRESS_SIZE, &mut data)
598 .is_err());
599
600 data = [0; 4];
601 assert!(io_mgr.pio_write(PIO_ADDRESS_BASE, &data).is_ok());
602 assert_eq!(*dum.config.lock().unwrap(), 0);
603
604 assert!(io_mgr
605 .pio_write(PIO_ADDRESS_BASE + PIO_ADDRESS_SIZE, &data)
606 .is_err());
607 }
608
609 #[test]
610 fn test_device_manager_data_structs() {
611 let range1 = IoRange::new_mmio_range(0x1000, 0x1000);
612 let range2 = IoRange::new_mmio_range(0x1000, 0x2000);
613 let range3 = IoRange::new_mmio_range(0x2000, 0x1000);
614
615 assert_eq!(range1, range1.clone());
616 assert_eq!(range1, range2);
617 assert!(range1 < range3);
618 }
619
620 #[test]
621 fn test_error_code() {
622 let err = super::Error::DeviceOverlap;
623
624 assert!(err.source().is_none());
625 assert_eq!(
626 format!("{}", err),
627 "device address conflicts with existing devices"
628 );
629
630 let err = super::Error::NoDevice;
631 assert!(err.source().is_none());
632 assert_eq!(format!("{:#?}", err), "NoDevice");
633 }
634
635 #[test]
636 fn test_io_manager_partial_eq() {
637 let mut io_mgr1 = IoManager::new();
638 let mut io_mgr2 = IoManager::new();
639 let dummy1 = Arc::new(DummyDevice::new(0));
640 let dummy2 = Arc::new(DummyDevice::new(0));
641
642 let mut resources1 = DeviceResources::new();
643 let mut resources2 = DeviceResources::new();
644
645 let mmio = Resource::MmioAddressRange {
646 base: MMIO_ADDRESS_BASE,
647 size: MMIO_ADDRESS_SIZE,
648 };
649 let pio = Resource::PioAddressRange {
650 base: PIO_ADDRESS_BASE,
651 size: PIO_ADDRESS_SIZE,
652 };
653
654 resources1.append(mmio.clone());
655 resources1.append(pio.clone());
656
657 resources2.append(mmio);
658 resources2.append(pio);
659
660 io_mgr1.register_device_io(dummy1, &resources1).unwrap();
661 io_mgr2.register_device_io(dummy2, &resources2).unwrap();
662
663 assert!(io_mgr1 == io_mgr2);
664 }
665
666 #[test]
667 fn test_io_manager_partial_neq() {
668 let mut io_mgr1 = IoManager::new();
669 let mut io_mgr2 = IoManager::new();
670 let dummy1 = Arc::new(DummyDevice::new(0));
671 let dummy2 = Arc::new(DummyDevice::new(0));
672
673 let mut resources1 = DeviceResources::new();
674 let mut resources2 = DeviceResources::new();
675
676 let mmio = Resource::MmioAddressRange {
677 base: MMIO_ADDRESS_BASE,
678 size: MMIO_ADDRESS_SIZE,
679 };
680 let pio = Resource::PioAddressRange {
681 base: PIO_ADDRESS_BASE,
682 size: PIO_ADDRESS_SIZE,
683 };
684
685 resources1.append(mmio.clone());
686 resources1.append(pio);
687
688 resources2.append(mmio);
689
690 io_mgr1.register_device_io(dummy1, &resources1).unwrap();
691 io_mgr2.register_device_io(dummy2, &resources2).unwrap();
692
693 assert!(io_mgr1 != io_mgr2);
694 }
695}