virtio.rs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. mod virtio_blk;
  2. #[cfg(not(target_arch = "riscv64"))]
  3. compile_error!("VirtIO drivers are only supported on RISC-V architecture");
  4. use crate::kernel::{
  5. block::{make_device, BlockDevice},
  6. mem::{AsMemoryBlock, MemoryBlock, Page},
  7. };
  8. use alloc::{sync::Arc, vec::Vec};
  9. use core::num::NonZero;
  10. use eonix_hal::{arch_exported::fdt::FDT, mm::ArchPhysAccess};
  11. use eonix_log::{println_info, println_warn};
  12. use eonix_mm::{
  13. address::{Addr, PAddr, PhysAccess},
  14. paging::PFN,
  15. };
  16. use eonix_runtime::task::Task;
  17. use eonix_sync::Spin;
  18. use virtio_drivers::{
  19. device::blk::VirtIOBlk,
  20. transport::{mmio::MmioTransport, Transport},
  21. Hal,
  22. };
  23. pub struct HAL;
  24. unsafe impl Hal for HAL {
  25. fn dma_alloc(
  26. pages: usize,
  27. _direction: virtio_drivers::BufferDirection,
  28. ) -> (virtio_drivers::PhysAddr, core::ptr::NonNull<u8>) {
  29. let page = Page::alloc_at_least(pages);
  30. let paddr = page.start().addr();
  31. let ptr = page.as_memblk().as_byte_ptr();
  32. page.into_raw();
  33. (paddr, ptr)
  34. }
  35. unsafe fn dma_dealloc(
  36. paddr: virtio_drivers::PhysAddr,
  37. _vaddr: core::ptr::NonNull<u8>,
  38. _pages: usize,
  39. ) -> i32 {
  40. let pfn = PFN::from(PAddr::from(paddr));
  41. unsafe {
  42. // SAFETY: The caller ensures that the pfn corresponds to a valid
  43. // page allocated by `dma_alloc`.
  44. Page::from_raw(pfn);
  45. }
  46. 0
  47. }
  48. unsafe fn mmio_phys_to_virt(
  49. paddr: virtio_drivers::PhysAddr,
  50. size: usize,
  51. ) -> core::ptr::NonNull<u8> {
  52. MemoryBlock::new(NonZero::new(paddr).expect("paddr must be non-zero"), size).as_byte_ptr()
  53. }
  54. unsafe fn share(
  55. buffer: core::ptr::NonNull<[u8]>,
  56. _direction: virtio_drivers::BufferDirection,
  57. ) -> virtio_drivers::PhysAddr {
  58. let paddr = unsafe {
  59. // SAFETY: The caller ensures that the buffer is valid.
  60. ArchPhysAccess::from_ptr(buffer.cast::<u8>())
  61. };
  62. paddr.addr()
  63. }
  64. unsafe fn unshare(
  65. _paddr: virtio_drivers::PhysAddr,
  66. _buffer: core::ptr::NonNull<[u8]>,
  67. _direction: virtio_drivers::BufferDirection,
  68. ) {
  69. }
  70. }
  71. pub fn init_virtio_devices() {
  72. let mut disk_id = 0;
  73. let mut virtio_devices: Vec<_> = FDT
  74. .all_nodes()
  75. .filter(|node| {
  76. node.compatible()
  77. .is_some_and(|compatible| compatible.all().any(|s| s == "virtio,mmio"))
  78. })
  79. .filter_map(|node| node.reg())
  80. .flatten()
  81. .collect();
  82. virtio_devices.sort_by_key(|reg| reg.starting_address);
  83. for reg in virtio_devices {
  84. let base = PAddr::from(reg.starting_address as usize);
  85. let size = reg.size.expect("Virtio device must have a size");
  86. let base = unsafe {
  87. // SAFETY: We get the base address from the FDT, which is guaranteed to be valid.
  88. ArchPhysAccess::as_ptr(base)
  89. };
  90. match unsafe { MmioTransport::new(base, size) } {
  91. Ok(transport) => match transport.device_type() {
  92. virtio_drivers::transport::DeviceType::Block => {
  93. let block_device = VirtIOBlk::<HAL, _>::new(transport)
  94. .expect("Failed to initialize VirtIO Block device");
  95. let block_device = BlockDevice::register_disk(
  96. make_device(8, 16 * disk_id),
  97. 2147483647,
  98. Arc::new(Spin::new(block_device)),
  99. )
  100. .expect("Failed to register VirtIO Block device");
  101. Task::block_on(block_device.partprobe())
  102. .expect("Failed to probe partitions for VirtIO Block device");
  103. disk_id += 1;
  104. }
  105. virtio_drivers::transport::DeviceType::Network => {
  106. println_info!(
  107. "Initializing Virtio Network device at {:?} with size {:#x}",
  108. base,
  109. size
  110. );
  111. }
  112. virtio_drivers::transport::DeviceType::Console => {
  113. println_info!(
  114. "Initializing Virtio Console at {:?} with size {:#x}",
  115. base,
  116. size
  117. );
  118. }
  119. virtio_drivers::transport::DeviceType::EntropySource => {
  120. println_info!(
  121. "Initializing Virtio Entropy Source at {:?} with size {:#x}",
  122. base,
  123. size
  124. );
  125. }
  126. _ => {}
  127. },
  128. Err(err) => {
  129. println_warn!(
  130. "Failed to initialize Virtio device at {:?} with size {:#x}: {}",
  131. base,
  132. size,
  133. err
  134. );
  135. }
  136. }
  137. }
  138. }