Skip to content

[feat] Enhance dyn driver #270

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 268 additions & 8 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions api/axfeat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ display = ["alloc", "paging", "axdriver/virtio-gpu", "dep:axdisplay", "axruntime
rtc = ["axhal/rtc", "axruntime/rtc"]

# Device drivers
driver-dyn = ["axruntime/driver-dyn","axdriver?/dyn"]
bus-mmio = ["axdriver?/bus-mmio"]
bus-pci = ["axdriver?/bus-pci"]
driver-ramdisk = ["axdriver?/ramdisk", "axfs?/use-ramdisk"]
Expand Down
9 changes: 8 additions & 1 deletion modules/axdriver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repository = "https://github.com/arceos-org/arceos/tree/main/modules/axdriver"
documentation = "https://arceos-org.github.io/arceos/axdriver/index.html"

[features]
dyn = []
dyn = ["dep:rdrive", "dep:arm-gic-driver"]
bus-mmio = []
bus-pci = ["dep:axdriver_pci", "dep:axhal", "dep:axconfig"]
net = ["axdriver_net"]
Expand Down Expand Up @@ -46,3 +46,10 @@ axalloc = { workspace = true, optional = true }
axhal = { workspace = true, optional = true }
axconfig = { workspace = true, optional = true }
axdma = { workspace = true, optional = true }
rdrive = { version = "0.15", optional = true }
axerrno = "0.1"
lazyinit = "0.2"
memory_addr = "0.4"

[target.'cfg(target_arch = "aarch64")'.dependencies]
arm-gic-driver = { version = "0.14", optional = true }
87 changes: 87 additions & 0 deletions modules/axdriver/src/dyn_drivers/blk/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use axdriver_base::{BaseDriverOps, DevError, DevResult, DeviceType};
use axdriver_block::BlockDriverOps;
use rdrive::{Device, driver::block::io};

#[cfg(feature = "virtio-blk")]
mod virtio;

pub struct Block(Device<rdrive::driver::Block>);

impl BaseDriverOps for Block {
fn device_type(&self) -> DeviceType {
DeviceType::Block
}
fn device_name(&self) -> &str {
self.0.descriptor().name
}
}

impl BlockDriverOps for Block {
fn num_blocks(&self) -> u64 {
self.0.lock().unwrap().num_blocks() as _
}
fn block_size(&self) -> usize {
self.0.lock().unwrap().block_size()
}
fn flush(&mut self) -> DevResult {
self.0
.lock()
.unwrap()
.flush()
.map_err(maping_io_err_to_dev_err)
}

fn read_block(&mut self, block_id: u64, buf: &mut [u8]) -> DevResult {
self.0
.lock()
.unwrap()
.read_block(block_id as _, buf)
.map_err(maping_io_err_to_dev_err)
}

fn write_block(&mut self, block_id: u64, buf: &[u8]) -> DevResult {
self.0
.lock()
.unwrap()
.write_block(block_id as _, buf)
.map_err(maping_io_err_to_dev_err)
}
}

impl From<Device<rdrive::driver::Block>> for Block {
fn from(base: Device<rdrive::driver::Block>) -> Self {
Self(base)
}
}

fn maping_io_err_to_dev_err(err: io::Error) -> DevError {
match err.kind {
io::ErrorKind::Other(_error) => DevError::Io,
io::ErrorKind::NotAvailable => DevError::BadState,
io::ErrorKind::BrokenPipe => DevError::BadState,
io::ErrorKind::InvalidParameter { name: _ } => DevError::InvalidParam,
io::ErrorKind::InvalidData => DevError::InvalidParam,
io::ErrorKind::TimedOut => DevError::Io,
io::ErrorKind::Interrupted => DevError::Again,
io::ErrorKind::Unsupported => DevError::Unsupported,
io::ErrorKind::OutOfMemory => DevError::NoMemory,
io::ErrorKind::WriteZero => DevError::InvalidParam,
}
}

fn maping_dev_err_to_io_err(err: DevError) -> io::Error {
let kind = match err {
DevError::Again => io::ErrorKind::Interrupted,
DevError::AlreadyExists => io::ErrorKind::Other("Already exists".into()),
DevError::BadState => io::ErrorKind::BrokenPipe,
DevError::InvalidParam => io::ErrorKind::InvalidData,
DevError::Io => io::ErrorKind::Other("I/O error".into()),
DevError::NoMemory => io::ErrorKind::OutOfMemory,
DevError::ResourceBusy => io::ErrorKind::Other("Resource busy".into()),
DevError::Unsupported => io::ErrorKind::Unsupported,
};
io::Error {
kind,
success_pos: 0,
}
}
99 changes: 99 additions & 0 deletions modules/axdriver/src/dyn_drivers/blk/virtio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
extern crate alloc;

use alloc::format;
use axdriver_base::DeviceType;
use axdriver_block::BlockDriverOps;
use axdriver_virtio::MmioTransport;
use axhal::mem::PhysAddr;
use rdrive::{
DriverGeneric, PlatformDevice, driver::block::*, module_driver, probe::OnProbeError,
register::FdtInfo,
};

use crate::dyn_drivers::blk::maping_dev_err_to_io_err;
use crate::dyn_drivers::iomap;
use crate::virtio::VirtIoHalImpl;

type Device<T> = axdriver_virtio::VirtIoBlkDev<VirtIoHalImpl, T>;

module_driver!(
name: "Virtio Block",
level: ProbeLevel::PostKernel,
priority: ProbePriority::DEFAULT,
probe_kinds: &[
ProbeKind::Fdt {
compatibles: &["virtio,mmio"],
on_probe: probe
}
],
);

fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> {
let base_reg = info
.node
.reg()
.and_then(|mut regs| regs.next())
.ok_or(OnProbeError::other(alloc::format!(
"[{}] has no reg",
info.node.name()
)))?;

let mmio_size = base_reg.size.unwrap_or(0x1000);
let mmio_base = PhysAddr::from_usize(base_reg.address as usize);

let mmio_base = iomap(mmio_base, mmio_size)?.as_ptr();

let (ty, transport) =
axdriver_virtio::probe_mmio_device(mmio_base, mmio_size).ok_or(OnProbeError::NotMatch)?;

if ty != DeviceType::Block {
return Err(OnProbeError::NotMatch);
}

let dev = Device::try_new(transport).map_err(|e| {
OnProbeError::other(format!(
"failed to initialize Virtio Block device at [PA:{mmio_base:?},): {e:?}"
))
})?;

let dev = BlockDivce(dev);
plat_dev.register_block(dev);
debug!("virtio block device registered successfully");
Ok(())
}

struct BlockDivce(Device<MmioTransport>);

impl DriverGeneric for BlockDivce {
fn open(&mut self) -> Result<(), rdrive::KError> {
Ok(())
}

fn close(&mut self) -> Result<(), rdrive::KError> {
Ok(())
}
}

impl rdrive::driver::block::Interface for BlockDivce {
fn num_blocks(&self) -> usize {
self.0.num_blocks() as _
}

fn block_size(&self) -> usize {
self.0.block_size()
}

fn read_block(&mut self, block_id: usize, buf: &mut [u8]) -> Result<(), io::Error> {
self.0
.read_block(block_id as u64, buf)
.map_err(maping_dev_err_to_io_err)
}
fn write_block(&mut self, block_id: usize, buf: &[u8]) -> Result<(), io::Error> {
self.0
.write_block(block_id as u64, buf)
.map_err(maping_dev_err_to_io_err)
}
fn flush(&mut self) -> Result<(), io::Error> {
self.0.flush().map_err(maping_dev_err_to_io_err)
}
}
44 changes: 44 additions & 0 deletions modules/axdriver/src/dyn_drivers/intc/gicv2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
extern crate alloc;

use alloc::format;
use arm_gic_driver::v2::Gic;
use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo};

use crate::dyn_drivers::iomap;

module_driver!(
name: "GICv2",
level: ProbeLevel::PreKernel,
priority: ProbePriority::INTC,
probe_kinds: &[
ProbeKind::Fdt {
compatibles: &["arm,cortex-a15-gic", "arm,gic-400"],
on_probe: probe_gic
},
] ,
);

fn probe_gic(info: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> {
let mut reg = info.node.reg().ok_or(OnProbeError::other(format!(
"[{}] has no reg",
info.node.name()
)))?;

let gicd_reg = reg.next().unwrap();
let gicc_reg = reg.next().unwrap();

let gicd = iomap(
(gicd_reg.address as usize).into(),
gicd_reg.size.unwrap_or(0x1000),
)?;
let gicc = iomap(
(gicc_reg.address as usize).into(),
gicc_reg.size.unwrap_or(0x1000),
)?;

let gic = Gic::new(gicd, gicc);

dev.register_intc(gic);

Ok(())
}
44 changes: 44 additions & 0 deletions modules/axdriver/src/dyn_drivers/intc/gicv3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
extern crate alloc;

use alloc::format;
use arm_gic_driver::v3::Gic;
use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo};

use crate::dyn_drivers::iomap;

module_driver!(
name: "GICv3",
level: ProbeLevel::PreKernel,
priority: ProbePriority::INTC,
probe_kinds: &[
ProbeKind::Fdt {
compatibles: &["arm,gic-v3"],
on_probe: probe_gic
}
],
);

fn probe_gic(info: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> {
let mut reg = info.node.reg().ok_or(OnProbeError::other(format!(
"[{}] has no reg",
info.node.name()
)))?;

let gicd_reg = reg.next().unwrap();
let gicr_reg = reg.next().unwrap();

let gicd = iomap(
(gicd_reg.address as usize).into(),
gicd_reg.size.unwrap_or(0x1000),
)?;
let gicr = iomap(
(gicr_reg.address as usize).into(),
gicr_reg.size.unwrap_or(0x1000),
)?;

let gic = Gic::new(gicd, gicr);

dev.register_intc(gic);

Ok(())
}
4 changes: 4 additions & 0 deletions modules/axdriver/src/dyn_drivers/intc/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[cfg(target_arch = "aarch64")]
mod gicv2;
#[cfg(target_arch = "aarch64")]
mod gicv3;
78 changes: 78 additions & 0 deletions modules/axdriver/src/dyn_drivers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use core::{error::Error, ops::Deref, ptr::NonNull};

use alloc::{boxed::Box, format, string::ToString};
use axerrno::{AxError, AxResult};
use axhal::mem::{PhysAddr, VirtAddr, phys_to_virt};
use lazyinit::LazyInit;
use memory_addr::MemoryAddr;
use rdrive::register::{DriverRegister, DriverRegisterSlice};

mod intc;

#[cfg(feature = "block")]
pub mod blk;

/// A function type that maps a physical address to a virtual address. map flags should be read/write/device.
pub type IoMapFunc = fn(PhysAddr, usize) -> AxResult<VirtAddr>;

static IO_MAP_FUNC: LazyInit<IoMapFunc> = LazyInit::new();

/// Sets up the device driver subsystem.
pub fn setup(dtb: usize, io_map_func: IoMapFunc) {
IO_MAP_FUNC.init_once(io_map_func);
if dtb == 0 {
warn!("Device tree base address is 0, skipping device driver setup.");
return;
}

let dtb_virt = phys_to_virt(dtb.into());
if let Some(dtb) = NonNull::new(dtb_virt.as_mut_ptr()) {
rdrive::init(rdrive::Platform::Fdt { addr: dtb }).unwrap();
rdrive::register_append(&driver_registers());
rdrive::probe_pre_kernel().unwrap();
}
}

#[allow(unused)]
/// maps a mmio physical address to a virtual address.
fn iomap(addr: PhysAddr, size: usize) -> Result<NonNull<u8>, Box<dyn Error>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why return Box<dyn Error> as error type?

Maybe you just return a string? Can we return AxError?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnProbeError uses Box<dyn Error>, these two error type are in other crates, if we want to use ? to convert err, need to create new type to warp a String or AxError,and boxed error has more err msg than AxError, make it easier to find error location.

let end = (addr + size).align_up_4k();
let start = addr.align_down_4k();
let offset = addr - start;
let size = end - start;
let iomap = *IO_MAP_FUNC
.get()
.ok_or_else(|| "IO map function not initialized".to_string())?;

let virt = match iomap(start, size) {
Ok(val) => val,
Err(AxError::AlreadyExists) => phys_to_virt(start),
Err(e) => {
return Err(format!(
"Failed to map MMIO region: {e:?} (addr: {start:?}, size: {size:#x})"
)
.into());
}
};
let start_virt = virt + offset;
Ok(unsafe { NonNull::new_unchecked(start_virt.as_mut_ptr()) })
}

fn driver_registers() -> impl Deref<Target = [DriverRegister]> {
unsafe extern "C" {
fn __sdriver_register();
fn __edriver_register();
}

unsafe {
let len = __edriver_register as usize - __sdriver_register as usize;

if len == 0 {
return DriverRegisterSlice::empty();
}

let data = core::slice::from_raw_parts(__sdriver_register as _, len);

DriverRegisterSlice::from_raw(data)
}
}
Loading
Loading