diff --git a/drivers/driver_base/docs/design.md b/drivers/driver_base/docs/design.md new file mode 100644 index 0000000000000000000000000000000000000000..593505907d6f533ab566eaf92c01a4d3683262d3 --- /dev/null +++ b/drivers/driver_base/docs/design.md @@ -0,0 +1,119 @@ +# driver_base — 设计文档 + +## 定位 + +本模块提供 x-kernel 所有设备驱动的公共基础接口,定义了设备分类枚举 +`DeviceKind`、驱动错误类型 `DriverError` / `DriverResult`,以及所有设备 +必须实现的 `Device` trait。它是 `drivers/` 下各子 crate +(block、net、display、input、vsock、virtio、kdriver)的共同依赖, +为内核驱动框架提供统一的类型契约。 + +## 背景 + +x-kernel 运行在裸机 `no_std` 环境中,无法使用 `std::io::Error` 等标准库 +错误类型。同时,内核需要对异构设备(块设备、网络设备、显示设备等)进行 +统一管理,因此需要一套轻量、无依赖的公共接口层,使各驱动 crate 在错误 +处理和设备身份描述上保持一致。 + +## 范围 + +涉及的源文件: + +``` +driver_base/ +├── src/ +│ └── lib.rs +│ +└── Cargo.toml +``` + +## 架构 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ driver_base │ +│ │ +│ DeviceKind ──分类──> Device │ +│ │ │ │ +│ │ ├── name() │ +│ │ ├── device_kind() │ +│ │ └── irq() │ +│ │ │ +│ DriverError ──错误──> DriverResult │ +│ │ │ │ +│ ├── should_retry() │ +│ └── message() │ +└──────────────────────────────────────────────────────────────┘ + ▲ ▲ ▲ ▲ + │ │ │ │ + block crate net crate display crate virtio crate + (及 kdriver、input、vsock 等所有驱动子 crate) +``` + +| 组件 | 职责 | +|------|------| +| `DeviceKind` | 枚举所有支持的设备类别(9 种),提供 `as_str()` 稳定短名 | +| `DriverError` | 统一驱动错误码,提供重试判断和日志消息 | +| `DriverResult` | 驱动操作的专用 `Result` 类型别名 | +| `Device` | 所有设备必须实现的 trait,定义设备身份元数据接口 | + +## 状态机 + +本模块为纯类型定义,无状态管理,不涉及状态机。 + +## 算法流程 + +本模块无复杂算法。核心逻辑仅为枚举匹配: + +### 错误重试判断 + +1. 调用者收到 `DriverResult::Err(e)` +2. 调用 `e.should_retry()` 判断是否为可重试错误(`WouldBlock` / `ResourceBusy`) +3. 若可重试,调用者按自身策略进行退避重试 + +### 设备分类查询 + +1. 通过 `Device::device_kind()` 获取 `DeviceKind` +2. 按 `DeviceKind` 变体分发到对应子系统处理 + +## 并发模型 + +本模块仅定义类型和 trait,无内部可变状态,无并发问题。 + +- `Device` 要求实现者满足 `Send + Sync`,确保 trait object 可跨线程共享。 +- `DeviceKind` 和 `DriverError` 均为 `Copy` 类型,天然线程安全。 + +## 设计决策 + +### 为什么用枚举而非字符串表示设备类别 + +使用 `#[repr(u8)]` 枚举而非字符串: +- 编译期穷举匹配,遗漏变体时编译器报错 +- 零堆分配,`Copy` 语义,适合热路径 +- `as_str()` 提供人类可读名称,仅在日志/调试时使用 + +### 为什么 DriverError 不实现 std::error::Error + +x-kernel 为 `no_std` 环境,无法依赖 `std::error::Error` trait。 +通过实现 `core::fmt::Display` 提供基本的错误描述能力。 + +### 为什么 Device trait 只定义三个方法 + +`Device` 故意保持最小接口,仅描述设备身份元数据: +- `name()` / `device_kind()` / `irq()` 是所有设备都需要的身份信息 +- 具体操作(读写、配置等)由各子 crate 的专用 trait 定义,以 `Device` 为 super-trait +- 避免在基础层引入不必要的抽象,保持正交性 + +### 为什么 irq() 返回 Option 而非 Result + +部分设备(如 ramdisk)不使用中断,`Option` 语义更准确: +- `None` 表示设备不使用中断(正常情况) +- `Some(irq)` 表示设备使用指定中断号 +- `Result` 的 `Err` 语义暗示操作失败,不适用于"无中断"的场景 + +### 为什么 DeviceKind 包含 Bus 变体 + +总线控制器(如 PCI 主桥)本身也是需要被驱动框架管理的设备: +- 总线驱动负责枚举和配置子设备 +- 统一纳入 `DeviceKind` 可复用设备注册和发现机制 +- 与其他设备类别享有相同的身份查询接口 diff --git a/drivers/driver_base/docs/security.md b/drivers/driver_base/docs/security.md new file mode 100644 index 0000000000000000000000000000000000000000..2280da0ff6829b7500aca1b97fa4a85782c4b51e --- /dev/null +++ b/drivers/driver_base/docs/security.md @@ -0,0 +1,83 @@ +# driver_base — 安全与可靠性分析 + +## 信任模型 + +``` +驱动子 crate(block、net、display、input、vsock、virtio、kdriver) + │ + │ safe API: DeviceKind, DriverError, DriverResult, Device + │ + v +┌──────────────────────────┐ +│ driver_base │ +│ │ +│ ┌── unsafe 边界 ──────┐ │ +│ │ (无 unsafe 代码) │ │ +│ └─────────────────────┘ │ +└──────────────────────────┘ +``` + +- **safe API 调用者**:模块仅提供纯 safe 类型定义和 trait,调用者无需额外证明安全性。 +- **unsafe API 调用者**:无 unsafe API。 + +## unsafe 代码清单 + +本模块不含任何 `unsafe` 块、`unsafe fn` 或 `unsafe impl`。 + +## 内存安全不变量 + +本模块无 `unsafe` 代码,无需维护额外的内存安全不变量。 +所有类型均为 `Copy` 或纯 trait 定义,不存在堆分配或裸指针操作。 + +## 线程安全 + +| 类型 | `Send` 条件 | `Sync` 条件 | +|------|-------------|-------------| +| `DeviceKind` | 自动 `Send`(`u8` 枚举,`Copy`) | 自动 `Sync`(`u8` 枚举,`Copy`) | +| `DriverError` | 自动 `Send`(`Copy` 枚举) | 自动 `Sync`(`Copy` 枚举) | +| `DriverResult` | 当 `T: Send` 时 `Send` | 当 `T: Sync` 时 `Sync` | +| `Device` | trait 约束 `Send + Sync` | trait 约束 `Send + Sync` | + +## 威胁分析 + +| 编号 | 威胁描述 | 影响等级 | 触发条件 | 应对措施 | +|------|----------|----------|----------|----------| +| T-01 | `Device` 实现者返回不一致的 `DeviceKind`,导致设备被错误子系统管理 | 中 | 驱动实现 bug,`device_kind()` 返回值与实际设备类型不符 | 各子 crate 在注册设备时校验 `DeviceKind` 与 trait 一致性;代码审查时重点检查 | +| T-02 | `Device::name()` 返回空字符串或无效 UTF-8 引用 | 低 | 驱动实现 bug | `name()` 返回 `&str`,Rust 保证 UTF-8 合法性;空字符串不影响安全,仅影响日志可读性 | +| T-03 | `DriverError` 新增变体后调用方未穷举处理 | 低 | 修改 `DriverError` 枚举但未更新所有 `match` | `match` 穷举检查由编译器强制执行;`should_retry()` 和 `message()` 为集中处理点,新增变体时必须更新 | + +## 故障模式与影响分析(FMEA) + +| 编号 | 故障模式 | 故障原因 | 局部影响 | 系统影响 | 严重度 | 应对措施 | +|------|----------|----------|----------|----------|--------|----------| +| F-01 | `Device` 实现返回错误的 `DeviceKind` | 驱动实现者误写 `device_kind()` 返回值 | 设备被路由到错误子系统 | 设备不可用,但不会导致内存安全问题 | 3 | 子系统注册时校验;代码审查 | +| F-02 | `irq()` 返回错误的中断号 | 驱动实现者误写中断号 | 中断路由到错误处理程序 | 可能导致设备中断丢失或错误处理 | 2 | 中断注册框架应校验 IRQ 号合法性 | +| F-03 | `should_retry()` 对新增错误变体返回 false | 新增 `DriverError` 变体后未更新 `should_retry()` | 可重试错误被当作永久失败 | 非阻塞操作提前失败 | 4 | 编译器穷举检查强制更新 `match` | +| F-04 | `message()` 对新增错误变体返回错误描述 | 新增 `DriverError` 变体后未更新 `message()` | 日志信息不准确 | 调试困难,无安全影响 | 4 | 编译器穷举检查强制更新 `match` | + +## 故障管理 + +- **错误码**:`DriverError` 枚举覆盖常见驱动故障场景,各变体语义明确。 +- **Panic 策略**:本模块无 panic 路径。 +- **故障恢复**:`should_retry()` 为调用者提供重试判断依据,具体恢复策略由调用者决定。 + +## 隐私分析 + +本模块不直接处理用户数据,不涉及隐私问题。 + +## 已知限制 + +1. `DriverError` 为固定枚举,无法携带附加上下文信息(如底层错误码、偏移量等), + 调用者如需详细错误信息需通过其他机制传递。 +2. `Device::irq()` 返回 `Option`,不支持多个中断号的设备 + (如多队列网卡),后续可能需要扩展为返回中断号切片。 + +## 审计清单 + +修改本模块时需验证: + +- [ ] 每个 `unsafe` 块均有 `SAFETY:` 注释(当前无 unsafe 代码) +- [ ] 新增 `DeviceKind` 变体后所有 `match` 穷举已更新 +- [ ] 新增 `DriverError` 变体后 `should_retry()` 和 `message()` 已更新 +- [ ] `Device` trait 变更为 breaking change,需同步所有实现者 +- [ ] 新增 panic 路径有对应的 PanicGuard 或等效保护(当前无 panic 路径) diff --git a/drivers/driver_base/src/lib.rs b/drivers/driver_base/src/lib.rs index 699c15fdd00b08d12d867402c038d80cf0cc648c..9b01484d20ea03d0af1bb91f69d6a3700cd1379f 100644 --- a/drivers/driver_base/src/lib.rs +++ b/drivers/driver_base/src/lib.rs @@ -2,24 +2,66 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Device driver interfaces used by x-kernel. It provides common traits and -//! types for implementing a device driver. +//! Device driver base interfaces for x-kernel. //! -//! You have to use this crate with the following crates for corresponding -//! device types: +//! This crate provides common traits and types for implementing a device driver. +//! It is the shared dependency of all driver sub-crates and defines the unified +//! type contract for device classification, error handling, and device identity. //! -//! - [`kdriver_block`][2]: Common traits for block storage drivers. -//! - [`kdriver_display`][3]: Common traits and types for graphics display drivers. -//! - [`net`][4]: Common traits and types for network (NIC) drivers. +//! # Core types //! -//! [2]: ../kdriver_block/index.html -//! [3]: ../kdriver_display/index.html -//! [4]: ../net/index.html +//! - [`DeviceKind`] — enumeration of all supported device categories. +//! - [`DriverError`] / [`DriverResult`] — unified error type and `Result` alias. +//! - [`Device`] — the minimal trait every device implementation must expose. +//! +//! # Companion crates +//! +//! Use the following crates for device-type-specific traits: +//! +//! - `kdriver_block`: block storage drivers. +//! - `kdriver_display`: graphics display drivers. +//! - `net`: network (NIC) drivers. +//! +//! # Example +//! +//! ``` +//! use driver_base::{Device, DeviceKind, DriverError, DriverResult}; +//! +//! struct MyDevice; +//! +//! impl Device for MyDevice { +//! fn name(&self) -> &str { +//! "my-device" +//! } +//! +//! fn device_kind(&self) -> DeviceKind { +//! DeviceKind::Char +//! } +//! } +//! +//! let dev = MyDevice; +//! assert_eq!(dev.name(), "my-device"); +//! assert_eq!(dev.device_kind(), DeviceKind::Char); +//! assert_eq!(dev.irq(), None); +//! ``` #![no_std] #![allow(rustdoc::broken_intra_doc_links)] /// All supported device kinds. +/// +/// Each variant corresponds to a device category in x-kernel. The `#[repr(u8)]` +/// layout ensures a compact, `Copy`-friendly representation suitable for hot +/// paths and FFI boundaries. +/// +/// # Example +/// +/// ``` +/// use driver_base::DeviceKind; +/// +/// let kind = DeviceKind::Net; +/// assert_eq!(kind.as_str(), "net"); +/// ``` #[repr(u8)] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum DeviceKind { @@ -29,7 +71,7 @@ pub enum DeviceKind { Char, /// Network device (e.g., ethernet card). Net, - /// Graphic display device (e.g., GPU) + /// Graphic display device (e.g., GPU). Display, /// Input device (e.g., keyboard, mouse). Input, @@ -42,7 +84,24 @@ pub enum DeviceKind { } impl DeviceKind { - /// Stable short name for the device category. + /// Returns a stable short name for the device category. + /// + /// The returned string is suitable for logging and display purposes. + /// It remains stable across crate versions. + /// + /// # Returns + /// + /// A `&'static str` identifying the category (e.g., `"block"`, `"net"`). + /// + /// # Example + /// + /// ``` + /// use driver_base::DeviceKind; + /// + /// assert_eq!(DeviceKind::Block.as_str(), "block"); + /// assert_eq!(DeviceKind::Fs9p.as_str(), "fs9p"); + /// assert_eq!(DeviceKind::Bus.as_str(), "bus"); + /// ``` pub const fn as_str(self) -> &'static str { use DeviceKind::*; @@ -60,6 +119,20 @@ impl DeviceKind { } /// The error type for driver operation failures. +/// +/// Covers common failure modes shared across all driver sub-crates. Each variant +/// maps to a distinct category; callers use [`should_retry`](DriverError::should_retry) +/// to decide whether to re-attempt the operation. +/// +/// # Example +/// +/// ``` +/// use driver_base::DriverError; +/// +/// let err = DriverError::WouldBlock; +/// assert!(err.should_retry()); +/// assert_eq!(err.message(), "Try again"); +/// ``` #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum DriverError { /// An entity already exists. @@ -81,12 +154,45 @@ pub enum DriverError { } impl DriverError { - /// Whether the caller may retry the operation later. + /// Returns whether the caller may retry the operation later. + /// + /// Only [`WouldBlock`](DriverError::WouldBlock) and + /// [`ResourceBusy`](DriverError::ResourceBusy) are considered retryable. + /// All other variants indicate permanent failures for the current request. + /// + /// # Returns + /// + /// `true` if the error is transient and the operation may succeed on retry. + /// + /// # Example + /// + /// ``` + /// use driver_base::DriverError; + /// + /// assert!(DriverError::WouldBlock.should_retry()); + /// assert!(DriverError::ResourceBusy.should_retry()); + /// assert!(!DriverError::Io.should_retry()); + /// ``` pub const fn should_retry(self) -> bool { matches!(self, Self::WouldBlock | Self::ResourceBusy) } - /// Stable error message for display/logging. + /// Returns a stable human-readable message for the error. + /// + /// Suitable for logging and diagnostics. The message is guaranteed to + /// remain stable across crate versions. + /// + /// # Returns + /// + /// A `&'static str` describing the error. + /// + /// # Example + /// + /// ``` + /// use driver_base::DriverError; + /// + /// assert_eq!(DriverError::NoMemory.message(), "Not enough memory"); + /// ``` pub const fn message(self) -> &'static str { use DriverError::*; @@ -110,6 +216,21 @@ impl core::fmt::Display for DriverError { } /// A specialized `Result` type for device operations. +/// +/// This alias eliminates the need to write `Result` throughout +/// the driver subsystem. The default success type is `()`. +/// +/// # Example +/// +/// ``` +/// use driver_base::{DriverError, DriverResult}; +/// +/// fn try_read() -> DriverResult> { +/// Err(DriverError::WouldBlock) +/// } +/// +/// assert!(try_read().is_err()); +/// ``` pub type DriverResult = core::result::Result; /// Common metadata that every device implementation must expose. @@ -119,14 +240,60 @@ pub type DriverResult = core::result::Result; /// (block read/write, net send/recv, ...) live in the per-category /// sub-traits in their respective crates (`block::BlockDevice`, /// `net::NetDevice`, etc.) and require this trait as a super-trait. +/// +/// # Requirements +/// +/// Implementors must be `Send + Sync` so that trait objects can be shared +/// across threads. +/// +/// # Example +/// +/// ``` +/// use driver_base::{Device, DeviceKind}; +/// +/// struct SerialPort; +/// +/// impl Device for SerialPort { +/// fn name(&self) -> &str { +/// "serial0" +/// } +/// +/// fn device_kind(&self) -> DeviceKind { +/// DeviceKind::Char +/// } +/// +/// fn irq(&self) -> Option { +/// Some(4) +/// } +/// } +/// +/// let dev = SerialPort; +/// assert_eq!(dev.name(), "serial0"); +/// assert_eq!(dev.device_kind().as_str(), "char"); +/// assert_eq!(dev.irq(), Some(4)); +/// ``` pub trait Device: Send + Sync { /// The name of the device. + /// + /// The name should be unique within the system and stable across reboots + /// for the same hardware configuration. fn name(&self) -> &str; - /// The kind of the device. + /// Returns the kind (category) of the device. + /// + /// Used by the driver framework to route the device to the appropriate + /// subsystem for management. fn device_kind(&self) -> DeviceKind; - /// The IRQ number of the device, if applicable. + /// Returns the IRQ number of the device, if applicable. + /// + /// Devices that do not use interrupts (e.g., ramdisk) should return + /// `None`, which is the default. + /// + /// # Returns + /// + /// - `Some(irq)` if the device uses an interrupt. + /// - `None` if the device is interrupt-free (default). fn irq(&self) -> Option { None }