Forráskód Böngészése

partial work: impl virtio block device and sbi console

greatbridf 7 hónapja
szülő
commit
bb2b276c8e

+ 84 - 2
Cargo.lock

@@ -90,6 +90,23 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
 
+[[package]]
+name = "embedded-io"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
+
+[[package]]
+name = "enumn"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "eonix_hal"
 version = "0.1.0"
@@ -152,6 +169,7 @@ dependencies = [
  "pointers",
  "posix_types",
  "slab_allocator",
+ "virtio-drivers",
 ]
 
 [[package]]
@@ -289,9 +307,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.22"
+version = "0.4.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 
 [[package]]
 name = "memoffset"
@@ -389,6 +407,15 @@ dependencies = [
  "riscv-pac",
 ]
 
+[[package]]
+name = "safe-mmio"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db02a82ad13df46afeba34a4e54065fa912308b9101b060e4422898eac0e06f6"
+dependencies = [
+ "zerocopy",
+]
+
 [[package]]
 name = "sbi"
 version = "0.3.0"
@@ -430,8 +457,63 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "thiserror"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[package]]
+name = "virtio-drivers"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe3f779fd88436e27b51540d9563c7454c8c814893a1e6f9bb6138bcac60627"
+dependencies = [
+ "bitflags",
+ "embedded-io",
+ "enumn",
+ "log",
+ "safe-mmio",
+ "thiserror",
+ "zerocopy",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]

+ 3 - 0
Cargo.toml

@@ -30,6 +30,9 @@ intrusive-collections = "0.9.7"
 itertools = { version = "0.13.0", default-features = false }
 acpi = "5.2.0"
 
+[target.'cfg(target_arch = "riscv64")'.dependencies]
+virtio-drivers = { version = "0.11.0" }
+
 [features]
 default = []
 trace_syscall = []

+ 5 - 0
crates/eonix_hal/src/arch/riscv64/bootstrap.rs

@@ -33,6 +33,7 @@ use riscv::{
         sstatus::{self, FS},
     },
 };
+use sbi::legacy::console_putchar;
 
 #[unsafe(link_section = ".bootstrap.stack")]
 static BOOT_STACK: [u8; 4096 * 16] = [0; 4096 * 16];
@@ -234,3 +235,7 @@ fn bootstrap_smp(alloc: impl Allocator, page_alloc: &RefCell<BasicPageAlloc>) {}
 pub fn early_console_write(s: &str) {
     write_str(s);
 }
+
+pub fn early_console_putchar(ch: u8) {
+    console_putchar(ch);
+}

+ 5 - 0
crates/eonix_hal/src/bootstrap.rs

@@ -25,3 +25,8 @@ impl BootStrapData {
 pub fn early_console_write(s: &str) {
     crate::arch::bootstrap::early_console_write(s);
 }
+
+#[cfg(target_arch = "riscv64")]
+pub fn early_console_putchar(ch: u8) {
+    crate::arch::bootstrap::early_console_putchar(ch);
+}

+ 9 - 1
src/driver.rs

@@ -1,12 +1,14 @@
 pub mod ahci;
 pub mod e1000e;
+#[cfg(target_arch = "x86_64")]
 pub mod serial;
 
-// TODO!!!: Put it somewhere else.
+#[cfg(target_arch = "x86_64")]
 pub struct Port8 {
     no: u16,
 }
 
+#[cfg(target_arch = "x86_64")]
 impl Port8 {
     pub const fn new(no: u16) -> Self {
         Self { no }
@@ -20,3 +22,9 @@ impl Port8 {
         arch::outb(self.no, data)
     }
 }
+
+#[cfg(target_arch = "riscv64")]
+pub mod virtio;
+
+#[cfg(target_arch = "riscv64")]
+pub mod sbi_console;

+ 36 - 0
src/driver/sbi_console.rs

@@ -0,0 +1,36 @@
+use crate::kernel::{block::make_device, CharDevice, CharDeviceType, Terminal, TerminalDevice};
+use alloc::sync::Arc;
+use eonix_log::ConsoleWrite;
+
+struct SbiConsole;
+
+impl ConsoleWrite for SbiConsole {
+    fn write(&self, s: &str) {
+        eonix_hal::bootstrap::early_console_write(s);
+    }
+}
+
+impl TerminalDevice for SbiConsole {
+    fn putchar(&self, ch: u8) {
+        eonix_hal::bootstrap::early_console_putchar(ch);
+    }
+
+    fn putchar_direct(&self, ch: u8) {
+        eonix_hal::bootstrap::early_console_putchar(ch);
+    }
+}
+
+pub fn init_console() {
+    eonix_log::set_console(Arc::new(SbiConsole));
+
+    let console = Arc::new(SbiConsole);
+    let terminal = Terminal::new(console.clone());
+    crate::kernel::console::set_console(terminal.clone()).expect("Failed to set console");
+
+    CharDevice::register(
+        make_device(4, 64),
+        Arc::from("sbi_console"),
+        CharDeviceType::Terminal(terminal),
+    )
+    .expect("Failed to register SBI console as a character device");
+}

+ 152 - 0
src/driver/virtio.rs

@@ -0,0 +1,152 @@
+mod virtio_blk;
+
+use crate::kernel::{
+    block::{make_device, BlockDevice},
+    mem::{AsMemoryBlock, MemoryBlock, Page},
+};
+use alloc::sync::Arc;
+use core::num::NonZero;
+use eonix_hal::{device::FDT, mm::ArchPhysAccess};
+use eonix_log::{println_info, println_warn};
+use eonix_mm::{
+    address::{Addr, PAddr, PhysAccess},
+    paging::PFN,
+};
+use eonix_sync::Spin;
+use virtio_drivers::{
+    device::blk::VirtIOBlk,
+    transport::{mmio::MmioTransport, Transport},
+    Hal,
+};
+
+pub struct HAL;
+
+unsafe impl Hal for HAL {
+    fn dma_alloc(
+        pages: usize,
+        _direction: virtio_drivers::BufferDirection,
+    ) -> (virtio_drivers::PhysAddr, core::ptr::NonNull<u8>) {
+        let page = Page::alloc_at_least(pages);
+
+        let paddr = page.start().addr();
+        let ptr = page.as_memblk().as_byte_ptr();
+        page.into_raw();
+
+        (paddr, ptr)
+    }
+
+    unsafe fn dma_dealloc(
+        paddr: virtio_drivers::PhysAddr,
+        _vaddr: core::ptr::NonNull<u8>,
+        _pages: usize,
+    ) -> i32 {
+        let pfn = PFN::from(PAddr::from(paddr));
+
+        unsafe {
+            // SAFETY: The caller ensures that the pfn corresponds to a valid
+            //         page allocated by `dma_alloc`.
+            Page::from_raw(pfn);
+        }
+
+        0
+    }
+
+    unsafe fn mmio_phys_to_virt(
+        paddr: virtio_drivers::PhysAddr,
+        size: usize,
+    ) -> core::ptr::NonNull<u8> {
+        MemoryBlock::new(NonZero::new(paddr).expect("paddr must be non-zero"), size).as_byte_ptr()
+    }
+
+    unsafe fn share(
+        buffer: core::ptr::NonNull<[u8]>,
+        _direction: virtio_drivers::BufferDirection,
+    ) -> virtio_drivers::PhysAddr {
+        let paddr = unsafe {
+            // SAFETY: The caller ensures that the buffer is valid.
+            ArchPhysAccess::from_ptr(buffer.cast::<u8>())
+        };
+
+        paddr.addr()
+    }
+
+    unsafe fn unshare(
+        _paddr: virtio_drivers::PhysAddr,
+        _buffer: core::ptr::NonNull<[u8]>,
+        _direction: virtio_drivers::BufferDirection,
+    ) {
+    }
+}
+
+pub fn init_virtio_devices() {
+    let mut disk_id = 0;
+    for reg in FDT
+        .all_nodes()
+        .filter(|node| {
+            node.compatible()
+                .is_some_and(|compatible| compatible.all().any(|s| s == "virtio,mmio"))
+        })
+        .filter_map(|node| node.reg())
+        .flatten()
+    {
+        let base = PAddr::from(reg.starting_address as usize);
+        let size = reg.size.expect("Virtio device must have a size");
+
+        let base = unsafe {
+            // SAFETY: We get the base address from the FDT, which is guaranteed to be valid.
+            ArchPhysAccess::as_ptr(base)
+        };
+
+        match unsafe { MmioTransport::new(base, size) } {
+            Ok(transport) => match transport.device_type() {
+                virtio_drivers::transport::DeviceType::Block => {
+                    let block_device = VirtIOBlk::<HAL, _>::new(transport)
+                        .expect("Failed to initialize VirtIO Block device");
+
+                    let block_device = BlockDevice::register_disk(
+                        make_device(8, 16 * disk_id),
+                        2147483647,
+                        Arc::new(Spin::new(block_device)),
+                    )
+                    .expect("Failed to register VirtIO Block device");
+
+                    block_device
+                        .partprobe()
+                        .expect("Failed to probe partitions for VirtIO Block device");
+
+                    disk_id += 1;
+                }
+                virtio_drivers::transport::DeviceType::Network => {
+                    println_info!(
+                        "Initializing Virtio Network device at {:?} with size {:#x}",
+                        base,
+                        size
+                    );
+                }
+                virtio_drivers::transport::DeviceType::Console => {
+                    println_info!(
+                        "Initializing Virtio Console at {:?} with size {:#x}",
+                        base,
+                        size
+                    );
+                }
+                virtio_drivers::transport::DeviceType::EntropySource => {
+                    println_info!(
+                        "Initializing Virtio Entropy Source at {:?} with size {:#x}",
+                        base,
+                        size
+                    );
+                }
+                _ => {}
+            },
+            Err(err) => {
+                println_warn!(
+                    "Failed to initialize Virtio device at {:?} with size {:#x}: {}",
+                    base,
+                    size,
+                    err
+                );
+            }
+        }
+    }
+}

+ 34 - 0
src/driver/virtio/virtio_blk.rs

@@ -0,0 +1,34 @@
+use super::HAL;
+use crate::{
+    io::Chunks,
+    kernel::{
+        block::{BlockDeviceRequest, BlockRequestQueue},
+        constants::EIO,
+        mem::AsMemoryBlock,
+    },
+    prelude::KResult,
+};
+use eonix_sync::Spin;
+use virtio_drivers::{device::blk::VirtIOBlk, transport::mmio::MmioTransport};
+
+impl BlockRequestQueue for Spin<VirtIOBlk<HAL, MmioTransport<'_>>> {
+    fn max_request_pages(&self) -> u64 {
+        1024
+    }
+
+    fn submit(&self, req: BlockDeviceRequest) -> KResult<()> {
+        let mut dev = self.lock();
+        for ((start, len), buffer_page) in
+            Chunks::new(req.sector as usize, req.count as usize, 8).zip(req.buffer.iter())
+        {
+            let buffer = unsafe {
+                // SAFETY: Pages in `req.buffer` are guaranteed to be exclusively owned by us.
+                &mut buffer_page.as_memblk().as_bytes_mut()[..len as usize * 512]
+            };
+
+            dev.read_blocks(start, buffer).map_err(|_| EIO)?;
+        }
+
+        Ok(())
+    }
+}

+ 7 - 7
src/kernel/interrupt.rs

@@ -1,7 +1,7 @@
 use super::mem::handle_kernel_page_fault;
 use super::timer::timer_interrupt;
 use crate::kernel::constants::EINVAL;
-use crate::{driver::Port8, prelude::*};
+use crate::prelude::*;
 use alloc::sync::Arc;
 use eonix_hal::processor::CPU;
 use eonix_hal::traits::fault::Fault;
@@ -22,13 +22,13 @@ pub fn default_irq_handler(irqno: usize) {
         handler();
     }
 
-    const PIC1_COMMAND: Port8 = Port8::new(0x20);
-    const PIC2_COMMAND: Port8 = Port8::new(0xA0);
+    // const PIC1_COMMAND: Port8 = Port8::new(0x20);
+    // const PIC2_COMMAND: Port8 = Port8::new(0xA0);
 
-    PIC1_COMMAND.write(0x20); // EOI
-    if irqno >= 8 {
-        PIC2_COMMAND.write(0x20); // EOI
-    }
+    // PIC1_COMMAND.write(0x20); // EOI
+    // if irqno >= 8 {
+    //     PIC2_COMMAND.write(0x20); // EOI
+    // }
 }
 
 pub fn default_fault_handler(fault_type: Fault, trap_ctx: &mut TrapContext) {

+ 8 - 2
src/kernel/mem/mm_list.rs

@@ -584,7 +584,10 @@ where
     fn set_anonymous(&mut self, execute: bool) {
         // Writable flag is set during page fault handling while executable flag is
         // preserved across page faults, so we set executable flag now.
-        let mut attr = PageAttribute::PRESENT | PageAttribute::USER | PageAttribute::COPY_ON_WRITE;
+        let mut attr = PageAttribute::PRESENT
+            | PageAttribute::READ
+            | PageAttribute::USER
+            | PageAttribute::COPY_ON_WRITE;
         attr.set(PageAttribute::EXECUTE, execute);
 
         self.set(EMPTY_PAGE.clone().into_raw(), T::Attr::from(attr));
@@ -593,7 +596,10 @@ where
     fn set_mapped(&mut self, execute: bool) {
         // Writable flag is set during page fault handling while executable flag is
         // preserved across page faults, so we set executable flag now.
-        let mut attr = PageAttribute::MAPPED | PageAttribute::USER | PageAttribute::COPY_ON_WRITE;
+        let mut attr = PageAttribute::READ
+            | PageAttribute::USER
+            | PageAttribute::MAPPED
+            | PageAttribute::COPY_ON_WRITE;
         attr.set(PageAttribute::EXECUTE, execute);
 
         self.set(EMPTY_PAGE.clone().into_raw(), T::Attr::from(attr));

+ 12 - 4
src/kernel/pcie/device.rs

@@ -1,6 +1,5 @@
 use super::{CommonHeader, Header};
 use crate::{kernel::mem::PhysAccess as _, sync::fence::memory_barrier};
-use acpi::mcfg::PciConfigEntry;
 use alloc::sync::Arc;
 use core::{ops::RangeInclusive, sync::atomic::Ordering};
 use eonix_mm::address::PAddr;
@@ -27,7 +26,7 @@ pub struct PCIDevice<'a> {
 #[allow(dead_code)]
 #[derive(Clone)]
 pub struct SegmentGroup {
-    id: u16,
+    id: usize,
     bus_range: RangeInclusive<u8>,
     base_address: PAddr,
 }
@@ -43,9 +42,18 @@ pub struct ConfigSpace {
 }
 
 impl SegmentGroup {
-    pub fn from_entry(entry: &PciConfigEntry) -> Self {
+    pub fn new(id: usize, bus_start: u8, bus_end: u8, base_address: PAddr) -> Self {
         Self {
-            id: entry.segment_group,
+            id,
+            bus_range: bus_start..=bus_end,
+            base_address,
+        }
+    }
+
+    #[cfg(target_arch = "x86_64")]
+    pub fn from_entry(entry: &acpi::mcfg::PciConfigEntry) -> Self {
+        Self {
+            id: entry.segment_group as usize,
             bus_range: entry.bus_range.clone(),
             base_address: PAddr::from(entry.physical_address),
         }

+ 2 - 4
src/kernel/pcie/driver.rs

@@ -31,11 +31,9 @@ pub fn register_driver(driver: impl PCIDriver + 'static) -> KResult<()> {
         btree_map::Entry::Occupied(_) => Err(EEXIST)?,
     };
 
-    let Some(device) = PCIE_DEVICES.lock().find(&index).clone_pointer() else {
-        Err(ENOENT)?
+    if let Some(device) = PCIE_DEVICES.lock().find(&index).clone_pointer() {
+        driver.handle_device(device)?;
     };
 
-    driver.handle_device(device)?;
-
     Ok(())
 }

+ 52 - 10
src/kernel/pcie/init.rs

@@ -2,9 +2,13 @@ use super::{
     device::{PCIDevice, SegmentGroup, PCIE_DEVICES},
     error::PciError,
 };
-use crate::kernel::mem::PhysAccess as _;
-use acpi::{AcpiHandler, AcpiTables, PciConfigRegions, PhysicalMapping};
-use eonix_mm::address::PAddr;
+use crate::kernel::{
+    constants::{EINVAL, ENOENT},
+    mem::PhysAccess as _,
+};
+use acpi::{AcpiHandler, PhysicalMapping};
+use eonix_hal::device::FDT;
+use eonix_mm::address::{PAddr, PRange};
 
 #[derive(Clone)]
 struct AcpiHandlerImpl;
@@ -27,15 +31,53 @@ impl AcpiHandler for AcpiHandlerImpl {
 }
 
 pub fn init_pcie() -> Result<(), PciError> {
-    let acpi_tables = unsafe {
-        // SAFETY: Our impl should be correct.
-        AcpiTables::search_for_rsdp_bios(AcpiHandlerImpl)?
-    };
+    #[cfg(target_arch = "x86_64")]
+    {
+        use acpi::{AcpiTables, PciConfigRegions};
+
+        let acpi_tables = unsafe {
+            // SAFETY: Our impl should be correct.
+            AcpiTables::search_for_rsdp_bios(AcpiHandlerImpl)?
+        };
+
+        let conf_regions = PciConfigRegions::new(&acpi_tables)?;
+        for region in conf_regions.iter() {
+            let segment_group = SegmentGroup::from_entry(&region);
+
+            for config_space in segment_group.iter() {
+                if let Some(header) = config_space.header() {
+                    let pci_device = PCIDevice::new(segment_group.clone(), config_space, header);
+
+                    PCIE_DEVICES.lock().insert(pci_device);
+                }
+            }
+        }
+    }
+
+    #[cfg(target_arch = "riscv64")]
+    {
+        let pcie_node = FDT.find_node("/soc/pci").ok_or(ENOENT)?;
+        let bus_range = pcie_node.property("bus-range").ok_or(ENOENT)?;
+        let reg = pcie_node.reg().ok_or(EINVAL)?.next().ok_or(EINVAL)?;
+
+        let mmio_range =
+            PRange::from(PAddr::from(reg.starting_address as usize)).grow(reg.size.ok_or(EINVAL)?);
+
+        if bus_range.value.len() != 8 {
+            Err(EINVAL)?;
+        }
+
+        let bus_start = u32::from_be_bytes(bus_range.value[..4].try_into().unwrap());
+        let bus_end = u32::from_be_bytes(bus_range.value[4..].try_into().unwrap());
+
+        if bus_start > u8::MAX as u32 || bus_end > u8::MAX as u32 || bus_start > bus_end {
+            Err(EINVAL)?;
+        }
 
-    let conf_regions = PciConfigRegions::new(&acpi_tables)?;
-    for region in conf_regions.iter() {
-        let segment_group = SegmentGroup::from_entry(&region);
+        let bus_start = bus_start as u8;
+        let bus_end = bus_end as u8;
 
+        let segment_group = SegmentGroup::new(0, bus_start, bus_end, mmio_range.start());
         for config_space in segment_group.iter() {
             if let Some(header) = config_space.header() {
                 let pci_device = PCIDevice::new(segment_group.clone(), config_space, header);

+ 0 - 1
src/kernel/task/thread.rs

@@ -22,7 +22,6 @@ use core::{
     sync::atomic::{AtomicBool, Ordering},
     task::{Context, Poll, Waker},
 };
-use eonix_hal::traits::trap::IrqState;
 use eonix_hal::{
     processor::{UserTLS, CPU},
     traits::{

+ 8 - 14
src/lib.rs

@@ -28,7 +28,6 @@ use core::{
 };
 use elf::ParsedElf32;
 use eonix_hal::{processor::CPU, trap::disable_irqs_save};
-use eonix_log::ConsoleWrite;
 use eonix_mm::address::PRange;
 use eonix_runtime::{run::FutureRun, scheduler::Scheduler, task::Task};
 use kernel::{
@@ -73,21 +72,10 @@ fn kernel_init(mut data: eonix_hal::bootstrap::BootStrapData) -> ! {
 
     #[cfg(target_arch = "riscv64")]
     {
-        struct Console;
-
-        impl ConsoleWrite for Console {
-            fn write(&self, s: &str) {
-                eonix_hal::bootstrap::early_console_write(s);
-            }
-        }
-
-        eonix_log::set_console(Arc::new(Console));
+        driver::sbi_console::init_console();
     }
 
-    #[cfg(target_arch = "x86_64")]
-    {
-        kernel::pcie::init_pcie().expect("Unable to initialize PCIe bus");
-    }
+    kernel::pcie::init_pcie().expect("Unable to initialize PCIe bus");
 
     // To satisfy the `Scheduler` "preempt count == 0" assertion.
     eonix_preempt::disable();
@@ -140,7 +128,13 @@ async fn init_process(early_kstack: PRange) {
     {
         // We might want the serial initialized as soon as possible.
         driver::serial::init().unwrap();
+        driver::e1000e::register_e1000e_driver();
+        driver::ahci::register_ahci_driver();
+    }
 
+    #[cfg(target_arch = "riscv64")]
+    {
+        driver::virtio::init_virtio_devices();
         driver::e1000e::register_e1000e_driver();
         driver::ahci::register_ahci_driver();
     }