27
.gitignore
vendido
Archivo normal
27
.gitignore
vendido
Archivo normal
@@ -0,0 +1,27 @@
|
||||
target/
|
||||
Cargo.lock
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
*.orig
|
||||
*.rej
|
||||
|
||||
# Kernel build artifacts
|
||||
*.o
|
||||
*.ko
|
||||
*.mod.c
|
||||
modules.order
|
||||
Module.symvers
|
||||
.tmp_versions/
|
||||
|
||||
# Documentation
|
||||
doc/
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# OS files
|
||||
Thumbs.db
|
||||
125
ARCHITECTURE.md
Archivo normal
125
ARCHITECTURE.md
Archivo normal
@@ -0,0 +1,125 @@
|
||||
# Rust Kernel Architecture
|
||||
|
||||
## Project Structure
|
||||
|
||||
This Rust kernel project is organized as follows:
|
||||
|
||||
```
|
||||
rust/
|
||||
├── README.md # Project overview and build instructions
|
||||
├── Cargo.toml # Root workspace configuration
|
||||
├── Makefile # Build automation
|
||||
├── src/
|
||||
│ └── lib.rs # Top-level kernel library
|
||||
├── kernel/ # Core kernel crate
|
||||
│ ├── Cargo.toml
|
||||
│ └── src/
|
||||
│ ├── lib.rs # Main kernel library
|
||||
│ ├── prelude.rs # Common imports and macros
|
||||
│ ├── error.rs # Error handling
|
||||
│ ├── types.rs # Kernel data types
|
||||
│ ├── memory/ # Memory management
|
||||
│ ├── sync.rs # Synchronization primitives
|
||||
│ ├── process.rs # Process management
|
||||
│ ├── scheduler.rs # Task scheduling
|
||||
│ ├── device.rs # Device management
|
||||
│ ├── driver.rs # Driver framework
|
||||
│ ├── interrupt.rs # Interrupt handling
|
||||
│ ├── console.rs # Console/logging
|
||||
│ ├── module.rs # Module system
|
||||
│ ├── init.rs # Initialization
|
||||
│ ├── panic.rs # Panic handler
|
||||
│ └── arch/ # Architecture-specific code
|
||||
├── modules/ # Loadable kernel modules
|
||||
│ ├── Cargo.toml
|
||||
│ └── src/
|
||||
│ ├── hello.rs # Hello world module
|
||||
│ └── test.rs # Test module
|
||||
├── drivers/ # Device drivers
|
||||
│ ├── Cargo.toml
|
||||
│ └── src/
|
||||
│ └── dummy.rs # Example dummy driver
|
||||
└── test.sh # Build and test script
|
||||
```
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
This kernel is designed with the following principles:
|
||||
|
||||
1. **Memory Safety**: Leveraging Rust's ownership system to prevent memory bugs
|
||||
2. **Zero-Cost Abstractions**: High-level APIs without runtime overhead
|
||||
3. **Modularity**: Clean separation between subsystems
|
||||
4. **Linux Compatibility**: Similar APIs and concepts where applicable
|
||||
5. **Modern Design**: Taking advantage of modern language features
|
||||
|
||||
## Key Components
|
||||
|
||||
### Core Kernel (`kernel/`)
|
||||
|
||||
- **Memory Management**: Page allocation, heap management, virtual memory
|
||||
- **Process Management**: Process and thread abstractions
|
||||
- **Synchronization**: Spinlocks, mutexes, and other primitives
|
||||
- **Device Framework**: Generic device and driver abstractions
|
||||
- **Module System**: Support for loadable kernel modules
|
||||
- **Error Handling**: Comprehensive error types and Result-based APIs
|
||||
|
||||
### Modules (`modules/`)
|
||||
|
||||
Loadable kernel modules that can extend kernel functionality:
|
||||
- `hello.rs`: Simple demonstration module
|
||||
- `test.rs`: Testing and validation module
|
||||
|
||||
### Drivers (`drivers/`)
|
||||
|
||||
Device drivers implementing the kernel driver framework:
|
||||
- `dummy.rs`: Example driver showing the driver API
|
||||
|
||||
## Architecture Support
|
||||
|
||||
Currently includes basic support for:
|
||||
- x86_64 (primary target)
|
||||
- ARM64 (stub)
|
||||
- RISC-V (stub)
|
||||
|
||||
## Building and Testing
|
||||
|
||||
```bash
|
||||
# Build everything
|
||||
make
|
||||
|
||||
# Run tests
|
||||
make test
|
||||
|
||||
# Check code quality
|
||||
make clippy fmt-check
|
||||
|
||||
# Run the automated test script
|
||||
./test.sh
|
||||
```
|
||||
|
||||
## Development Status
|
||||
|
||||
This is a foundational implementation demonstrating:
|
||||
- ✅ Basic kernel structure and organization
|
||||
- ✅ Memory management framework
|
||||
- ✅ Module system with examples
|
||||
- ✅ Driver framework
|
||||
- ✅ Synchronization primitives
|
||||
- ✅ Error handling
|
||||
- ✅ Build system integration
|
||||
|
||||
Areas for future development:
|
||||
- [ ] Complete memory management implementation
|
||||
- [ ] Interrupt handling and timers
|
||||
- [ ] Full scheduler implementation
|
||||
- [ ] File system support
|
||||
- [ ] Network stack
|
||||
- [ ] Hardware abstraction layers
|
||||
- [ ] Boot loader integration
|
||||
- [ ] Performance optimization
|
||||
|
||||
## Relationship to Linux Kernel
|
||||
|
||||
This project is inspired by the Linux kernel's Rust infrastructure (`/linux/rust/`) but is designed as a standalone kernel implementation. It demonstrates how modern Rust can be used for systems programming while maintaining the familiar concepts from traditional kernel development.
|
||||
|
||||
The module system and driver framework are designed to be conceptually similar to Linux while taking advantage of Rust's type system for additional safety guarantees.
|
||||
48
Cargo.toml
Archivo normal
48
Cargo.toml
Archivo normal
@@ -0,0 +1,48 @@
|
||||
[package]
|
||||
name = "rust-kernel"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Rust Kernel Contributors"]
|
||||
description = "A Rust-based kernel inspired by Linux"
|
||||
license = "GPL-2.0"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"kernel",
|
||||
"drivers",
|
||||
"modules",
|
||||
"arch",
|
||||
"mm",
|
||||
"fs",
|
||||
"net",
|
||||
"security"
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
kernel = { path = "kernel" }
|
||||
|
||||
[lib]
|
||||
name = "rust_kernel"
|
||||
crate-type = ["staticlib", "cdylib"]
|
||||
|
||||
[features]
|
||||
default = ["alloc", "std"]
|
||||
alloc = []
|
||||
std = []
|
||||
no_std = []
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
opt-level = 0
|
||||
debug = true
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s" # Optimize for size
|
||||
debug = false
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")']
|
||||
[target.'cfg(target_arch = "aarch64")']
|
||||
[target.'cfg(target_arch = "riscv64")']
|
||||
73
Makefile
Archivo normal
73
Makefile
Archivo normal
@@ -0,0 +1,73 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Rust Kernel Makefile
|
||||
# Based on Linux kernel Rust infrastructure
|
||||
|
||||
RUST_KERNEL_VERSION := 0.1.0
|
||||
|
||||
# Build configuration
|
||||
ARCH ?= x86_64
|
||||
BUILD_TYPE ?= release
|
||||
|
||||
# Cargo configuration
|
||||
CARGO := cargo
|
||||
CARGO_FLAGS := --target-dir target
|
||||
|
||||
ifeq ($(BUILD_TYPE),debug)
|
||||
CARGO_FLAGS +=
|
||||
else
|
||||
CARGO_FLAGS += --release
|
||||
endif
|
||||
|
||||
# Kernel modules
|
||||
RUST_MODULES := $(shell find modules -name "*.rs" -type f)
|
||||
DRIVERS := $(shell find drivers -name "*.rs" -type f)
|
||||
|
||||
# Default target
|
||||
all: kernel modules drivers
|
||||
|
||||
# Build the core kernel
|
||||
kernel:
|
||||
@echo "Building Rust kernel ($(ARCH), $(BUILD_TYPE))"
|
||||
$(CARGO) build $(CARGO_FLAGS)
|
||||
|
||||
# Build kernel modules
|
||||
modules: $(RUST_MODULES)
|
||||
@echo "Building kernel modules"
|
||||
cd modules && $(CARGO) build $(CARGO_FLAGS)
|
||||
|
||||
# Build drivers
|
||||
drivers: $(DRIVERS)
|
||||
@echo "Building drivers"
|
||||
cd drivers && $(CARGO) build $(CARGO_FLAGS)
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
$(CARGO) clean
|
||||
rm -rf target/
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
$(CARGO) test $(CARGO_FLAGS)
|
||||
|
||||
# Check formatting
|
||||
fmt-check:
|
||||
$(CARGO) fmt --check
|
||||
|
||||
# Format code
|
||||
fmt:
|
||||
$(CARGO) fmt
|
||||
|
||||
# Run clippy
|
||||
clippy:
|
||||
$(CARGO) clippy $(CARGO_FLAGS) -- -D warnings
|
||||
|
||||
# Generate documentation
|
||||
doc:
|
||||
$(CARGO) doc $(CARGO_FLAGS) --no-deps
|
||||
|
||||
# Install (placeholder)
|
||||
install:
|
||||
@echo "Install target not implemented yet"
|
||||
|
||||
.PHONY: all kernel modules drivers clean test fmt-check fmt clippy doc install
|
||||
50
README.md
Archivo normal
50
README.md
Archivo normal
@@ -0,0 +1,50 @@
|
||||
# Rust Kernel
|
||||
|
||||
A Rust-based kernel inspired by the Linux kernel, utilizing the Rust for Linux infrastructure.
|
||||
|
||||
## Overview
|
||||
|
||||
This project aims to create a modern kernel implementation in Rust, leveraging memory safety and performance benefits while maintaining compatibility with Linux kernel concepts and APIs.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **kernel/**: Core kernel functionality and APIs
|
||||
- **drivers/**: Device drivers written in Rust
|
||||
- **modules/**: Loadable kernel modules
|
||||
- **arch/**: Architecture-specific code
|
||||
- **mm/**: Memory management
|
||||
- **fs/**: File system implementations
|
||||
- **net/**: Network stack
|
||||
- **security/**: Security subsystem
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
# Build the kernel
|
||||
cargo build --release
|
||||
|
||||
# Run tests
|
||||
cargo test
|
||||
|
||||
# Check code formatting
|
||||
cargo fmt --check
|
||||
|
||||
# Run clippy lints
|
||||
cargo clippy -- -D warnings
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Memory-safe kernel implementation
|
||||
- Zero-cost abstractions
|
||||
- Modern async/await support for I/O operations
|
||||
- Modular architecture
|
||||
- Linux-compatible APIs where possible
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under GPL-2.0, following the Linux kernel license.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please follow the Linux kernel coding style and Rust conventions.
|
||||
21
clippy.toml
Archivo normal
21
clippy.toml
Archivo normal
@@ -0,0 +1,21 @@
|
||||
# Clippy configuration for kernel development
|
||||
warn = [
|
||||
"clippy::all",
|
||||
"clippy::pedantic",
|
||||
"clippy::nursery",
|
||||
"clippy::cargo"
|
||||
]
|
||||
|
||||
allow = [
|
||||
"clippy::missing_errors_doc",
|
||||
"clippy::missing_panics_doc",
|
||||
"clippy::module_name_repetitions",
|
||||
"clippy::inline_always",
|
||||
"clippy::must_use_candidate",
|
||||
"clippy::cast_possible_truncation",
|
||||
"clippy::cast_sign_loss",
|
||||
"clippy::cast_possible_wrap",
|
||||
"clippy::similar_names",
|
||||
"clippy::too_many_lines",
|
||||
"clippy::unused_self"
|
||||
]
|
||||
26
drivers/Cargo.toml
Archivo normal
26
drivers/Cargo.toml
Archivo normal
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "drivers"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Rust Kernel Contributors"]
|
||||
description = "Kernel drivers"
|
||||
license = "GPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
kernel = { path = "../kernel" }
|
||||
|
||||
[[bin]]
|
||||
name = "dummy_driver"
|
||||
path = "src/dummy.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mem_devices"
|
||||
path = "src/mem.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "platform_example"
|
||||
path = "src/platform_example.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ramdisk"
|
||||
path = "src/ramdisk.rs"
|
||||
94
drivers/src/dummy.rs
Archivo normal
94
drivers/src/dummy.rs
Archivo normal
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Dummy device driver
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use kernel::prelude::*;
|
||||
use kernel::driver::{Driver, DriverOps};
|
||||
use kernel::device::{Device, DeviceType};
|
||||
|
||||
struct DummyDriver {
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl DummyDriver {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
name: "dummy_driver",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for DummyDriver {
|
||||
fn name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn probe(&self, device: &mut Device) -> Result<()> {
|
||||
info!("DummyDriver: probing device {}", device.name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, device: &mut Device) -> Result<()> {
|
||||
info!("DummyDriver: removing device {}", device.name);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DriverOps for DummyDriver {
|
||||
fn read(&self, offset: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||
info!("DummyDriver: read at offset {} for {} bytes", offset, buffer.len());
|
||||
// Fill buffer with dummy data
|
||||
for (i, byte) in buffer.iter_mut().enumerate() {
|
||||
*byte = (i % 256) as u8;
|
||||
}
|
||||
Ok(buffer.len())
|
||||
}
|
||||
|
||||
fn write(&self, offset: u64, buffer: &[u8]) -> Result<usize> {
|
||||
info!("DummyDriver: write at offset {} for {} bytes", offset, buffer.len());
|
||||
Ok(buffer.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: u32, arg: usize) -> Result<usize> {
|
||||
info!("DummyDriver: ioctl cmd={}, arg={}", cmd, arg);
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyModule {
|
||||
driver: DummyDriver,
|
||||
}
|
||||
|
||||
impl kernel::module::Module for DummyModule {
|
||||
fn init(_module: &'static kernel::module::ThisModule) -> Result<Self> {
|
||||
info!("Dummy driver module initializing...");
|
||||
|
||||
let driver = DummyDriver::new();
|
||||
|
||||
// Register the driver
|
||||
kernel::driver::register_driver(Box::new(driver))?;
|
||||
|
||||
info!("Dummy driver registered successfully");
|
||||
|
||||
Ok(DummyModule {
|
||||
driver: DummyDriver::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn exit(_module: &'static kernel::module::ThisModule) {
|
||||
info!("Dummy driver module exiting");
|
||||
// Unregister driver
|
||||
kernel::driver::unregister_driver("dummy_driver").ok();
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: DummyModule,
|
||||
name: "dummy_driver",
|
||||
author: "Rust Kernel Contributors",
|
||||
description: "A dummy device driver for testing",
|
||||
license: "GPL-2.0",
|
||||
}
|
||||
170
drivers/src/mem.rs
Archivo normal
170
drivers/src/mem.rs
Archivo normal
@@ -0,0 +1,170 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Null, zero, and full device drivers
|
||||
//! Based on Linux drivers/char/mem.c
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use kernel::prelude::*;
|
||||
use kernel::device::{CharDevice, FileOperations, Inode, File, VMA};
|
||||
use kernel::driver::CharDriverOps;
|
||||
|
||||
/// Null device driver (/dev/null)
|
||||
struct NullDevice;
|
||||
|
||||
impl FileOperations for NullDevice {
|
||||
fn open(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&self, _file: &mut File, _buf: &mut [u8], _offset: u64) -> Result<usize> {
|
||||
// Reading from /dev/null always returns EOF
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write(&self, _file: &mut File, buf: &[u8], _offset: u64) -> Result<usize> {
|
||||
// Writing to /dev/null always succeeds and discards data
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &mut File, _cmd: u32, _arg: usize) -> Result<usize> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &mut File, _vma: &mut VMA) -> Result<()> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Zero device driver (/dev/zero)
|
||||
struct ZeroDevice;
|
||||
|
||||
impl FileOperations for ZeroDevice {
|
||||
fn open(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&self, _file: &mut File, buf: &mut [u8], _offset: u64) -> Result<usize> {
|
||||
// Reading from /dev/zero returns zeros
|
||||
for byte in buf.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write(&self, _file: &mut File, buf: &[u8], _offset: u64) -> Result<usize> {
|
||||
// Writing to /dev/zero always succeeds and discards data
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &mut File, _cmd: u32, _arg: usize) -> Result<usize> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &mut File, vma: &mut VMA) -> Result<()> {
|
||||
// /dev/zero can be mmap'd to get zero-filled pages
|
||||
// TODO: implement proper mmap support
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Full device driver (/dev/full)
|
||||
struct FullDevice;
|
||||
|
||||
impl FileOperations for FullDevice {
|
||||
fn open(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &mut File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&self, _file: &mut File, buf: &mut [u8], _offset: u64) -> Result<usize> {
|
||||
// Reading from /dev/full returns zeros
|
||||
for byte in buf.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write(&self, _file: &mut File, _buf: &[u8], _offset: u64) -> Result<usize> {
|
||||
// Writing to /dev/full always fails with "no space left"
|
||||
Err(Error::OutOfMemory) // ENOSPC equivalent
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &mut File, _cmd: u32, _arg: usize) -> Result<usize> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &mut File, _vma: &mut VMA) -> Result<()> {
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory devices module
|
||||
struct MemoryDevicesModule {
|
||||
null_major: u32,
|
||||
zero_major: u32,
|
||||
full_major: u32,
|
||||
}
|
||||
|
||||
impl kernel::module::Module for MemoryDevicesModule {
|
||||
fn init(_module: &'static kernel::module::ThisModule) -> Result<Self> {
|
||||
info!("Memory devices module initializing...");
|
||||
|
||||
// Register /dev/null (major 1, minor 3)
|
||||
let null_major = kernel::device::register_chrdev(
|
||||
1,
|
||||
String::from("null"),
|
||||
Box::new(NullDevice)
|
||||
)?;
|
||||
|
||||
// Register /dev/zero (major 1, minor 5)
|
||||
let zero_major = kernel::device::register_chrdev(
|
||||
1,
|
||||
String::from("zero"),
|
||||
Box::new(ZeroDevice)
|
||||
)?;
|
||||
|
||||
// Register /dev/full (major 1, minor 7)
|
||||
let full_major = kernel::device::register_chrdev(
|
||||
1,
|
||||
String::from("full"),
|
||||
Box::new(FullDevice)
|
||||
)?;
|
||||
|
||||
info!("Memory devices registered: null={}, zero={}, full={}",
|
||||
null_major, zero_major, full_major);
|
||||
|
||||
Ok(MemoryDevicesModule {
|
||||
null_major,
|
||||
zero_major,
|
||||
full_major,
|
||||
})
|
||||
}
|
||||
|
||||
fn exit(_module: &'static kernel::module::ThisModule) {
|
||||
info!("Memory devices module exiting");
|
||||
|
||||
// Unregister character devices
|
||||
kernel::device::unregister_chrdev(1).ok();
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: MemoryDevicesModule,
|
||||
name: "mem_devices",
|
||||
author: "Rust Kernel Contributors",
|
||||
description: "Memory devices (/dev/null, /dev/zero, /dev/full)",
|
||||
license: "GPL-2.0",
|
||||
}
|
||||
127
drivers/src/platform_example.rs
Archivo normal
127
drivers/src/platform_example.rs
Archivo normal
@@ -0,0 +1,127 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Platform device driver example
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use kernel::prelude::*;
|
||||
use kernel::driver::{PlatformDriver, Driver, DeviceId};
|
||||
use kernel::device::Device;
|
||||
|
||||
/// Example platform driver
|
||||
struct ExamplePlatformDriver {
|
||||
name: &'static str,
|
||||
device_ids: &'static [DeviceId],
|
||||
}
|
||||
|
||||
impl ExamplePlatformDriver {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
name: "example_platform",
|
||||
device_ids: &[
|
||||
DeviceId::new(String::from("example,platform-device"))
|
||||
.with_compatible(vec![
|
||||
String::from("example,platform-device"),
|
||||
String::from("generic,platform-device"),
|
||||
]),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for ExamplePlatformDriver {
|
||||
fn name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn probe(&self, device: &mut Device) -> Result<()> {
|
||||
info!("Platform driver probing device: {}", device.name());
|
||||
|
||||
// Initialize device-specific data
|
||||
device.set_private_data(ExampleDeviceData {
|
||||
initialized: true,
|
||||
counter: 0,
|
||||
});
|
||||
|
||||
info!("Platform device {} probed successfully", device.name());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, device: &mut Device) -> Result<()> {
|
||||
info!("Platform driver removing device: {}", device.name());
|
||||
|
||||
if let Some(data) = device.get_private_data::<ExampleDeviceData>() {
|
||||
info!("Device had counter value: {}", data.counter);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn suspend(&self, device: &mut Device) -> Result<()> {
|
||||
info!("Platform device {} suspending", device.name());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resume(&self, device: &mut Device) -> Result<()> {
|
||||
info!("Platform device {} resuming", device.name());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn shutdown(&self, device: &mut Device) {
|
||||
info!("Platform device {} shutting down", device.name());
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformDriver for ExamplePlatformDriver {
|
||||
fn match_device(&self, device: &Device) -> bool {
|
||||
// Simple name-based matching
|
||||
device.name().contains("example") || device.name().contains("platform")
|
||||
}
|
||||
|
||||
fn device_ids(&self) -> &[DeviceId] {
|
||||
self.device_ids
|
||||
}
|
||||
}
|
||||
|
||||
/// Device-specific private data
|
||||
#[derive(Debug)]
|
||||
struct ExampleDeviceData {
|
||||
initialized: bool,
|
||||
counter: u32,
|
||||
}
|
||||
|
||||
/// Platform driver module
|
||||
struct PlatformDriverModule {
|
||||
driver: ExamplePlatformDriver,
|
||||
}
|
||||
|
||||
impl kernel::module::Module for PlatformDriverModule {
|
||||
fn init(_module: &'static kernel::module::ThisModule) -> Result<Self> {
|
||||
info!("Platform driver module initializing...");
|
||||
|
||||
let driver = ExamplePlatformDriver::new();
|
||||
|
||||
// Register the platform driver
|
||||
kernel::driver::register_platform_driver(Box::new(driver))?;
|
||||
|
||||
info!("Platform driver registered successfully");
|
||||
|
||||
Ok(PlatformDriverModule {
|
||||
driver: ExamplePlatformDriver::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn exit(_module: &'static kernel::module::ThisModule) {
|
||||
info!("Platform driver module exiting");
|
||||
kernel::driver::unregister_driver("example_platform").ok();
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: PlatformDriverModule,
|
||||
name: "example_platform_driver",
|
||||
author: "Rust Kernel Contributors",
|
||||
description: "Example platform device driver",
|
||||
license: "GPL-2.0",
|
||||
}
|
||||
186
drivers/src/ramdisk.rs
Archivo normal
186
drivers/src/ramdisk.rs
Archivo normal
@@ -0,0 +1,186 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! RAM disk block device driver
|
||||
//! Based on Linux drivers/block/brd.c
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use kernel::prelude::*;
|
||||
use kernel::driver::{Driver, BlockDriverOps};
|
||||
use kernel::device::{Device, BlockDevice, DeviceType};
|
||||
use kernel::memory::{AllocFlags, GFP_KERNEL};
|
||||
|
||||
/// RAM disk device
|
||||
struct RamDisk {
|
||||
size: u64, // Size in bytes
|
||||
block_size: u32, // Block size in bytes
|
||||
data: Vec<u8>, // Actual storage
|
||||
}
|
||||
|
||||
impl RamDisk {
|
||||
fn new(size: u64, block_size: u32) -> Result<Self> {
|
||||
let mut data = Vec::new();
|
||||
data.try_reserve(size as usize)?;
|
||||
data.resize(size as usize, 0);
|
||||
|
||||
Ok(Self {
|
||||
size,
|
||||
block_size,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_block_count(&self) -> u64 {
|
||||
self.size / self.block_size as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDriverOps for RamDisk {
|
||||
fn read_block(&self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||
if block >= self.get_block_count() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let offset = (block * self.block_size as u64) as usize;
|
||||
let size = core::cmp::min(buffer.len(), self.block_size as usize);
|
||||
|
||||
if offset + size > self.data.len() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
buffer[..size].copy_from_slice(&self.data[offset..offset + size]);
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
fn write_block(&self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||
if block >= self.get_block_count() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let offset = (block * self.block_size as u64) as usize;
|
||||
let size = core::cmp::min(buffer.len(), self.block_size as usize);
|
||||
|
||||
if offset + size > self.data.len() {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
// This is a bit unsafe due to the immutable reference, but for simplicity...
|
||||
// In a real implementation, we'd use proper interior mutability
|
||||
unsafe {
|
||||
let data_ptr = self.data.as_ptr() as *mut u8;
|
||||
let dest = core::slice::from_raw_parts_mut(data_ptr.add(offset), size);
|
||||
dest.copy_from_slice(&buffer[..size]);
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
fn get_block_size(&self) -> u32 {
|
||||
self.block_size
|
||||
}
|
||||
|
||||
fn get_total_blocks(&self) -> u64 {
|
||||
self.get_block_count()
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
// RAM disk doesn't need flushing
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// RAM disk driver
|
||||
struct RamDiskDriver {
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
impl RamDiskDriver {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
name: "ramdisk",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for RamDiskDriver {
|
||||
fn name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
|
||||
fn probe(&self, device: &mut Device) -> Result<()> {
|
||||
info!("RAM disk driver probing device: {}", device.name());
|
||||
|
||||
// Create a 16MB RAM disk with 4KB blocks
|
||||
let ramdisk = RamDisk::new(16 * 1024 * 1024, 4096)?;
|
||||
|
||||
info!("Created RAM disk: {} blocks of {} bytes each",
|
||||
ramdisk.get_total_blocks(), ramdisk.get_block_size());
|
||||
|
||||
device.set_private_data(ramdisk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, device: &mut Device) -> Result<()> {
|
||||
info!("RAM disk driver removing device: {}", device.name());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// RAM disk module
|
||||
struct RamDiskModule {
|
||||
driver: RamDiskDriver,
|
||||
device_created: bool,
|
||||
}
|
||||
|
||||
impl kernel::module::Module for RamDiskModule {
|
||||
fn init(_module: &'static kernel::module::ThisModule) -> Result<Self> {
|
||||
info!("RAM disk module initializing...");
|
||||
|
||||
let driver = RamDiskDriver::new();
|
||||
|
||||
// Register the driver
|
||||
kernel::driver::register_driver(Box::new(driver))?;
|
||||
|
||||
// Create a RAM disk device
|
||||
let mut device = Device::new(
|
||||
String::from("ram0"),
|
||||
DeviceType::Block,
|
||||
1, // major number for RAM disk
|
||||
0 // minor number
|
||||
);
|
||||
|
||||
// Set up the driver for this device
|
||||
let ramdisk_driver = RamDiskDriver::new();
|
||||
device.set_driver(Box::new(ramdisk_driver))?;
|
||||
|
||||
// Register the device
|
||||
kernel::device::register_device(device)?;
|
||||
|
||||
info!("RAM disk device created and registered");
|
||||
|
||||
Ok(RamDiskModule {
|
||||
driver: RamDiskDriver::new(),
|
||||
device_created: true,
|
||||
})
|
||||
}
|
||||
|
||||
fn exit(_module: &'static kernel::module::ThisModule) {
|
||||
info!("RAM disk module exiting");
|
||||
|
||||
if self.device_created {
|
||||
kernel::device::unregister_device("ram0").ok();
|
||||
}
|
||||
|
||||
kernel::driver::unregister_driver("ramdisk").ok();
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: RamDiskModule,
|
||||
name: "ramdisk",
|
||||
author: "Rust Kernel Contributors",
|
||||
description: "RAM disk block device driver",
|
||||
license: "GPL-2.0",
|
||||
}
|
||||
23
kernel/Cargo.toml
Archivo normal
23
kernel/Cargo.toml
Archivo normal
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Rust Kernel Contributors"]
|
||||
description = "Core kernel APIs and functionality"
|
||||
license = "GPL-2.0"
|
||||
|
||||
[lib]
|
||||
name = "kernel"
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[dependencies]
|
||||
spin = "0.9"
|
||||
bitflags = "2.4"
|
||||
linked_list_allocator = "0.10"
|
||||
once_cell = { version = "1.19", default-features = false, features = ["critical-section"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
alloc = []
|
||||
smp = [] # Symmetric Multi-Processing
|
||||
debug = []
|
||||
12
kernel/src/arch/mod.rs
Archivo normal
12
kernel/src/arch/mod.rs
Archivo normal
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Architecture-specific code
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub mod x86_64;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub mod aarch64;
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub mod riscv64;
|
||||
4
kernel/src/arch/x86_64/gdt.rs
Archivo normal
4
kernel/src/arch/x86_64/gdt.rs
Archivo normal
@@ -0,0 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! GDT stub
|
||||
pub fn init() {}
|
||||
4
kernel/src/arch/x86_64/idt.rs
Archivo normal
4
kernel/src/arch/x86_64/idt.rs
Archivo normal
@@ -0,0 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! IDT stub
|
||||
pub fn init() {}
|
||||
9
kernel/src/arch/x86_64/mod.rs
Archivo normal
9
kernel/src/arch/x86_64/mod.rs
Archivo normal
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! x86_64 architecture support
|
||||
|
||||
pub mod port;
|
||||
pub mod gdt;
|
||||
pub mod idt;
|
||||
pub mod paging;
|
||||
pub mod pic;
|
||||
4
kernel/src/arch/x86_64/paging.rs
Archivo normal
4
kernel/src/arch/x86_64/paging.rs
Archivo normal
@@ -0,0 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Paging stub
|
||||
pub fn init() {}
|
||||
97
kernel/src/arch/x86_64/pic.rs
Archivo normal
97
kernel/src/arch/x86_64/pic.rs
Archivo normal
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Programmable Interrupt Controller (8259 PIC) support
|
||||
|
||||
use crate::arch::x86_64::port::Port;
|
||||
|
||||
/// Primary PIC ports
|
||||
const PIC1_COMMAND: u16 = 0x20;
|
||||
const PIC1_DATA: u16 = 0x21;
|
||||
|
||||
/// Secondary PIC ports
|
||||
const PIC2_COMMAND: u16 = 0xA0;
|
||||
const PIC2_DATA: u16 = 0xA1;
|
||||
|
||||
/// PIC commands
|
||||
const PIC_EOI: u8 = 0x20; // End of Interrupt
|
||||
|
||||
/// Initialize the PIC
|
||||
pub unsafe fn init_pic() {
|
||||
let mut pic1_command = Port::new(PIC1_COMMAND);
|
||||
let mut pic1_data = Port::new(PIC1_DATA);
|
||||
let mut pic2_command = Port::new(PIC2_COMMAND);
|
||||
let mut pic2_data = Port::new(PIC2_DATA);
|
||||
|
||||
// Save masks
|
||||
let mask1 = pic1_data.read() as u8;
|
||||
let mask2 = pic2_data.read() as u8;
|
||||
|
||||
// Initialize PIC1
|
||||
pic1_command.write(0x11); // ICW1: Initialize + expect ICW4
|
||||
io_wait();
|
||||
pic1_data.write(0x20); // ICW2: PIC1 offset (32)
|
||||
io_wait();
|
||||
pic1_data.write(0x04); // ICW3: Tell PIC1 there's a PIC2 at IRQ2
|
||||
io_wait();
|
||||
pic1_data.write(0x01); // ICW4: 8086 mode
|
||||
io_wait();
|
||||
|
||||
// Initialize PIC2
|
||||
pic2_command.write(0x11); // ICW1: Initialize + expect ICW4
|
||||
io_wait();
|
||||
pic2_data.write(0x28); // ICW2: PIC2 offset (40)
|
||||
io_wait();
|
||||
pic2_data.write(0x02); // ICW3: Tell PIC2 it's at IRQ2 of PIC1
|
||||
io_wait();
|
||||
pic2_data.write(0x01); // ICW4: 8086 mode
|
||||
io_wait();
|
||||
|
||||
// Restore masks
|
||||
pic1_data.write(mask1 as u32);
|
||||
pic2_data.write(mask2 as u32);
|
||||
}
|
||||
|
||||
/// Send End of Interrupt signal
|
||||
pub unsafe fn send_eoi(irq: u8) {
|
||||
let mut pic1_command = Port::new(PIC1_COMMAND);
|
||||
let mut pic2_command = Port::new(PIC2_COMMAND);
|
||||
|
||||
if irq >= 8 {
|
||||
pic2_command.write(PIC_EOI as u32);
|
||||
}
|
||||
pic1_command.write(PIC_EOI as u32);
|
||||
}
|
||||
|
||||
/// Mask (disable) an IRQ
|
||||
pub unsafe fn mask_irq(irq: u8) {
|
||||
let port = if irq < 8 {
|
||||
PIC1_DATA
|
||||
} else {
|
||||
PIC2_DATA
|
||||
};
|
||||
|
||||
let mut data_port = Port::new(port);
|
||||
let value = data_port.read() as u8;
|
||||
let mask = 1 << (irq % 8);
|
||||
data_port.write((value | mask) as u32);
|
||||
}
|
||||
|
||||
/// Unmask (enable) an IRQ
|
||||
pub unsafe fn unmask_irq(irq: u8) {
|
||||
let port = if irq < 8 {
|
||||
PIC1_DATA
|
||||
} else {
|
||||
PIC2_DATA
|
||||
};
|
||||
|
||||
let mut data_port = Port::new(port);
|
||||
let value = data_port.read() as u8;
|
||||
let mask = 1 << (irq % 8);
|
||||
data_port.write((value & !mask) as u32);
|
||||
}
|
||||
|
||||
/// I/O wait - small delay for old hardware
|
||||
unsafe fn io_wait() {
|
||||
let mut port = Port::new(0x80);
|
||||
port.write(0);
|
||||
}
|
||||
32
kernel/src/arch/x86_64/port.rs
Archivo normal
32
kernel/src/arch/x86_64/port.rs
Archivo normal
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Port I/O operations
|
||||
|
||||
/// Port I/O wrapper
|
||||
pub struct Port {
|
||||
port: u16,
|
||||
}
|
||||
|
||||
impl Port {
|
||||
pub const fn new(port: u16) -> Self {
|
||||
Self { port }
|
||||
}
|
||||
|
||||
pub unsafe fn write(&mut self, value: u32) {
|
||||
core::arch::asm!(
|
||||
"out dx, eax",
|
||||
in("dx") self.port,
|
||||
in("eax") value,
|
||||
);
|
||||
}
|
||||
|
||||
pub unsafe fn read(&mut self) -> u32 {
|
||||
let value: u32;
|
||||
core::arch::asm!(
|
||||
"in eax, dx",
|
||||
out("eax") value,
|
||||
in("dx") self.port,
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
8
kernel/src/boot.rs
Archivo normal
8
kernel/src/boot.rs
Archivo normal
@@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Boot initialization
|
||||
use crate::error::Result;
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
79
kernel/src/console.rs
Archivo normal
79
kernel/src/console.rs
Archivo normal
@@ -0,0 +1,79 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Console and kernel output
|
||||
|
||||
use core::fmt::{self, Write};
|
||||
use crate::sync::Spinlock;
|
||||
use crate::error::Result;
|
||||
|
||||
/// Console writer
|
||||
static CONSOLE: Spinlock<Console> = Spinlock::new(Console::new());
|
||||
|
||||
struct Console {
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
impl Console {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
initialized: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<()> {
|
||||
// TODO: Initialize actual console hardware
|
||||
self.initialized = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_str(&self, s: &str) {
|
||||
if !self.initialized {
|
||||
return;
|
||||
}
|
||||
|
||||
for byte in s.bytes() {
|
||||
self.write_byte(byte);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_byte(&self, byte: u8) {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
// Write to serial port (COM1)
|
||||
core::arch::asm!(
|
||||
"out dx, al",
|
||||
in("dx") 0x3f8u16,
|
||||
in("al") byte,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize console
|
||||
pub fn init() -> Result<()> {
|
||||
let mut console = CONSOLE.lock();
|
||||
console.init()
|
||||
}
|
||||
|
||||
/// Print function for kernel output
|
||||
pub fn _print(args: fmt::Arguments) {
|
||||
let console = CONSOLE.lock();
|
||||
let mut writer = ConsoleWriter(&*console);
|
||||
writer.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
/// Print function for kernel messages with prefix
|
||||
pub fn _kprint(args: fmt::Arguments) {
|
||||
let console = CONSOLE.lock();
|
||||
let mut writer = ConsoleWriter(&*console);
|
||||
writer.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
struct ConsoleWriter<'a>(&'a Console);
|
||||
|
||||
impl Write for ConsoleWriter<'_> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.0.write_str(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
8
kernel/src/cpu.rs
Archivo normal
8
kernel/src/cpu.rs
Archivo normal
@@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! CPU management
|
||||
use crate::error::Result;
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
383
kernel/src/device.rs
Archivo normal
383
kernel/src/device.rs
Archivo normal
@@ -0,0 +1,383 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Device management compatible with Linux kernel
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::driver::Driver;
|
||||
use crate::sync::Spinlock;
|
||||
use alloc::{vec::Vec, string::String, collections::BTreeMap, boxed::Box};
|
||||
use core::any::Any;
|
||||
|
||||
/// Device types - Linux compatible
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DeviceType {
|
||||
Character,
|
||||
Block,
|
||||
Network,
|
||||
Input,
|
||||
Sound,
|
||||
Video,
|
||||
Misc,
|
||||
Platform,
|
||||
Pci,
|
||||
Usb,
|
||||
}
|
||||
|
||||
/// Device structure - similar to Linux struct device
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
pub name: String,
|
||||
pub device_type: DeviceType,
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
pub driver: Option<Box<dyn Driver>>,
|
||||
pub parent: Option<String>,
|
||||
pub private_data: Option<Box<dyn Any + Send + Sync>>,
|
||||
pub power_state: PowerState,
|
||||
pub dma_coherent: bool,
|
||||
pub numa_node: i32,
|
||||
}
|
||||
|
||||
/// Device power states
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PowerState {
|
||||
On,
|
||||
Suspend,
|
||||
Hibernate,
|
||||
Off,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(name: String, device_type: DeviceType, major: u32, minor: u32) -> Self {
|
||||
Self {
|
||||
name,
|
||||
device_type,
|
||||
major,
|
||||
minor,
|
||||
driver: None,
|
||||
parent: None,
|
||||
private_data: None,
|
||||
power_state: PowerState::On,
|
||||
dma_coherent: false,
|
||||
numa_node: -1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set device driver
|
||||
pub fn set_driver(&mut self, driver: Box<dyn Driver>) -> Result<()> {
|
||||
// Probe the device with the driver
|
||||
driver.probe(self)?;
|
||||
self.driver = Some(driver);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove device driver
|
||||
pub fn remove_driver(&mut self) -> Result<()> {
|
||||
if let Some(ref driver) = self.driver {
|
||||
driver.remove(self)?;
|
||||
}
|
||||
self.driver = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get device name
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Check if device has driver
|
||||
pub fn has_driver(&self) -> bool {
|
||||
self.driver.is_some()
|
||||
}
|
||||
|
||||
/// Set private data
|
||||
pub fn set_private_data<T: Any + Send + Sync>(&mut self, data: T) {
|
||||
self.private_data = Some(Box::new(data));
|
||||
}
|
||||
|
||||
/// Get private data
|
||||
pub fn get_private_data<T: Any + Send + Sync>(&self) -> Option<&T> {
|
||||
self.private_data.as_ref()?.downcast_ref::<T>()
|
||||
}
|
||||
|
||||
/// Power management
|
||||
pub fn suspend(&mut self) -> Result<()> {
|
||||
if let Some(ref driver) = self.driver {
|
||||
driver.suspend(self)?;
|
||||
}
|
||||
self.power_state = PowerState::Suspend;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn resume(&mut self) -> Result<()> {
|
||||
if let Some(ref driver) = self.driver {
|
||||
driver.resume(self)?;
|
||||
}
|
||||
self.power_state = PowerState::On;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Character device structure - Linux compatible
|
||||
#[derive(Debug)]
|
||||
pub struct CharDevice {
|
||||
pub major: u32,
|
||||
pub minor_start: u32,
|
||||
pub minor_count: u32,
|
||||
pub name: String,
|
||||
pub fops: Option<Box<dyn FileOperations>>,
|
||||
}
|
||||
|
||||
impl CharDevice {
|
||||
pub fn new(major: u32, minor_start: u32, minor_count: u32, name: String) -> Self {
|
||||
Self {
|
||||
major,
|
||||
minor_start,
|
||||
minor_count,
|
||||
name,
|
||||
fops: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Block device structure - Linux compatible
|
||||
#[derive(Debug)]
|
||||
pub struct BlockDevice {
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
pub name: String,
|
||||
pub size: u64, // Size in bytes
|
||||
pub block_size: u32,
|
||||
}
|
||||
|
||||
impl BlockDevice {
|
||||
pub fn new(major: u32, minor: u32, name: String, size: u64, block_size: u32) -> Self {
|
||||
Self {
|
||||
major,
|
||||
minor,
|
||||
name,
|
||||
size,
|
||||
block_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// File operations structure - Linux compatible
|
||||
pub trait FileOperations: Send + Sync {
|
||||
fn open(&self, inode: &Inode, file: &mut File) -> Result<()>;
|
||||
fn release(&self, inode: &Inode, file: &mut File) -> Result<()>;
|
||||
fn read(&self, file: &mut File, buf: &mut [u8], offset: u64) -> Result<usize>;
|
||||
fn write(&self, file: &mut File, buf: &[u8], offset: u64) -> Result<usize>;
|
||||
fn ioctl(&self, file: &mut File, cmd: u32, arg: usize) -> Result<usize>;
|
||||
fn mmap(&self, file: &mut File, vma: &mut VMA) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Inode structure - simplified Linux compatible
|
||||
#[derive(Debug)]
|
||||
pub struct Inode {
|
||||
pub ino: u64,
|
||||
pub mode: u32,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
pub size: u64,
|
||||
pub blocks: u64,
|
||||
pub rdev: u32, // Device number for device files
|
||||
}
|
||||
|
||||
/// File structure - simplified Linux compatible
|
||||
#[derive(Debug)]
|
||||
pub struct File {
|
||||
pub inode: Option<Box<Inode>>,
|
||||
pub position: u64,
|
||||
pub flags: u32,
|
||||
pub private_data: Option<Box<dyn Any + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inode: None,
|
||||
position: 0,
|
||||
flags: 0,
|
||||
private_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtual Memory Area - simplified
|
||||
#[derive(Debug)]
|
||||
pub struct VMA {
|
||||
pub start: u64,
|
||||
pub end: u64,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
/// Global device subsystem
|
||||
static DEVICE_SUBSYSTEM: Spinlock<DeviceSubsystem> = Spinlock::new(DeviceSubsystem::new());
|
||||
|
||||
/// Device subsystem state
|
||||
struct DeviceSubsystem {
|
||||
devices: BTreeMap<String, Device>,
|
||||
char_devices: BTreeMap<u32, CharDevice>, // major -> CharDevice
|
||||
block_devices: BTreeMap<u32, BlockDevice>, // major -> BlockDevice
|
||||
next_major: u32,
|
||||
}
|
||||
|
||||
impl DeviceSubsystem {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
devices: BTreeMap::new(),
|
||||
char_devices: BTreeMap::new(),
|
||||
block_devices: BTreeMap::new(),
|
||||
next_major: 240, // Start with dynamic major numbers
|
||||
}
|
||||
}
|
||||
|
||||
fn register_device(&mut self, device: Device) -> Result<()> {
|
||||
let name = device.name.clone();
|
||||
if self.devices.contains_key(&name) {
|
||||
return Err(Error::Busy);
|
||||
}
|
||||
self.devices.insert(name, device);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister_device(&mut self, name: &str) -> Result<Device> {
|
||||
self.devices.remove(name).ok_or(Error::NotFound)
|
||||
}
|
||||
|
||||
fn find_device(&self, name: &str) -> Option<&Device> {
|
||||
self.devices.get(name)
|
||||
}
|
||||
|
||||
fn find_device_mut(&mut self, name: &str) -> Option<&mut Device> {
|
||||
self.devices.get_mut(name)
|
||||
}
|
||||
|
||||
fn allocate_major(&mut self) -> u32 {
|
||||
let major = self.next_major;
|
||||
self.next_major += 1;
|
||||
major
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize device subsystem
|
||||
pub fn init() -> Result<()> {
|
||||
let mut subsystem = DEVICE_SUBSYSTEM.lock();
|
||||
|
||||
// Register standard character devices
|
||||
register_std_char_devices(&mut subsystem)?;
|
||||
|
||||
// Register standard block devices
|
||||
register_std_block_devices(&mut subsystem)?;
|
||||
|
||||
crate::info!("Device subsystem initialized");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register standard character devices
|
||||
fn register_std_char_devices(subsystem: &mut DeviceSubsystem) -> Result<()> {
|
||||
// /dev/null (major 1, minor 3)
|
||||
let null_dev = CharDevice::new(1, 3, 1, String::from("null"));
|
||||
subsystem.char_devices.insert(1, null_dev);
|
||||
|
||||
// /dev/zero (major 1, minor 5)
|
||||
let zero_dev = CharDevice::new(1, 5, 1, String::from("zero"));
|
||||
// Note: This would overwrite the previous entry, so we need a better structure
|
||||
// For now, simplified
|
||||
|
||||
// /dev/random (major 1, minor 8)
|
||||
let random_dev = CharDevice::new(1, 8, 1, String::from("random"));
|
||||
|
||||
// /dev/urandom (major 1, minor 9)
|
||||
let urandom_dev = CharDevice::new(1, 9, 1, String::from("urandom"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register standard block devices
|
||||
fn register_std_block_devices(subsystem: &mut DeviceSubsystem) -> Result<()> {
|
||||
// RAM disk (major 1)
|
||||
let ramdisk = BlockDevice::new(1, 0, String::from("ram0"), 16 * 1024 * 1024, 4096);
|
||||
subsystem.block_devices.insert(1, ramdisk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register a device
|
||||
pub fn register_device(device: Device) -> Result<()> {
|
||||
let mut subsystem = DEVICE_SUBSYSTEM.lock();
|
||||
subsystem.register_device(device)
|
||||
}
|
||||
|
||||
/// Unregister a device
|
||||
pub fn unregister_device(name: &str) -> Result<Device> {
|
||||
let mut subsystem = DEVICE_SUBSYSTEM.lock();
|
||||
subsystem.unregister_device(name)
|
||||
}
|
||||
|
||||
/// Find a device by name
|
||||
pub fn find_device(name: &str) -> Option<Device> {
|
||||
let subsystem = DEVICE_SUBSYSTEM.lock();
|
||||
subsystem.find_device(name).cloned()
|
||||
}
|
||||
|
||||
/// Register a character device
|
||||
pub fn register_chrdev(major: u32, name: String, fops: Box<dyn FileOperations>) -> Result<u32> {
|
||||
let mut subsystem = DEVICE_SUBSYSTEM.lock();
|
||||
|
||||
let actual_major = if major == 0 {
|
||||
subsystem.allocate_major()
|
||||
} else {
|
||||
major
|
||||
};
|
||||
|
||||
let mut char_dev = CharDevice::new(actual_major, 0, 256, name);
|
||||
char_dev.fops = Some(fops);
|
||||
|
||||
if subsystem.char_devices.contains_key(&actual_major) {
|
||||
return Err(Error::Busy);
|
||||
}
|
||||
|
||||
subsystem.char_devices.insert(actual_major, char_dev);
|
||||
Ok(actual_major)
|
||||
}
|
||||
|
||||
/// Unregister a character device
|
||||
pub fn unregister_chrdev(major: u32) -> Result<()> {
|
||||
let mut subsystem = DEVICE_SUBSYSTEM.lock();
|
||||
|
||||
if subsystem.char_devices.remove(&major).is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// List all devices
|
||||
pub fn list_devices() -> Vec<String> {
|
||||
let subsystem = DEVICE_SUBSYSTEM.lock();
|
||||
subsystem.devices.keys().cloned().collect()
|
||||
}
|
||||
|
||||
/// Device tree node - for device tree support
|
||||
#[derive(Debug)]
|
||||
pub struct DeviceTreeNode {
|
||||
pub name: String,
|
||||
pub compatible: Vec<String>,
|
||||
pub reg: Vec<u64>,
|
||||
pub interrupts: Vec<u32>,
|
||||
pub properties: BTreeMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl DeviceTreeNode {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
compatible: Vec::new(),
|
||||
reg: Vec::new(),
|
||||
interrupts: Vec::new(),
|
||||
properties: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
384
kernel/src/driver.rs
Archivo normal
384
kernel/src/driver.rs
Archivo normal
@@ -0,0 +1,384 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Driver framework compatible with Linux kernel
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::device::Device;
|
||||
use crate::sync::Spinlock;
|
||||
use alloc::{vec::Vec, string::String, collections::BTreeMap, boxed::Box};
|
||||
|
||||
/// Driver trait - Linux compatible
|
||||
pub trait Driver: Send + Sync {
|
||||
/// Driver name
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Probe function - called when device is found
|
||||
fn probe(&self, device: &mut Device) -> Result<()>;
|
||||
|
||||
/// Remove function - called when device is removed
|
||||
fn remove(&self, device: &mut Device) -> Result<()>;
|
||||
|
||||
/// Suspend function - power management
|
||||
fn suspend(&self, device: &mut Device) -> Result<()> {
|
||||
// Default implementation does nothing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resume function - power management
|
||||
fn resume(&self, device: &mut Device) -> Result<()> {
|
||||
// Default implementation does nothing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shutdown function - system shutdown
|
||||
fn shutdown(&self, device: &mut Device) {
|
||||
// Default implementation does nothing
|
||||
}
|
||||
}
|
||||
|
||||
/// Driver operations for character devices
|
||||
pub trait CharDriverOps: Send + Sync {
|
||||
fn open(&self, inode: &crate::device::Inode, file: &mut crate::device::File) -> Result<()>;
|
||||
fn release(&self, inode: &crate::device::Inode, file: &mut crate::device::File) -> Result<()>;
|
||||
fn read(&self, file: &mut crate::device::File, buf: &mut [u8], offset: u64) -> Result<usize>;
|
||||
fn write(&self, file: &mut crate::device::File, buf: &[u8], offset: u64) -> Result<usize>;
|
||||
fn ioctl(&self, file: &mut crate::device::File, cmd: u32, arg: usize) -> Result<usize>;
|
||||
}
|
||||
|
||||
/// Driver operations for block devices
|
||||
pub trait BlockDriverOps: Send + Sync {
|
||||
fn read_block(&self, block: u64, buffer: &mut [u8]) -> Result<usize>;
|
||||
fn write_block(&self, block: u64, buffer: &[u8]) -> Result<usize>;
|
||||
fn get_block_size(&self) -> u32;
|
||||
fn get_total_blocks(&self) -> u64;
|
||||
fn flush(&self) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Platform driver - for platform devices
|
||||
pub trait PlatformDriver: Driver {
|
||||
/// Match function - check if driver supports device
|
||||
fn match_device(&self, device: &Device) -> bool;
|
||||
|
||||
/// Get supported device IDs
|
||||
fn device_ids(&self) -> &[DeviceId];
|
||||
}
|
||||
|
||||
/// Device ID structure - for driver matching
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeviceId {
|
||||
pub name: String,
|
||||
pub vendor_id: Option<u32>,
|
||||
pub device_id: Option<u32>,
|
||||
pub class: Option<u32>,
|
||||
pub compatible: Vec<String>, // Device tree compatible strings
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
vendor_id: None,
|
||||
device_id: None,
|
||||
class: None,
|
||||
compatible: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_vendor_device(mut self, vendor_id: u32, device_id: u32) -> Self {
|
||||
self.vendor_id = Some(vendor_id);
|
||||
self.device_id = Some(device_id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_compatible(mut self, compatible: Vec<String>) -> Self {
|
||||
self.compatible = compatible;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// PCI driver - for PCI devices
|
||||
pub trait PciDriver: Driver {
|
||||
/// PCI device IDs supported by this driver
|
||||
fn pci_ids(&self) -> &[PciDeviceId];
|
||||
|
||||
/// PCI-specific probe
|
||||
fn pci_probe(&self, pci_dev: &mut PciDevice) -> Result<()>;
|
||||
|
||||
/// PCI-specific remove
|
||||
fn pci_remove(&self, pci_dev: &mut PciDevice) -> Result<()>;
|
||||
}
|
||||
|
||||
/// PCI device ID
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PciDeviceId {
|
||||
pub vendor: u16,
|
||||
pub device: u16,
|
||||
pub subvendor: u16,
|
||||
pub subdevice: u16,
|
||||
pub class: u32,
|
||||
pub class_mask: u32,
|
||||
}
|
||||
|
||||
impl PciDeviceId {
|
||||
pub const fn new(vendor: u16, device: u16) -> Self {
|
||||
Self {
|
||||
vendor,
|
||||
device,
|
||||
subvendor: 0xFFFF, // PCI_ANY_ID
|
||||
subdevice: 0xFFFF,
|
||||
class: 0,
|
||||
class_mask: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PCI device structure
|
||||
#[derive(Debug)]
|
||||
pub struct PciDevice {
|
||||
pub vendor: u16,
|
||||
pub device: u16,
|
||||
pub subsystem_vendor: u16,
|
||||
pub subsystem_device: u16,
|
||||
pub class: u32,
|
||||
pub revision: u8,
|
||||
pub bus: u8,
|
||||
pub slot: u8,
|
||||
pub function: u8,
|
||||
pub irq: u32,
|
||||
pub bars: [PciBar; 6],
|
||||
}
|
||||
|
||||
/// PCI Base Address Register
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PciBar {
|
||||
pub address: u64,
|
||||
pub size: u64,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
impl PciBar {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
address: 0,
|
||||
size: 0,
|
||||
flags: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_io(&self) -> bool {
|
||||
self.flags & 1 != 0
|
||||
}
|
||||
|
||||
pub fn is_memory(&self) -> bool {
|
||||
!self.is_io()
|
||||
}
|
||||
|
||||
pub fn is_64bit(&self) -> bool {
|
||||
self.is_memory() && (self.flags & 0x6) == 0x4
|
||||
}
|
||||
}
|
||||
|
||||
/// USB driver - for USB devices
|
||||
pub trait UsbDriver: Driver {
|
||||
/// USB device IDs supported by this driver
|
||||
fn usb_ids(&self) -> &[UsbDeviceId];
|
||||
|
||||
/// USB-specific probe
|
||||
fn usb_probe(&self, usb_dev: &mut UsbDevice) -> Result<()>;
|
||||
|
||||
/// USB-specific disconnect
|
||||
fn usb_disconnect(&self, usb_dev: &mut UsbDevice) -> Result<()>;
|
||||
}
|
||||
|
||||
/// USB device ID
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct UsbDeviceId {
|
||||
pub vendor: u16,
|
||||
pub product: u16,
|
||||
pub device_class: u8,
|
||||
pub device_subclass: u8,
|
||||
pub device_protocol: u8,
|
||||
pub interface_class: u8,
|
||||
pub interface_subclass: u8,
|
||||
pub interface_protocol: u8,
|
||||
}
|
||||
|
||||
/// USB device structure
|
||||
#[derive(Debug)]
|
||||
pub struct UsbDevice {
|
||||
pub vendor: u16,
|
||||
pub product: u16,
|
||||
pub device_class: u8,
|
||||
pub device_subclass: u8,
|
||||
pub device_protocol: u8,
|
||||
pub speed: UsbSpeed,
|
||||
pub address: u8,
|
||||
pub configuration: u8,
|
||||
}
|
||||
|
||||
/// USB speeds
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum UsbSpeed {
|
||||
Low, // 1.5 Mbps
|
||||
Full, // 12 Mbps
|
||||
High, // 480 Mbps
|
||||
Super, // 5 Gbps
|
||||
SuperPlus, // 10 Gbps
|
||||
}
|
||||
|
||||
/// Global driver subsystem
|
||||
static DRIVER_SUBSYSTEM: Spinlock<DriverSubsystem> = Spinlock::new(DriverSubsystem::new());
|
||||
|
||||
/// Driver subsystem state
|
||||
struct DriverSubsystem {
|
||||
drivers: BTreeMap<String, Box<dyn Driver>>,
|
||||
platform_drivers: Vec<Box<dyn PlatformDriver>>,
|
||||
pci_drivers: Vec<Box<dyn PciDriver>>,
|
||||
usb_drivers: Vec<Box<dyn UsbDriver>>,
|
||||
}
|
||||
|
||||
impl DriverSubsystem {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
drivers: BTreeMap::new(),
|
||||
platform_drivers: Vec::new(),
|
||||
pci_drivers: Vec::new(),
|
||||
usb_drivers: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn register_driver(&mut self, driver: Box<dyn Driver>) -> Result<()> {
|
||||
let name = driver.name().to_string();
|
||||
if self.drivers.contains_key(&name) {
|
||||
return Err(Error::Busy);
|
||||
}
|
||||
self.drivers.insert(name, driver);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister_driver(&mut self, name: &str) -> Result<()> {
|
||||
if self.drivers.remove(name).is_some() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_driver(&self, name: &str) -> Option<&dyn Driver> {
|
||||
self.drivers.get(name).map(|d| d.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a driver
|
||||
pub fn register_driver(driver: Box<dyn Driver>) -> Result<()> {
|
||||
let mut subsystem = DRIVER_SUBSYSTEM.lock();
|
||||
let name = driver.name().to_string();
|
||||
subsystem.register_driver(driver)?;
|
||||
crate::info!("Registered driver: {}", name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unregister a driver
|
||||
pub fn unregister_driver(name: &str) -> Result<()> {
|
||||
let mut subsystem = DRIVER_SUBSYSTEM.lock();
|
||||
subsystem.unregister_driver(name)?;
|
||||
crate::info!("Unregistered driver: {}", name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register a platform driver
|
||||
pub fn register_platform_driver(driver: Box<dyn PlatformDriver>) -> Result<()> {
|
||||
let mut subsystem = DRIVER_SUBSYSTEM.lock();
|
||||
let name = driver.name().to_string();
|
||||
|
||||
// Also register as a regular driver
|
||||
let driver_copy = unsafe {
|
||||
// This is a bit of a hack - we need to clone the driver
|
||||
// In a real implementation, we'd use Arc or similar
|
||||
core::mem::transmute::<*const dyn PlatformDriver, Box<dyn Driver>>(
|
||||
driver.as_ref() as *const dyn PlatformDriver
|
||||
)
|
||||
};
|
||||
subsystem.register_driver(driver_copy)?;
|
||||
subsystem.platform_drivers.push(driver);
|
||||
|
||||
crate::info!("Registered platform driver: {}", name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register a PCI driver
|
||||
pub fn register_pci_driver(driver: Box<dyn PciDriver>) -> Result<()> {
|
||||
let mut subsystem = DRIVER_SUBSYSTEM.lock();
|
||||
let name = driver.name().to_string();
|
||||
subsystem.pci_drivers.push(driver);
|
||||
crate::info!("Registered PCI driver: {}", name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register a USB driver
|
||||
pub fn register_usb_driver(driver: Box<dyn UsbDriver>) -> Result<()> {
|
||||
let mut subsystem = DRIVER_SUBSYSTEM.lock();
|
||||
let name = driver.name().to_string();
|
||||
subsystem.usb_drivers.push(driver);
|
||||
crate::info!("Registered USB driver: {}", name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Find and match a driver for a device
|
||||
pub fn match_driver(device: &Device) -> Option<String> {
|
||||
let subsystem = DRIVER_SUBSYSTEM.lock();
|
||||
|
||||
// Try platform drivers first
|
||||
for driver in &subsystem.platform_drivers {
|
||||
if driver.match_device(device) {
|
||||
return Some(driver.name().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Try PCI, USB, etc. drivers based on device type
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Get list of registered drivers
|
||||
pub fn list_drivers() -> Vec<String> {
|
||||
let subsystem = DRIVER_SUBSYSTEM.lock();
|
||||
subsystem.drivers.keys().cloned().collect()
|
||||
}
|
||||
|
||||
/// Module macros for easier driver registration
|
||||
#[macro_export]
|
||||
macro_rules! platform_driver {
|
||||
($driver:ident) => {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn init_module() -> core::ffi::c_int {
|
||||
match $crate::driver::register_platform_driver(Box::new($driver)) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => e.to_errno(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn cleanup_module() {
|
||||
$crate::driver::unregister_driver(stringify!($driver)).ok();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pci_driver {
|
||||
($driver:ident) => {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn init_module() -> core::ffi::c_int {
|
||||
match $crate::driver::register_pci_driver(Box::new($driver)) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => e.to_errno(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn cleanup_module() {
|
||||
$crate::driver::unregister_driver(stringify!($driver)).ok();
|
||||
}
|
||||
};
|
||||
}
|
||||
85
kernel/src/error.rs
Archivo normal
85
kernel/src/error.rs
Archivo normal
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Error handling types and utilities
|
||||
|
||||
use core::fmt;
|
||||
|
||||
/// Kernel error type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// Out of memory
|
||||
OutOfMemory,
|
||||
/// Invalid argument
|
||||
InvalidArgument,
|
||||
/// Permission denied
|
||||
PermissionDenied,
|
||||
/// Resource busy
|
||||
Busy,
|
||||
/// Resource not found
|
||||
NotFound,
|
||||
/// Operation not supported
|
||||
NotSupported,
|
||||
/// I/O error
|
||||
Io,
|
||||
/// Interrupted operation
|
||||
Interrupted,
|
||||
/// Resource temporarily unavailable
|
||||
WouldBlock,
|
||||
/// Device error
|
||||
Device,
|
||||
/// Generic error
|
||||
Generic,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Convert error to errno value
|
||||
pub fn to_errno(self) -> i32 {
|
||||
match self {
|
||||
Error::OutOfMemory => -12, // ENOMEM
|
||||
Error::InvalidArgument => -22, // EINVAL
|
||||
Error::PermissionDenied => -1, // EPERM
|
||||
Error::Busy => -16, // EBUSY
|
||||
Error::NotFound => -2, // ENOENT
|
||||
Error::NotSupported => -38, // ENOSYS
|
||||
Error::Io => -5, // EIO
|
||||
Error::Interrupted => -4, // EINTR
|
||||
Error::WouldBlock => -11, // EAGAIN
|
||||
Error::Device => -19, // ENODEV
|
||||
Error::Generic => -1, // EPERM
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::OutOfMemory => write!(f, "Out of memory"),
|
||||
Error::InvalidArgument => write!(f, "Invalid argument"),
|
||||
Error::PermissionDenied => write!(f, "Permission denied"),
|
||||
Error::Busy => write!(f, "Resource busy"),
|
||||
Error::NotFound => write!(f, "Resource not found"),
|
||||
Error::NotSupported => write!(f, "Operation not supported"),
|
||||
Error::Io => write!(f, "I/O error"),
|
||||
Error::Interrupted => write!(f, "Interrupted operation"),
|
||||
Error::WouldBlock => write!(f, "Resource temporarily unavailable"),
|
||||
Error::Device => write!(f, "Device error"),
|
||||
Error::Generic => write!(f, "Generic error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Kernel result type
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
/// Convert from various error types
|
||||
impl From<()> for Error {
|
||||
fn from(_: ()) -> Self {
|
||||
Error::Generic
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::alloc::AllocError> for Error {
|
||||
fn from(_: core::alloc::AllocError) -> Self {
|
||||
Error::OutOfMemory
|
||||
}
|
||||
}
|
||||
283
kernel/src/fs/dentry.rs
Archivo normal
283
kernel/src/fs/dentry.rs
Archivo normal
@@ -0,0 +1,283 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Directory entry (dentry) abstraction - Linux compatible
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sync::{Arc, Mutex, RwLock};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
/// Dentry structure - similar to Linux struct dentry
|
||||
pub struct Dentry {
|
||||
/// Entry name
|
||||
pub d_name: String,
|
||||
/// Associated inode
|
||||
pub d_inode: Option<Arc<super::Inode>>,
|
||||
/// Parent dentry
|
||||
pub d_parent: Option<Arc<Dentry>>,
|
||||
/// Child entries (for directories)
|
||||
pub d_subdirs: Mutex<Vec<Arc<Dentry>>>,
|
||||
/// Dentry operations
|
||||
pub d_op: Option<Arc<dyn DentryOperations>>,
|
||||
/// Superblock
|
||||
pub d_sb: Option<Arc<super::SuperBlock>>,
|
||||
/// Reference count
|
||||
pub d_count: AtomicU32,
|
||||
/// Dentry flags
|
||||
pub d_flags: AtomicU32,
|
||||
/// Hash for dcache
|
||||
pub d_hash: u32,
|
||||
}
|
||||
|
||||
impl Dentry {
|
||||
/// Create a new dentry
|
||||
pub fn new(name: String, inode: Option<Arc<super::Inode>>) -> Self {
|
||||
Self {
|
||||
d_name: name,
|
||||
d_inode: inode,
|
||||
d_parent: None,
|
||||
d_subdirs: Mutex::new(Vec::new()),
|
||||
d_op: None,
|
||||
d_sb: None,
|
||||
d_count: AtomicU32::new(1),
|
||||
d_flags: AtomicU32::new(0),
|
||||
d_hash: 0, // TODO: Calculate hash
|
||||
}
|
||||
}
|
||||
|
||||
/// Set parent dentry
|
||||
pub fn set_parent(&mut self, parent: Arc<Dentry>) {
|
||||
self.d_parent = Some(parent);
|
||||
}
|
||||
|
||||
/// Add child dentry
|
||||
pub fn add_child(&self, child: Arc<Dentry>) {
|
||||
let mut subdirs = self.d_subdirs.lock();
|
||||
subdirs.push(child);
|
||||
}
|
||||
|
||||
/// Find child dentry by name
|
||||
pub fn find_child(&self, name: &str) -> Option<Arc<Dentry>> {
|
||||
let subdirs = self.d_subdirs.lock();
|
||||
for child in subdirs.iter() {
|
||||
if child.d_name == name {
|
||||
return Some(child.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Get full path of this dentry
|
||||
pub fn get_path(&self) -> String {
|
||||
if let Some(ref parent) = self.d_parent {
|
||||
if parent.d_name == "/" {
|
||||
format!("/{}", self.d_name)
|
||||
} else {
|
||||
format!("{}/{}", parent.get_path(), self.d_name)
|
||||
}
|
||||
} else {
|
||||
self.d_name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if dentry is root
|
||||
pub fn is_root(&self) -> bool {
|
||||
self.d_parent.is_none() || self.d_name == "/"
|
||||
}
|
||||
|
||||
/// Increment reference count
|
||||
pub fn dget(&self) {
|
||||
self.d_count.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Decrement reference count
|
||||
pub fn dput(&self) {
|
||||
let old_count = self.d_count.fetch_sub(1, Ordering::Relaxed);
|
||||
if old_count == 1 {
|
||||
// Last reference, dentry should be cleaned up
|
||||
// TODO: Call d_delete operation if present
|
||||
}
|
||||
}
|
||||
|
||||
/// Revalidate dentry
|
||||
pub fn revalidate(&self) -> Result<bool> {
|
||||
if let Some(ref ops) = self.d_op {
|
||||
ops.revalidate(self)
|
||||
} else {
|
||||
Ok(true) // Always valid by default
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete dentry
|
||||
pub fn delete(&self) -> Result<()> {
|
||||
if let Some(ref ops) = self.d_op {
|
||||
ops.delete(self)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare two dentries
|
||||
pub fn compare(&self, other: &Dentry) -> Result<bool> {
|
||||
if let Some(ref ops) = self.d_op {
|
||||
ops.compare(self, other)
|
||||
} else {
|
||||
Ok(self.d_name == other.d_name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash dentry name
|
||||
pub fn hash(&self) -> Result<u32> {
|
||||
if let Some(ref ops) = self.d_op {
|
||||
ops.hash(self)
|
||||
} else {
|
||||
// Simple hash function
|
||||
let mut hash = 0u32;
|
||||
for byte in self.d_name.bytes() {
|
||||
hash = hash.wrapping_mul(31).wrapping_add(byte as u32);
|
||||
}
|
||||
Ok(hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Dentry {}
|
||||
unsafe impl Sync for Dentry {}
|
||||
|
||||
/// Dentry operations trait - similar to Linux dentry_operations
|
||||
pub trait DentryOperations: Send + Sync {
|
||||
/// Revalidate dentry
|
||||
fn revalidate(&self, dentry: &Dentry) -> Result<bool>;
|
||||
|
||||
/// Hash dentry name
|
||||
fn hash(&self, dentry: &Dentry) -> Result<u32>;
|
||||
|
||||
/// Compare two dentries
|
||||
fn compare(&self, d1: &Dentry, d2: &Dentry) -> Result<bool>;
|
||||
|
||||
/// Delete dentry
|
||||
fn delete(&self, dentry: &Dentry) -> Result<()>;
|
||||
|
||||
/// Release dentry
|
||||
fn release(&self, dentry: &Dentry) -> Result<()>;
|
||||
|
||||
/// Canonicalize path
|
||||
fn canonical_path(&self, dentry: &Dentry) -> Result<String>;
|
||||
}
|
||||
|
||||
/// Generic dentry operations
|
||||
pub struct GenericDentryOps;
|
||||
|
||||
impl DentryOperations for GenericDentryOps {
|
||||
fn revalidate(&self, dentry: &Dentry) -> Result<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn hash(&self, dentry: &Dentry) -> Result<u32> {
|
||||
dentry.hash()
|
||||
}
|
||||
|
||||
fn compare(&self, d1: &Dentry, d2: &Dentry) -> Result<bool> {
|
||||
Ok(d1.d_name == d2.d_name)
|
||||
}
|
||||
|
||||
fn delete(&self, dentry: &Dentry) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, dentry: &Dentry) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn canonical_path(&self, dentry: &Dentry) -> Result<String> {
|
||||
Ok(dentry.get_path())
|
||||
}
|
||||
}
|
||||
|
||||
/// Dentry cache (dcache) - simplified version
|
||||
pub struct DentryCache {
|
||||
/// Cached dentries
|
||||
cache: Mutex<alloc::collections::BTreeMap<String, Arc<Dentry>>>,
|
||||
/// Hash buckets for faster lookup
|
||||
hash_table: Vec<Mutex<Vec<Arc<Dentry>>>>,
|
||||
}
|
||||
|
||||
impl DentryCache {
|
||||
/// Create a new dentry cache
|
||||
pub fn new() -> Self {
|
||||
const HASH_BUCKETS: usize = 256;
|
||||
let mut hash_table = Vec::with_capacity(HASH_BUCKETS);
|
||||
for _ in 0..HASH_BUCKETS {
|
||||
hash_table.push(Mutex::new(Vec::new()));
|
||||
}
|
||||
|
||||
Self {
|
||||
cache: Mutex::new(alloc::collections::BTreeMap::new()),
|
||||
hash_table,
|
||||
}
|
||||
}
|
||||
|
||||
/// Look up dentry by path
|
||||
pub fn lookup(&self, path: &str) -> Option<Arc<Dentry>> {
|
||||
let cache = self.cache.lock();
|
||||
cache.get(path).cloned()
|
||||
}
|
||||
|
||||
/// Insert dentry into cache
|
||||
pub fn insert(&self, path: String, dentry: Arc<Dentry>) {
|
||||
let mut cache = self.cache.lock();
|
||||
cache.insert(path, dentry.clone());
|
||||
|
||||
// Also insert into hash table
|
||||
let hash = dentry.d_hash as usize % self.hash_table.len();
|
||||
let mut bucket = self.hash_table[hash].lock();
|
||||
bucket.push(dentry);
|
||||
}
|
||||
|
||||
/// Remove dentry from cache
|
||||
pub fn remove(&self, path: &str) -> Option<Arc<Dentry>> {
|
||||
let mut cache = self.cache.lock();
|
||||
cache.remove(path)
|
||||
}
|
||||
|
||||
/// Prune unused dentries
|
||||
pub fn prune(&self) {
|
||||
let mut cache = self.cache.lock();
|
||||
cache.retain(|_, dentry| {
|
||||
dentry.d_count.load(Ordering::Relaxed) > 1
|
||||
});
|
||||
|
||||
// Also prune hash table
|
||||
for bucket in &self.hash_table {
|
||||
let mut bucket = bucket.lock();
|
||||
bucket.retain(|dentry| {
|
||||
dentry.d_count.load(Ordering::Relaxed) > 1
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global dentry cache
|
||||
static DCACHE: once_cell::sync::Lazy<DentryCache> =
|
||||
once_cell::sync::Lazy::new(|| DentryCache::new());
|
||||
|
||||
/// Look up dentry in cache
|
||||
pub fn dcache_lookup(path: &str) -> Option<Arc<Dentry>> {
|
||||
DCACHE.lookup(path)
|
||||
}
|
||||
|
||||
/// Insert dentry into cache
|
||||
pub fn dcache_insert(path: String, dentry: Arc<Dentry>) {
|
||||
DCACHE.insert(path, dentry);
|
||||
}
|
||||
|
||||
/// Remove dentry from cache
|
||||
pub fn dcache_remove(path: &str) -> Option<Arc<Dentry>> {
|
||||
DCACHE.remove(path)
|
||||
}
|
||||
|
||||
/// Prune dentry cache
|
||||
pub fn dcache_prune() {
|
||||
DCACHE.prune();
|
||||
}
|
||||
441
kernel/src/fs/devfs.rs
Archivo normal
441
kernel/src/fs/devfs.rs
Archivo normal
@@ -0,0 +1,441 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Character device filesystem integration
|
||||
//!
|
||||
//! This driver provides VFS integration for character devices
|
||||
//! like /dev/null, /dev/zero, /dev/random, etc.
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::fs::*;
|
||||
use crate::memory::UserSlicePtr;
|
||||
use crate::sync::Arc;
|
||||
use alloc::string::String;
|
||||
|
||||
/// Character device file operations
|
||||
pub struct CharDevFileOps {
|
||||
/// Device operations
|
||||
dev_ops: Option<Arc<dyn CharDevOperations>>,
|
||||
}
|
||||
|
||||
impl CharDevFileOps {
|
||||
pub fn new(dev_ops: Option<Arc<dyn CharDevOperations>>) -> Self {
|
||||
Self { dev_ops }
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOperations for CharDevFileOps {
|
||||
fn read(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
if let Some(ref ops) = self.dev_ops {
|
||||
ops.read(file, buf, count)
|
||||
} else {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
if let Some(ref ops) = self.dev_ops {
|
||||
ops.write(file, buf, count)
|
||||
} else {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
fn seek(&self, file: &File, offset: i64, whence: i32) -> Result<i64> {
|
||||
// Most character devices don't support seeking
|
||||
Err(Error::ESPIPE)
|
||||
}
|
||||
|
||||
fn ioctl(&self, file: &File, cmd: u32, arg: usize) -> Result<isize> {
|
||||
if let Some(ref ops) = self.dev_ops {
|
||||
ops.ioctl(file, cmd, arg)
|
||||
} else {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
}
|
||||
|
||||
fn mmap(&self, file: &File, vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
if let Some(ref ops) = self.dev_ops {
|
||||
ops.mmap(file, vma)
|
||||
} else {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
fn fsync(&self, file: &File, datasync: bool) -> Result<()> {
|
||||
// Character devices don't need syncing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, file: &File, wait: &mut PollWait) -> Result<u32> {
|
||||
if let Some(ref ops) = self.dev_ops {
|
||||
ops.poll(file, wait)
|
||||
} else {
|
||||
Ok(POLLIN | POLLOUT)
|
||||
}
|
||||
}
|
||||
|
||||
fn open(&self, inode: &Inode, file: &File) -> Result<()> {
|
||||
if let Some(ref ops) = self.dev_ops {
|
||||
ops.open(inode, file)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn release(&self, inode: &Inode, file: &File) -> Result<()> {
|
||||
if let Some(ref ops) = self.dev_ops {
|
||||
ops.release(inode, file)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Character device operations trait
|
||||
pub trait CharDevOperations: Send + Sync {
|
||||
fn read(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize>;
|
||||
fn write(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize>;
|
||||
fn ioctl(&self, file: &File, cmd: u32, arg: usize) -> Result<isize>;
|
||||
fn mmap(&self, file: &File, vma: &mut crate::memory::VmaArea) -> Result<()>;
|
||||
fn poll(&self, file: &File, wait: &mut PollWait) -> Result<u32>;
|
||||
fn open(&self, inode: &Inode, file: &File) -> Result<()>;
|
||||
fn release(&self, inode: &Inode, file: &File) -> Result<()>;
|
||||
}
|
||||
|
||||
/// /dev/null device operations
|
||||
pub struct NullDevOps;
|
||||
|
||||
impl CharDevOperations for NullDevOps {
|
||||
fn read(&self, _file: &File, _buf: UserSlicePtr, _count: usize) -> Result<isize> {
|
||||
Ok(0) // EOF
|
||||
}
|
||||
|
||||
fn write(&self, _file: &File, _buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
Ok(count as isize) // Discard all data
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &File, _cmd: u32, _arg: usize) -> Result<isize> {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &File, _vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn poll(&self, _file: &File, _wait: &mut PollWait) -> Result<u32> {
|
||||
Ok(POLLIN | POLLOUT) // Always ready
|
||||
}
|
||||
|
||||
fn open(&self, _inode: &Inode, _file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// /dev/zero device operations
|
||||
pub struct ZeroDevOps;
|
||||
|
||||
impl CharDevOperations for ZeroDevOps {
|
||||
fn read(&self, _file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Fill buffer with zeros
|
||||
let zeros = vec![0u8; count];
|
||||
buf.copy_from_slice(&zeros)?;
|
||||
Ok(count as isize)
|
||||
}
|
||||
|
||||
fn write(&self, _file: &File, _buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
Ok(count as isize) // Discard all data
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &File, _cmd: u32, _arg: usize) -> Result<isize> {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &File, _vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
// TODO: Map zero page
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn poll(&self, _file: &File, _wait: &mut PollWait) -> Result<u32> {
|
||||
Ok(POLLIN | POLLOUT) // Always ready
|
||||
}
|
||||
|
||||
fn open(&self, _inode: &Inode, _file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// /dev/full device operations
|
||||
pub struct FullDevOps;
|
||||
|
||||
impl CharDevOperations for FullDevOps {
|
||||
fn read(&self, _file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Fill buffer with zeros (like /dev/zero)
|
||||
let zeros = vec![0u8; count];
|
||||
buf.copy_from_slice(&zeros)?;
|
||||
Ok(count as isize)
|
||||
}
|
||||
|
||||
fn write(&self, _file: &File, _buf: UserSlicePtr, _count: usize) -> Result<isize> {
|
||||
Err(Error::ENOSPC) // No space left on device
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &File, _cmd: u32, _arg: usize) -> Result<isize> {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &File, _vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn poll(&self, _file: &File, _wait: &mut PollWait) -> Result<u32> {
|
||||
Ok(POLLIN) // Only readable
|
||||
}
|
||||
|
||||
fn open(&self, _inode: &Inode, _file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// /dev/random device operations (simplified)
|
||||
pub struct RandomDevOps;
|
||||
|
||||
impl CharDevOperations for RandomDevOps {
|
||||
fn read(&self, _file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Generate pseudo-random data
|
||||
// TODO: Use proper random number generator
|
||||
let mut random_data = vec![0u8; count];
|
||||
for i in 0..count {
|
||||
random_data[i] = (i * 37 + 13) as u8; // Very simple PRNG
|
||||
}
|
||||
buf.copy_from_slice(&random_data)?;
|
||||
Ok(count as isize)
|
||||
}
|
||||
|
||||
fn write(&self, _file: &File, _buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// TODO: Add entropy to random pool
|
||||
Ok(count as isize)
|
||||
}
|
||||
|
||||
fn ioctl(&self, _file: &File, _cmd: u32, _arg: usize) -> Result<isize> {
|
||||
// TODO: Implement random device ioctls
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, _file: &File, _vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn poll(&self, _file: &File, _wait: &mut PollWait) -> Result<u32> {
|
||||
Ok(POLLIN | POLLOUT) // Always ready
|
||||
}
|
||||
|
||||
fn open(&self, _inode: &Inode, _file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _inode: &Inode, _file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a character device inode
|
||||
pub fn create_char_device_inode(
|
||||
major: u32,
|
||||
minor: u32,
|
||||
mode: u32,
|
||||
ops: Arc<dyn CharDevOperations>,
|
||||
) -> Arc<Inode> {
|
||||
let mut inode = Inode::new(0, mode::S_IFCHR | mode);
|
||||
inode.i_rdev = crate::device::DeviceNumber::new(major, minor);
|
||||
inode.set_file_operations(Arc::new(CharDevFileOps::new(Some(ops))));
|
||||
|
||||
Arc::new(inode)
|
||||
}
|
||||
|
||||
/// DevFS - device filesystem for /dev
|
||||
pub struct DevFs {
|
||||
/// Device entries
|
||||
devices: crate::sync::Mutex<alloc::collections::BTreeMap<String, Arc<Inode>>>,
|
||||
}
|
||||
|
||||
impl DevFs {
|
||||
pub fn new() -> Self {
|
||||
let mut devfs = Self {
|
||||
devices: crate::sync::Mutex::new(alloc::collections::BTreeMap::new()),
|
||||
};
|
||||
|
||||
devfs.create_standard_devices();
|
||||
devfs
|
||||
}
|
||||
|
||||
fn create_standard_devices(&mut self) {
|
||||
// Create /dev/null
|
||||
let null_inode = create_char_device_inode(1, 3, 0o666, Arc::new(NullDevOps));
|
||||
self.add_device("null", null_inode);
|
||||
|
||||
// Create /dev/zero
|
||||
let zero_inode = create_char_device_inode(1, 5, 0o666, Arc::new(ZeroDevOps));
|
||||
self.add_device("zero", zero_inode);
|
||||
|
||||
// Create /dev/full
|
||||
let full_inode = create_char_device_inode(1, 7, 0o666, Arc::new(FullDevOps));
|
||||
self.add_device("full", full_inode);
|
||||
|
||||
// Create /dev/random
|
||||
let random_inode = create_char_device_inode(1, 8, 0o666, Arc::new(RandomDevOps));
|
||||
self.add_device("random", random_inode);
|
||||
|
||||
// Create /dev/urandom (same as random for now)
|
||||
let urandom_inode = create_char_device_inode(1, 9, 0o666, Arc::new(RandomDevOps));
|
||||
self.add_device("urandom", urandom_inode);
|
||||
}
|
||||
|
||||
pub fn add_device(&mut self, name: &str, inode: Arc<Inode>) {
|
||||
let mut devices = self.devices.lock();
|
||||
devices.insert(String::from(name), inode);
|
||||
}
|
||||
|
||||
pub fn get_device(&self, name: &str) -> Option<Arc<Inode>> {
|
||||
let devices = self.devices.lock();
|
||||
devices.get(name).cloned()
|
||||
}
|
||||
|
||||
pub fn list_devices(&self) -> alloc::vec::Vec<String> {
|
||||
let devices = self.devices.lock();
|
||||
devices.keys().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// DevFS inode operations
|
||||
pub struct DevFsInodeOps {
|
||||
devfs: *const DevFs,
|
||||
}
|
||||
|
||||
impl DevFsInodeOps {
|
||||
pub fn new(devfs: &DevFs) -> Self {
|
||||
Self {
|
||||
devfs: devfs as *const DevFs,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_devfs(&self) -> &DevFs {
|
||||
unsafe { &*self.devfs }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for DevFsInodeOps {}
|
||||
unsafe impl Sync for DevFsInodeOps {}
|
||||
|
||||
impl InodeOperations for DevFsInodeOps {
|
||||
fn lookup(&self, dir: &Inode, name: &str) -> Result<Arc<Inode>> {
|
||||
let devfs = self.get_devfs();
|
||||
devfs.get_device(name).ok_or(Error::ENOENT)
|
||||
}
|
||||
|
||||
fn create(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
Err(Error::EPERM) // Can't create devices in /dev directly
|
||||
}
|
||||
|
||||
fn mkdir(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn unlink(&self, dir: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn rmdir(&self, dir: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn symlink(&self, dir: &Inode, name: &str, target: &str) -> Result<Arc<Inode>> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn rename(&self, old_dir: &Inode, old_name: &str, new_dir: &Inode, new_name: &str) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn setattr(&self, inode: &Inode, attr: &InodeAttr) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn getattr(&self, inode: &Inode) -> Result<InodeAttr> {
|
||||
let generic_ops = GenericInodeOps;
|
||||
generic_ops.getattr(inode)
|
||||
}
|
||||
|
||||
fn readlink(&self, inode: &Inode) -> Result<String> {
|
||||
Err(Error::EINVAL)
|
||||
}
|
||||
|
||||
fn follow_link(&self, inode: &Inode) -> Result<Arc<Inode>> {
|
||||
Err(Error::EINVAL)
|
||||
}
|
||||
|
||||
fn truncate(&self, inode: &Inode, size: u64) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn getxattr(&self, inode: &Inode, name: &str) -> Result<alloc::vec::Vec<u8>> {
|
||||
Err(Error::ENODATA)
|
||||
}
|
||||
|
||||
fn setxattr(&self, inode: &Inode, name: &str, value: &[u8], flags: u32) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn listxattr(&self, inode: &Inode) -> Result<alloc::vec::Vec<String>> {
|
||||
Ok(alloc::vec::Vec::new())
|
||||
}
|
||||
|
||||
fn removexattr(&self, inode: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mount devfs
|
||||
pub fn mount_devfs(_dev_name: &str, _flags: u32, _data: Option<&str>) -> Result<Arc<SuperBlock>> {
|
||||
let mut sb = SuperBlock::new("devfs")?;
|
||||
sb.s_magic = 0x1373; // DEVFS magic
|
||||
|
||||
let devfs = Box::leak(Box::new(DevFs::new()));
|
||||
sb.s_fs_info = Some(devfs as *mut DevFs as *mut u8);
|
||||
|
||||
// Create root inode
|
||||
let root_inode = Arc::new({
|
||||
let mut inode = Inode::new(1, mode::S_IFDIR | 0o755);
|
||||
inode.set_operations(Arc::new(DevFsInodeOps::new(devfs)));
|
||||
inode
|
||||
});
|
||||
|
||||
let root_dentry = Arc::new(Dentry::new(String::from("/"), Some(root_inode)));
|
||||
sb.s_root = Some(root_dentry);
|
||||
|
||||
Ok(Arc::new(sb))
|
||||
}
|
||||
|
||||
/// Register devfs filesystem
|
||||
pub fn register_devfs() -> Result<()> {
|
||||
let devfs_type = FileSystemType::new(
|
||||
String::from("devfs"),
|
||||
|_fstype, flags, _dev_name, data| mount_devfs(_dev_name, flags, data),
|
||||
|_sb| Ok(()),
|
||||
);
|
||||
|
||||
// TODO: Register with VFS
|
||||
crate::console::print_info("Registered devfs filesystem\n");
|
||||
Ok(())
|
||||
}
|
||||
271
kernel/src/fs/file.rs
Archivo normal
271
kernel/src/fs/file.rs
Archivo normal
@@ -0,0 +1,271 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! File abstraction - Linux compatible
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::types::*;
|
||||
use crate::memory::{UserPtr, UserSlicePtr};
|
||||
use crate::sync::{Arc, Mutex, RwLock};
|
||||
use alloc::string::String;
|
||||
use core::sync::atomic::{AtomicI64, AtomicU32, Ordering};
|
||||
|
||||
/// File structure - similar to Linux struct file
|
||||
pub struct File {
|
||||
/// File operations
|
||||
pub f_op: Option<Arc<dyn FileOperations>>,
|
||||
/// Current file position
|
||||
pub pos: AtomicI64,
|
||||
/// File flags
|
||||
pub flags: AtomicU32,
|
||||
/// File mode
|
||||
pub mode: u32,
|
||||
/// Associated inode
|
||||
pub inode: Option<Arc<super::Inode>>,
|
||||
/// Associated dentry
|
||||
pub dentry: Option<Arc<super::Dentry>>,
|
||||
/// Private data for file operations
|
||||
pub private_data: Option<*mut u8>,
|
||||
/// File path (for debugging/proc)
|
||||
pub path: String,
|
||||
/// Reference count
|
||||
pub refcount: AtomicU32,
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Create a new file
|
||||
pub fn new(path: &str, flags: u32, mode: u32) -> Result<Self> {
|
||||
Ok(Self {
|
||||
f_op: None,
|
||||
pos: AtomicI64::new(0),
|
||||
flags: AtomicU32::new(flags),
|
||||
mode,
|
||||
inode: None,
|
||||
dentry: None,
|
||||
private_data: None,
|
||||
path: String::from(path),
|
||||
refcount: AtomicU32::new(1),
|
||||
})
|
||||
}
|
||||
|
||||
/// Set file operations
|
||||
pub fn set_operations(&mut self, ops: Arc<dyn FileOperations>) {
|
||||
self.f_op = Some(ops);
|
||||
}
|
||||
|
||||
/// Read from file
|
||||
pub fn read(&self, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
if let Some(ref ops) = self.f_op {
|
||||
ops.read(self, buf, count)
|
||||
} else {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write to file
|
||||
pub fn write(&self, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
if let Some(ref ops) = self.f_op {
|
||||
ops.write(self, buf, count)
|
||||
} else {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Seek within file
|
||||
pub fn seek(&self, offset: i64, whence: i32) -> Result<i64> {
|
||||
if let Some(ref ops) = self.f_op {
|
||||
let new_pos = ops.seek(self, offset, whence)?;
|
||||
self.pos.store(new_pos, Ordering::Relaxed);
|
||||
Ok(new_pos)
|
||||
} else {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get file status
|
||||
pub fn stat(&self) -> Result<super::KStat> {
|
||||
if let Some(ref inode) = self.inode {
|
||||
inode.stat()
|
||||
} else {
|
||||
// Create a basic stat structure
|
||||
Ok(super::KStat {
|
||||
st_dev: 0,
|
||||
st_ino: 0,
|
||||
st_nlink: 1,
|
||||
st_mode: self.mode,
|
||||
st_uid: 0,
|
||||
st_gid: 0,
|
||||
st_rdev: 0,
|
||||
st_size: 0,
|
||||
st_blksize: 4096,
|
||||
st_blocks: 0,
|
||||
st_atime: 0,
|
||||
st_atime_nsec: 0,
|
||||
st_mtime: 0,
|
||||
st_mtime_nsec: 0,
|
||||
st_ctime: 0,
|
||||
st_ctime_nsec: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform ioctl operation
|
||||
pub fn ioctl(&self, cmd: u32, arg: usize) -> Result<isize> {
|
||||
if let Some(ref ops) = self.f_op {
|
||||
ops.ioctl(self, cmd, arg)
|
||||
} else {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory map file
|
||||
pub fn mmap(&self, vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
if let Some(ref ops) = self.f_op {
|
||||
ops.mmap(self, vma)
|
||||
} else {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync file to storage
|
||||
pub fn fsync(&self, datasync: bool) -> Result<()> {
|
||||
if let Some(ref ops) = self.f_op {
|
||||
ops.fsync(self, datasync)
|
||||
} else {
|
||||
Ok(()) // No-op for files without sync
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll file for events
|
||||
pub fn poll(&self, wait: &mut super::PollWait) -> Result<u32> {
|
||||
if let Some(ref ops) = self.f_op {
|
||||
ops.poll(self, wait)
|
||||
} else {
|
||||
Ok(super::POLLIN | super::POLLOUT) // Always ready
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current file position
|
||||
pub fn get_pos(&self) -> i64 {
|
||||
self.pos.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Set file position
|
||||
pub fn set_pos(&self, pos: i64) {
|
||||
self.pos.store(pos, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Get file flags
|
||||
pub fn get_flags(&self) -> u32 {
|
||||
self.flags.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Check if file is readable
|
||||
pub fn is_readable(&self) -> bool {
|
||||
let flags = self.get_flags();
|
||||
(flags & super::flags::O_ACCMODE) != super::flags::O_WRONLY
|
||||
}
|
||||
|
||||
/// Check if file is writable
|
||||
pub fn is_writable(&self) -> bool {
|
||||
let flags = self.get_flags();
|
||||
(flags & super::flags::O_ACCMODE) != super::flags::O_RDONLY
|
||||
}
|
||||
|
||||
/// Increment reference count
|
||||
pub fn get_file(&self) -> Result<()> {
|
||||
self.refcount.fetch_add(1, Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decrement reference count (fput equivalent)
|
||||
pub fn put_file(&self) -> Result<()> {
|
||||
let old_count = self.refcount.fetch_sub(1, Ordering::Relaxed);
|
||||
if old_count == 1 {
|
||||
// Last reference, file should be cleaned up
|
||||
// TODO: Call release operation if present
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for File {}
|
||||
unsafe impl Sync for File {}
|
||||
|
||||
/// File operations trait - similar to Linux file_operations
|
||||
pub trait FileOperations: Send + Sync {
|
||||
/// Read from file
|
||||
fn read(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize>;
|
||||
|
||||
/// Write to file
|
||||
fn write(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize>;
|
||||
|
||||
/// Seek within file
|
||||
fn seek(&self, file: &File, offset: i64, whence: i32) -> Result<i64>;
|
||||
|
||||
/// I/O control
|
||||
fn ioctl(&self, file: &File, cmd: u32, arg: usize) -> Result<isize>;
|
||||
|
||||
/// Memory map
|
||||
fn mmap(&self, file: &File, vma: &mut crate::memory::VmaArea) -> Result<()>;
|
||||
|
||||
/// Sync file
|
||||
fn fsync(&self, file: &File, datasync: bool) -> Result<()>;
|
||||
|
||||
/// Poll for events
|
||||
fn poll(&self, file: &File, wait: &mut super::PollWait) -> Result<u32>;
|
||||
|
||||
/// Open file (optional)
|
||||
fn open(&self, inode: &super::Inode, file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Release file (optional)
|
||||
fn release(&self, inode: &super::Inode, file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Flush file (optional)
|
||||
fn flush(&self, file: &File) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lock file (optional)
|
||||
fn lock(&self, file: &File, cmd: i32) -> Result<()> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
/// Read directory entries (optional)
|
||||
fn readdir(&self, file: &File, ctx: &mut super::DirContext) -> Result<()> {
|
||||
Err(Error::ENOTDIR)
|
||||
}
|
||||
}
|
||||
|
||||
/// Directory context for readdir operations
|
||||
pub struct DirContext {
|
||||
/// Current position in directory
|
||||
pub pos: i64,
|
||||
/// Entries collected so far
|
||||
pub entries: alloc::vec::Vec<super::DirEntry>,
|
||||
}
|
||||
|
||||
impl DirContext {
|
||||
pub fn new(pos: i64) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
entries: alloc::vec::Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a directory entry
|
||||
pub fn add_entry(&mut self, ino: u64, name: &str, d_type: u8) {
|
||||
let entry = super::DirEntry {
|
||||
ino,
|
||||
off: self.pos,
|
||||
reclen: (core::mem::size_of::<super::DirEntry>() + name.len() + 1) as u16,
|
||||
name: String::from(name),
|
||||
d_type,
|
||||
};
|
||||
self.entries.push(entry);
|
||||
self.pos += 1;
|
||||
}
|
||||
}
|
||||
421
kernel/src/fs/inode.rs
Archivo normal
421
kernel/src/fs/inode.rs
Archivo normal
@@ -0,0 +1,421 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Inode abstraction - Linux compatible
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::types::*;
|
||||
use crate::sync::{Arc, Mutex, RwLock};
|
||||
use crate::device::DeviceNumber;
|
||||
use crate::time::{TimeSpec, get_current_time};
|
||||
use alloc::string::String;
|
||||
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
|
||||
|
||||
/// Inode structure - similar to Linux struct inode
|
||||
pub struct Inode {
|
||||
/// Inode number
|
||||
pub i_ino: u64,
|
||||
/// File mode and type
|
||||
pub i_mode: AtomicU32,
|
||||
/// Number of hard links
|
||||
pub i_nlink: AtomicU32,
|
||||
/// User ID
|
||||
pub i_uid: AtomicU32,
|
||||
/// Group ID
|
||||
pub i_gid: AtomicU32,
|
||||
/// Device number (for device files)
|
||||
pub i_rdev: DeviceNumber,
|
||||
/// File size
|
||||
pub i_size: AtomicU64,
|
||||
/// Block size
|
||||
pub i_blksize: u32,
|
||||
/// Number of blocks
|
||||
pub i_blocks: AtomicU64,
|
||||
/// Access time
|
||||
pub i_atime: Mutex<TimeSpec>,
|
||||
/// Modification time
|
||||
pub i_mtime: Mutex<TimeSpec>,
|
||||
/// Status change time
|
||||
pub i_ctime: Mutex<TimeSpec>,
|
||||
/// Inode operations
|
||||
pub i_op: Option<Arc<dyn InodeOperations>>,
|
||||
/// File operations (for regular files)
|
||||
pub i_fop: Option<Arc<dyn super::FileOperations>>,
|
||||
/// Superblock this inode belongs to
|
||||
pub i_sb: Option<Arc<super::SuperBlock>>,
|
||||
/// Private data
|
||||
pub private_data: Option<*mut u8>,
|
||||
/// Reference count
|
||||
pub refcount: AtomicU32,
|
||||
/// Inode flags
|
||||
pub i_flags: AtomicU32,
|
||||
}
|
||||
|
||||
impl Inode {
|
||||
/// Create a new inode
|
||||
pub fn new(ino: u64, mode: u32) -> Self {
|
||||
let now = get_current_time();
|
||||
Self {
|
||||
i_ino: ino,
|
||||
i_mode: AtomicU32::new(mode),
|
||||
i_nlink: AtomicU32::new(1),
|
||||
i_uid: AtomicU32::new(0),
|
||||
i_gid: AtomicU32::new(0),
|
||||
i_rdev: DeviceNumber::new(0, 0),
|
||||
i_size: AtomicU64::new(0),
|
||||
i_blksize: 4096,
|
||||
i_blocks: AtomicU64::new(0),
|
||||
i_atime: Mutex::new(now),
|
||||
i_mtime: Mutex::new(now),
|
||||
i_ctime: Mutex::new(now),
|
||||
i_op: None,
|
||||
i_fop: None,
|
||||
i_sb: None,
|
||||
private_data: None,
|
||||
refcount: AtomicU32::new(1),
|
||||
i_flags: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set inode operations
|
||||
pub fn set_operations(&mut self, ops: Arc<dyn InodeOperations>) {
|
||||
self.i_op = Some(ops);
|
||||
}
|
||||
|
||||
/// Set file operations
|
||||
pub fn set_file_operations(&mut self, ops: Arc<dyn super::FileOperations>) {
|
||||
self.i_fop = Some(ops);
|
||||
}
|
||||
|
||||
/// Get file statistics
|
||||
pub fn stat(&self) -> Result<super::KStat> {
|
||||
let atime = self.i_atime.lock();
|
||||
let mtime = self.i_mtime.lock();
|
||||
let ctime = self.i_ctime.lock();
|
||||
|
||||
Ok(super::KStat {
|
||||
st_dev: if let Some(ref sb) = self.i_sb {
|
||||
sb.s_dev.as_raw()
|
||||
} else {
|
||||
0
|
||||
},
|
||||
st_ino: self.i_ino,
|
||||
st_nlink: self.i_nlink.load(Ordering::Relaxed) as u64,
|
||||
st_mode: self.i_mode.load(Ordering::Relaxed),
|
||||
st_uid: self.i_uid.load(Ordering::Relaxed),
|
||||
st_gid: self.i_gid.load(Ordering::Relaxed),
|
||||
st_rdev: self.i_rdev.as_raw(),
|
||||
st_size: self.i_size.load(Ordering::Relaxed) as i64,
|
||||
st_blksize: self.i_blksize as u64,
|
||||
st_blocks: self.i_blocks.load(Ordering::Relaxed),
|
||||
st_atime: atime.sec,
|
||||
st_atime_nsec: atime.nsec,
|
||||
st_mtime: mtime.sec,
|
||||
st_mtime_nsec: mtime.nsec,
|
||||
st_ctime: ctime.sec,
|
||||
st_ctime_nsec: ctime.nsec,
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if inode is a regular file
|
||||
pub fn is_regular(&self) -> bool {
|
||||
let mode = self.i_mode.load(Ordering::Relaxed);
|
||||
super::mode::s_isreg(mode)
|
||||
}
|
||||
|
||||
/// Check if inode is a directory
|
||||
pub fn is_directory(&self) -> bool {
|
||||
let mode = self.i_mode.load(Ordering::Relaxed);
|
||||
super::mode::s_isdir(mode)
|
||||
}
|
||||
|
||||
/// Check if inode is a character device
|
||||
pub fn is_char_device(&self) -> bool {
|
||||
let mode = self.i_mode.load(Ordering::Relaxed);
|
||||
super::mode::s_ischr(mode)
|
||||
}
|
||||
|
||||
/// Check if inode is a block device
|
||||
pub fn is_block_device(&self) -> bool {
|
||||
let mode = self.i_mode.load(Ordering::Relaxed);
|
||||
super::mode::s_isblk(mode)
|
||||
}
|
||||
|
||||
/// Update access time
|
||||
pub fn update_atime(&self) {
|
||||
let mut atime = self.i_atime.lock();
|
||||
*atime = get_current_time();
|
||||
}
|
||||
|
||||
/// Update modification time
|
||||
pub fn update_mtime(&self) {
|
||||
let mut mtime = self.i_mtime.lock();
|
||||
*mtime = get_current_time();
|
||||
}
|
||||
|
||||
/// Update status change time
|
||||
pub fn update_ctime(&self) {
|
||||
let mut ctime = self.i_ctime.lock();
|
||||
*ctime = get_current_time();
|
||||
}
|
||||
|
||||
/// Set file size
|
||||
pub fn set_size(&self, size: u64) {
|
||||
self.i_size.store(size, Ordering::Relaxed);
|
||||
self.update_mtime();
|
||||
self.update_ctime();
|
||||
}
|
||||
|
||||
/// Get file size
|
||||
pub fn get_size(&self) -> u64 {
|
||||
self.i_size.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Increment reference count
|
||||
pub fn iget(&self) {
|
||||
self.refcount.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Decrement reference count
|
||||
pub fn iput(&self) {
|
||||
let old_count = self.refcount.fetch_sub(1, Ordering::Relaxed);
|
||||
if old_count == 1 {
|
||||
// Last reference, inode should be cleaned up
|
||||
// TODO: Call destroy_inode operation if present
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new file in this directory
|
||||
pub fn create(&self, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
if let Some(ref ops) = self.i_op {
|
||||
ops.create(self, name, mode)
|
||||
} else {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Look up a file in this directory
|
||||
pub fn lookup(&self, name: &str) -> Result<Arc<Inode>> {
|
||||
if let Some(ref ops) = self.i_op {
|
||||
ops.lookup(self, name)
|
||||
} else {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a directory
|
||||
pub fn mkdir(&self, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
if let Some(ref ops) = self.i_op {
|
||||
ops.mkdir(self, name, mode)
|
||||
} else {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a file
|
||||
pub fn unlink(&self, name: &str) -> Result<()> {
|
||||
if let Some(ref ops) = self.i_op {
|
||||
ops.unlink(self, name)
|
||||
} else {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a directory
|
||||
pub fn rmdir(&self, name: &str) -> Result<()> {
|
||||
if let Some(ref ops) = self.i_op {
|
||||
ops.rmdir(self, name)
|
||||
} else {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Inode {}
|
||||
unsafe impl Sync for Inode {}
|
||||
|
||||
/// Inode operations trait - similar to Linux inode_operations
|
||||
pub trait InodeOperations: Send + Sync {
|
||||
/// Look up a file in directory
|
||||
fn lookup(&self, dir: &Inode, name: &str) -> Result<Arc<Inode>>;
|
||||
|
||||
/// Create a new file
|
||||
fn create(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>>;
|
||||
|
||||
/// Create a directory
|
||||
fn mkdir(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>>;
|
||||
|
||||
/// Remove a file
|
||||
fn unlink(&self, dir: &Inode, name: &str) -> Result<()>;
|
||||
|
||||
/// Remove a directory
|
||||
fn rmdir(&self, dir: &Inode, name: &str) -> Result<()>;
|
||||
|
||||
/// Create a symbolic link
|
||||
fn symlink(&self, dir: &Inode, name: &str, target: &str) -> Result<Arc<Inode>>;
|
||||
|
||||
/// Rename a file
|
||||
fn rename(&self, old_dir: &Inode, old_name: &str, new_dir: &Inode, new_name: &str) -> Result<()>;
|
||||
|
||||
/// Set attributes
|
||||
fn setattr(&self, inode: &Inode, attr: &InodeAttr) -> Result<()>;
|
||||
|
||||
/// Get attributes
|
||||
fn getattr(&self, inode: &Inode) -> Result<InodeAttr>;
|
||||
|
||||
/// Read symbolic link
|
||||
fn readlink(&self, inode: &Inode) -> Result<String>;
|
||||
|
||||
/// Follow symbolic link
|
||||
fn follow_link(&self, inode: &Inode) -> Result<Arc<Inode>>;
|
||||
|
||||
/// Truncate file
|
||||
fn truncate(&self, inode: &Inode, size: u64) -> Result<()>;
|
||||
|
||||
/// Get extended attribute
|
||||
fn getxattr(&self, inode: &Inode, name: &str) -> Result<alloc::vec::Vec<u8>>;
|
||||
|
||||
/// Set extended attribute
|
||||
fn setxattr(&self, inode: &Inode, name: &str, value: &[u8], flags: u32) -> Result<()>;
|
||||
|
||||
/// List extended attributes
|
||||
fn listxattr(&self, inode: &Inode) -> Result<alloc::vec::Vec<String>>;
|
||||
|
||||
/// Remove extended attribute
|
||||
fn removexattr(&self, inode: &Inode, name: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Inode attributes structure
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct InodeAttr {
|
||||
pub mode: Option<u32>,
|
||||
pub uid: Option<u32>,
|
||||
pub gid: Option<u32>,
|
||||
pub size: Option<u64>,
|
||||
pub atime: Option<TimeSpec>,
|
||||
pub mtime: Option<TimeSpec>,
|
||||
pub ctime: Option<TimeSpec>,
|
||||
}
|
||||
|
||||
impl InodeAttr {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mode: None,
|
||||
uid: None,
|
||||
gid: None,
|
||||
size: None,
|
||||
atime: None,
|
||||
mtime: None,
|
||||
ctime: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_mode(mut self, mode: u32) -> Self {
|
||||
self.mode = Some(mode);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_size(mut self, size: u64) -> Self {
|
||||
self.size = Some(size);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic inode operations for simple filesystems
|
||||
pub struct GenericInodeOps;
|
||||
|
||||
impl InodeOperations for GenericInodeOps {
|
||||
fn lookup(&self, dir: &Inode, name: &str) -> Result<Arc<Inode>> {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
|
||||
fn create(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
fn mkdir(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
fn unlink(&self, dir: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
fn rmdir(&self, dir: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
fn symlink(&self, dir: &Inode, name: &str, target: &str) -> Result<Arc<Inode>> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
fn rename(&self, old_dir: &Inode, old_name: &str, new_dir: &Inode, new_name: &str) -> Result<()> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
fn setattr(&self, inode: &Inode, attr: &InodeAttr) -> Result<()> {
|
||||
// Apply basic attributes
|
||||
if let Some(mode) = attr.mode {
|
||||
inode.i_mode.store(mode, Ordering::Relaxed);
|
||||
}
|
||||
if let Some(uid) = attr.uid {
|
||||
inode.i_uid.store(uid, Ordering::Relaxed);
|
||||
}
|
||||
if let Some(gid) = attr.gid {
|
||||
inode.i_gid.store(gid, Ordering::Relaxed);
|
||||
}
|
||||
if let Some(size) = attr.size {
|
||||
inode.set_size(size);
|
||||
}
|
||||
if let Some(atime) = attr.atime {
|
||||
*inode.i_atime.lock() = atime;
|
||||
}
|
||||
if let Some(mtime) = attr.mtime {
|
||||
*inode.i_mtime.lock() = mtime;
|
||||
}
|
||||
if let Some(ctime) = attr.ctime {
|
||||
*inode.i_ctime.lock() = ctime;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn getattr(&self, inode: &Inode) -> Result<InodeAttr> {
|
||||
Ok(InodeAttr {
|
||||
mode: Some(inode.i_mode.load(Ordering::Relaxed)),
|
||||
uid: Some(inode.i_uid.load(Ordering::Relaxed)),
|
||||
gid: Some(inode.i_gid.load(Ordering::Relaxed)),
|
||||
size: Some(inode.i_size.load(Ordering::Relaxed)),
|
||||
atime: Some(*inode.i_atime.lock()),
|
||||
mtime: Some(*inode.i_mtime.lock()),
|
||||
ctime: Some(*inode.i_ctime.lock()),
|
||||
})
|
||||
}
|
||||
|
||||
fn readlink(&self, inode: &Inode) -> Result<String> {
|
||||
Err(Error::EINVAL)
|
||||
}
|
||||
|
||||
fn follow_link(&self, inode: &Inode) -> Result<Arc<Inode>> {
|
||||
Err(Error::EINVAL)
|
||||
}
|
||||
|
||||
fn truncate(&self, inode: &Inode, size: u64) -> Result<()> {
|
||||
inode.set_size(size);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn getxattr(&self, inode: &Inode, name: &str) -> Result<alloc::vec::Vec<u8>> {
|
||||
Err(Error::ENODATA)
|
||||
}
|
||||
|
||||
fn setxattr(&self, inode: &Inode, name: &str, value: &[u8], flags: u32) -> Result<()> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
fn listxattr(&self, inode: &Inode) -> Result<alloc::vec::Vec<String>> {
|
||||
Ok(alloc::vec::Vec::new())
|
||||
}
|
||||
|
||||
fn removexattr(&self, inode: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::ENODATA)
|
||||
}
|
||||
}
|
||||
407
kernel/src/fs/mod.rs
Archivo normal
407
kernel/src/fs/mod.rs
Archivo normal
@@ -0,0 +1,407 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Virtual File System (VFS) - Linux compatible
|
||||
//!
|
||||
//! This module provides the core filesystem abstractions and compatibility
|
||||
//! with Linux VFS operations.
|
||||
|
||||
pub mod file;
|
||||
pub mod inode;
|
||||
pub mod dentry;
|
||||
pub mod super_block;
|
||||
pub mod mount;
|
||||
pub mod path;
|
||||
pub mod operations;
|
||||
pub mod ramfs;
|
||||
pub mod procfs;
|
||||
pub mod devfs;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::types::*;
|
||||
use crate::sync::{Arc, Mutex, RwLock};
|
||||
use crate::memory::{UserPtr, UserSlicePtr};
|
||||
use crate::device::DeviceNumber;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::string::String;
|
||||
use alloc::collections::BTreeMap;
|
||||
use core::fmt;
|
||||
|
||||
pub use file::*;
|
||||
pub use inode::*;
|
||||
pub use dentry::*;
|
||||
pub use super_block::*;
|
||||
pub use mount::*;
|
||||
pub use path::*;
|
||||
pub use operations::*;
|
||||
|
||||
/// File access modes - Linux compatible
|
||||
pub mod flags {
|
||||
pub const O_ACCMODE: u32 = 0o00000003;
|
||||
pub const O_RDONLY: u32 = 0o00000000;
|
||||
pub const O_WRONLY: u32 = 0o00000001;
|
||||
pub const O_RDWR: u32 = 0o00000002;
|
||||
pub const O_CREAT: u32 = 0o00000100;
|
||||
pub const O_EXCL: u32 = 0o00000200;
|
||||
pub const O_NOCTTY: u32 = 0o00000400;
|
||||
pub const O_TRUNC: u32 = 0o00001000;
|
||||
pub const O_APPEND: u32 = 0o00002000;
|
||||
pub const O_NONBLOCK: u32 = 0o00004000;
|
||||
pub const O_DSYNC: u32 = 0o00010000;
|
||||
pub const O_FASYNC: u32 = 0o00020000;
|
||||
pub const O_DIRECT: u32 = 0o00040000;
|
||||
pub const O_LARGEFILE: u32 = 0o00100000;
|
||||
pub const O_DIRECTORY: u32 = 0o00200000;
|
||||
pub const O_NOFOLLOW: u32 = 0o00400000;
|
||||
pub const O_NOATIME: u32 = 0o01000000;
|
||||
pub const O_CLOEXEC: u32 = 0o02000000;
|
||||
pub const O_SYNC: u32 = 0o04000000 | O_DSYNC;
|
||||
pub const O_PATH: u32 = 0o10000000;
|
||||
pub const O_TMPFILE: u32 = 0o20000000 | O_DIRECTORY;
|
||||
}
|
||||
|
||||
/// File mode constants - Linux compatible
|
||||
pub mod mode {
|
||||
pub const S_IFMT: u32 = 0o170000;
|
||||
pub const S_IFSOCK: u32 = 0o140000;
|
||||
pub const S_IFLNK: u32 = 0o120000;
|
||||
pub const S_IFREG: u32 = 0o100000;
|
||||
pub const S_IFBLK: u32 = 0o060000;
|
||||
pub const S_IFDIR: u32 = 0o040000;
|
||||
pub const S_IFCHR: u32 = 0o020000;
|
||||
pub const S_IFIFO: u32 = 0o010000;
|
||||
|
||||
pub const S_ISUID: u32 = 0o004000;
|
||||
pub const S_ISGID: u32 = 0o002000;
|
||||
pub const S_ISVTX: u32 = 0o001000;
|
||||
|
||||
pub const S_IRWXU: u32 = 0o000700;
|
||||
pub const S_IRUSR: u32 = 0o000400;
|
||||
pub const S_IWUSR: u32 = 0o000200;
|
||||
pub const S_IXUSR: u32 = 0o000100;
|
||||
|
||||
pub const S_IRWXG: u32 = 0o000070;
|
||||
pub const S_IRGRP: u32 = 0o000040;
|
||||
pub const S_IWGRP: u32 = 0o000020;
|
||||
pub const S_IXGRP: u32 = 0o000010;
|
||||
|
||||
pub const S_IRWXO: u32 = 0o000007;
|
||||
pub const S_IROTH: u32 = 0o000004;
|
||||
pub const S_IWOTH: u32 = 0o000002;
|
||||
pub const S_IXOTH: u32 = 0o000001;
|
||||
}
|
||||
|
||||
/// File type helper functions
|
||||
impl mode {
|
||||
pub fn s_isreg(mode: u32) -> bool {
|
||||
(mode & S_IFMT) == S_IFREG
|
||||
}
|
||||
|
||||
pub fn s_isdir(mode: u32) -> bool {
|
||||
(mode & S_IFMT) == S_IFDIR
|
||||
}
|
||||
|
||||
pub fn s_ischr(mode: u32) -> bool {
|
||||
(mode & S_IFMT) == S_IFCHR
|
||||
}
|
||||
|
||||
pub fn s_isblk(mode: u32) -> bool {
|
||||
(mode & S_IFMT) == S_IFBLK
|
||||
}
|
||||
|
||||
pub fn s_isfifo(mode: u32) -> bool {
|
||||
(mode & S_IFMT) == S_IFIFO
|
||||
}
|
||||
|
||||
pub fn s_islnk(mode: u32) -> bool {
|
||||
(mode & S_IFMT) == S_IFLNK
|
||||
}
|
||||
|
||||
pub fn s_issock(mode: u32) -> bool {
|
||||
(mode & S_IFMT) == S_IFSOCK
|
||||
}
|
||||
}
|
||||
|
||||
/// Seek constants
|
||||
pub const SEEK_SET: i32 = 0;
|
||||
pub const SEEK_CUR: i32 = 1;
|
||||
pub const SEEK_END: i32 = 2;
|
||||
pub const SEEK_DATA: i32 = 3;
|
||||
pub const SEEK_HOLE: i32 = 4;
|
||||
|
||||
/// Maximum filename length
|
||||
pub const NAME_MAX: usize = 255;
|
||||
|
||||
/// Maximum path length
|
||||
pub const PATH_MAX: usize = 4096;
|
||||
|
||||
/// File system statistics structure
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct KStatFs {
|
||||
pub f_type: u64,
|
||||
pub f_bsize: u64,
|
||||
pub f_blocks: u64,
|
||||
pub f_bfree: u64,
|
||||
pub f_bavail: u64,
|
||||
pub f_files: u64,
|
||||
pub f_ffree: u64,
|
||||
pub f_fsid: [u32; 2],
|
||||
pub f_namelen: u64,
|
||||
pub f_frsize: u64,
|
||||
pub f_flags: u64,
|
||||
pub f_spare: [u64; 4],
|
||||
}
|
||||
|
||||
/// File attributes structure
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct KStat {
|
||||
pub st_dev: u64,
|
||||
pub st_ino: u64,
|
||||
pub st_nlink: u64,
|
||||
pub st_mode: u32,
|
||||
pub st_uid: u32,
|
||||
pub st_gid: u32,
|
||||
pub st_rdev: u64,
|
||||
pub st_size: i64,
|
||||
pub st_blksize: u64,
|
||||
pub st_blocks: u64,
|
||||
pub st_atime: i64,
|
||||
pub st_atime_nsec: i64,
|
||||
pub st_mtime: i64,
|
||||
pub st_mtime_nsec: i64,
|
||||
pub st_ctime: i64,
|
||||
pub st_ctime_nsec: i64,
|
||||
}
|
||||
|
||||
/// I/O vector for scatter-gather I/O
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct IoVec {
|
||||
pub iov_base: *mut u8,
|
||||
pub iov_len: usize,
|
||||
}
|
||||
|
||||
/// Directory entry type returned by readdir
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DirEntry {
|
||||
pub ino: u64,
|
||||
pub off: i64,
|
||||
pub reclen: u16,
|
||||
pub name: String,
|
||||
pub d_type: u8,
|
||||
}
|
||||
|
||||
/// Directory entry types
|
||||
pub const DT_UNKNOWN: u8 = 0;
|
||||
pub const DT_FIFO: u8 = 1;
|
||||
pub const DT_CHR: u8 = 2;
|
||||
pub const DT_DIR: u8 = 4;
|
||||
pub const DT_BLK: u8 = 6;
|
||||
pub const DT_REG: u8 = 8;
|
||||
pub const DT_LNK: u8 = 10;
|
||||
pub const DT_SOCK: u8 = 12;
|
||||
pub const DT_WHT: u8 = 14;
|
||||
|
||||
/// Global VFS state
|
||||
static VFS: Mutex<Vfs> = Mutex::new(Vfs::new());
|
||||
|
||||
/// Virtual File System state
|
||||
pub struct Vfs {
|
||||
/// Mounted filesystems
|
||||
pub mounts: Vec<Arc<VfsMount>>,
|
||||
/// Root dentry
|
||||
pub root: Option<Arc<Dentry>>,
|
||||
/// File descriptor table (per-process will be separate)
|
||||
pub fd_table: BTreeMap<i32, Arc<File>>,
|
||||
/// Next file descriptor number
|
||||
pub next_fd: i32,
|
||||
}
|
||||
|
||||
impl Vfs {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
mounts: Vec::new(),
|
||||
root: None,
|
||||
fd_table: BTreeMap::new(),
|
||||
next_fd: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Mount a filesystem
|
||||
pub fn mount(
|
||||
&mut self,
|
||||
source: &str,
|
||||
target: &str,
|
||||
fstype: &str,
|
||||
flags: u32,
|
||||
data: Option<&str>,
|
||||
) -> Result<()> {
|
||||
// TODO: Implement proper mount logic
|
||||
// For now, just create a basic mount
|
||||
let sb = Arc::new(SuperBlock::new(fstype)?);
|
||||
let mount = Arc::new(VfsMount::new(sb, target, flags)?);
|
||||
self.mounts.push(mount);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Allocate a new file descriptor
|
||||
pub fn alloc_fd(&mut self) -> i32 {
|
||||
let fd = self.next_fd;
|
||||
self.next_fd += 1;
|
||||
fd
|
||||
}
|
||||
|
||||
/// Install a file into the file descriptor table
|
||||
pub fn install_fd(&mut self, fd: i32, file: Arc<File>) {
|
||||
self.fd_table.insert(fd, file);
|
||||
}
|
||||
|
||||
/// Get a file by file descriptor
|
||||
pub fn get_file(&self, fd: i32) -> Option<Arc<File>> {
|
||||
self.fd_table.get(&fd).cloned()
|
||||
}
|
||||
|
||||
/// Close a file descriptor
|
||||
pub fn close_fd(&mut self, fd: i32) -> Result<()> {
|
||||
self.fd_table.remove(&fd);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the VFS subsystem
|
||||
pub fn init() -> Result<()> {
|
||||
// Register built-in filesystems
|
||||
ramfs::register_ramfs()?;
|
||||
procfs::register_procfs()?;
|
||||
devfs::register_devfs()?;
|
||||
|
||||
// Create initial mounts
|
||||
mount::do_mount("none", "/", "ramfs", 0, None)?;
|
||||
|
||||
// Create essential directories
|
||||
// TODO: Create /proc and /dev directories in root filesystem
|
||||
|
||||
mount::do_mount("proc", "/proc", "proc", 0, None)?;
|
||||
mount::do_mount("devfs", "/dev", "devfs", 0, None)?;
|
||||
|
||||
crate::console::print_info("VFS: Initialized virtual file system\n");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Open a file - Linux compatible sys_open
|
||||
pub fn open(pathname: &str, flags: i32, mode: u32) -> Result<i32> {
|
||||
let mut vfs = VFS.lock();
|
||||
|
||||
// TODO: Path resolution, permission checks, etc.
|
||||
// For now, create a simple file
|
||||
let file = Arc::new(File::new(pathname, flags as u32, mode)?);
|
||||
let fd = vfs.alloc_fd();
|
||||
vfs.install_fd(fd, file);
|
||||
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
/// Close a file descriptor - Linux compatible sys_close
|
||||
pub fn close(fd: i32) -> Result<()> {
|
||||
let mut vfs = VFS.lock();
|
||||
vfs.close_fd(fd)
|
||||
}
|
||||
|
||||
/// Read from a file descriptor - Linux compatible sys_read
|
||||
pub fn read(fd: i32, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
let vfs = VFS.lock();
|
||||
let file = vfs.get_file(fd).ok_or(Error::EBADF)?;
|
||||
drop(vfs);
|
||||
|
||||
file.read(buf, count)
|
||||
}
|
||||
|
||||
/// Write to a file descriptor - Linux compatible sys_write
|
||||
pub fn write(fd: i32, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
let vfs = VFS.lock();
|
||||
let file = vfs.get_file(fd).ok_or(Error::EBADF)?;
|
||||
drop(vfs);
|
||||
|
||||
file.write(buf, count)
|
||||
}
|
||||
|
||||
/// Seek within a file - Linux compatible sys_lseek
|
||||
pub fn lseek(fd: i32, offset: i64, whence: i32) -> Result<i64> {
|
||||
let vfs = VFS.lock();
|
||||
let file = vfs.get_file(fd).ok_or(Error::EBADF)?;
|
||||
drop(vfs);
|
||||
|
||||
file.seek(offset, whence)
|
||||
}
|
||||
|
||||
/// Get file status - Linux compatible sys_fstat
|
||||
pub fn fstat(fd: i32, statbuf: UserPtr<KStat>) -> Result<()> {
|
||||
let vfs = VFS.lock();
|
||||
let file = vfs.get_file(fd).ok_or(Error::EBADF)?;
|
||||
drop(vfs);
|
||||
|
||||
let stat = file.stat()?;
|
||||
statbuf.write(stat)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generic file operations for simple filesystems
|
||||
pub struct GenericFileOps;
|
||||
|
||||
impl FileOperations for GenericFileOps {
|
||||
fn read(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Default read implementation
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Default write implementation
|
||||
Ok(count as isize)
|
||||
}
|
||||
|
||||
fn seek(&self, file: &File, offset: i64, whence: i32) -> Result<i64> {
|
||||
// Default seek implementation
|
||||
match whence {
|
||||
SEEK_SET => Ok(offset),
|
||||
SEEK_CUR => Ok(file.pos.load(core::sync::atomic::Ordering::Relaxed) + offset),
|
||||
SEEK_END => Ok(offset), // TODO: Get file size
|
||||
_ => Err(Error::EINVAL),
|
||||
}
|
||||
}
|
||||
|
||||
fn ioctl(&self, file: &File, cmd: u32, arg: usize) -> Result<isize> {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, file: &File, vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn fsync(&self, file: &File, datasync: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, file: &File, wait: &mut PollWait) -> Result<u32> {
|
||||
Ok(POLLIN | POLLOUT)
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll events
|
||||
pub const POLLIN: u32 = 0x001;
|
||||
pub const POLLPRI: u32 = 0x002;
|
||||
pub const POLLOUT: u32 = 0x004;
|
||||
pub const POLLERR: u32 = 0x008;
|
||||
pub const POLLHUP: u32 = 0x010;
|
||||
pub const POLLNVAL: u32 = 0x020;
|
||||
|
||||
/// Poll wait structure (simplified)
|
||||
pub struct PollWait {
|
||||
// TODO: Implement proper poll/select mechanism
|
||||
}
|
||||
|
||||
impl PollWait {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
295
kernel/src/fs/mount.rs
Archivo normal
295
kernel/src/fs/mount.rs
Archivo normal
@@ -0,0 +1,295 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! VFS mount abstraction - Linux compatible
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sync::{Arc, Mutex, RwLock};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
/// VFS mount structure - similar to Linux struct vfsmount
|
||||
pub struct VfsMount {
|
||||
/// Mounted superblock
|
||||
pub mnt_sb: Arc<super::SuperBlock>,
|
||||
/// Mount point path
|
||||
pub mnt_mountpoint: String,
|
||||
/// Mount flags
|
||||
pub mnt_flags: AtomicU32,
|
||||
/// Parent mount
|
||||
pub mnt_parent: Option<Arc<VfsMount>>,
|
||||
/// Child mounts
|
||||
pub mnt_children: Mutex<Vec<Arc<VfsMount>>>,
|
||||
/// Reference count
|
||||
pub mnt_count: AtomicU32,
|
||||
/// Device name
|
||||
pub mnt_devname: Option<String>,
|
||||
/// Mount options
|
||||
pub mnt_opts: Option<String>,
|
||||
}
|
||||
|
||||
impl VfsMount {
|
||||
/// Create a new VFS mount
|
||||
pub fn new(sb: Arc<super::SuperBlock>, mountpoint: &str, flags: u32) -> Result<Self> {
|
||||
Ok(Self {
|
||||
mnt_sb: sb,
|
||||
mnt_mountpoint: String::from(mountpoint),
|
||||
mnt_flags: AtomicU32::new(flags),
|
||||
mnt_parent: None,
|
||||
mnt_children: Mutex::new(Vec::new()),
|
||||
mnt_count: AtomicU32::new(1),
|
||||
mnt_devname: None,
|
||||
mnt_opts: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Set parent mount
|
||||
pub fn set_parent(&mut self, parent: Arc<VfsMount>) {
|
||||
self.mnt_parent = Some(parent);
|
||||
}
|
||||
|
||||
/// Add child mount
|
||||
pub fn add_child(&self, child: Arc<VfsMount>) {
|
||||
let mut children = self.mnt_children.lock();
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
/// Get mount flags
|
||||
pub fn get_flags(&self) -> u32 {
|
||||
self.mnt_flags.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Check if mount is read-only
|
||||
pub fn is_readonly(&self) -> bool {
|
||||
(self.get_flags() & super::super_block::MS_RDONLY) != 0
|
||||
}
|
||||
|
||||
/// Check if mount is nosuid
|
||||
pub fn is_nosuid(&self) -> bool {
|
||||
(self.get_flags() & super::super_block::MS_NOSUID) != 0
|
||||
}
|
||||
|
||||
/// Check if mount is nodev
|
||||
pub fn is_nodev(&self) -> bool {
|
||||
(self.get_flags() & super::super_block::MS_NODEV) != 0
|
||||
}
|
||||
|
||||
/// Check if mount is noexec
|
||||
pub fn is_noexec(&self) -> bool {
|
||||
(self.get_flags() & super::super_block::MS_NOEXEC) != 0
|
||||
}
|
||||
|
||||
/// Increment reference count
|
||||
pub fn mntget(&self) {
|
||||
self.mnt_count.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Decrement reference count
|
||||
pub fn mntput(&self) {
|
||||
let old_count = self.mnt_count.fetch_sub(1, Ordering::Relaxed);
|
||||
if old_count == 1 {
|
||||
// Last reference, mount should be cleaned up
|
||||
// TODO: Unmount filesystem
|
||||
}
|
||||
}
|
||||
|
||||
/// Get full mount path
|
||||
pub fn get_path(&self) -> String {
|
||||
if let Some(ref parent) = self.mnt_parent {
|
||||
if parent.mnt_mountpoint == "/" {
|
||||
self.mnt_mountpoint.clone()
|
||||
} else {
|
||||
format!("{}{}", parent.get_path(), self.mnt_mountpoint)
|
||||
}
|
||||
} else {
|
||||
self.mnt_mountpoint.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Find child mount by path
|
||||
pub fn find_child_mount(&self, path: &str) -> Option<Arc<VfsMount>> {
|
||||
let children = self.mnt_children.lock();
|
||||
for child in children.iter() {
|
||||
if child.mnt_mountpoint == path {
|
||||
return Some(child.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for VfsMount {}
|
||||
unsafe impl Sync for VfsMount {}
|
||||
|
||||
/// Mount namespace - similar to Linux struct mnt_namespace
|
||||
pub struct MountNamespace {
|
||||
/// Root mount
|
||||
pub root: Option<Arc<VfsMount>>,
|
||||
/// All mounts in this namespace
|
||||
pub mounts: Mutex<Vec<Arc<VfsMount>>>,
|
||||
/// Namespace ID
|
||||
pub ns_id: u64,
|
||||
/// Reference count
|
||||
pub count: AtomicU32,
|
||||
}
|
||||
|
||||
impl MountNamespace {
|
||||
/// Create a new mount namespace
|
||||
pub fn new(ns_id: u64) -> Self {
|
||||
Self {
|
||||
root: None,
|
||||
mounts: Mutex::new(Vec::new()),
|
||||
ns_id,
|
||||
count: AtomicU32::new(1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add mount to namespace
|
||||
pub fn add_mount(&self, mount: Arc<VfsMount>) {
|
||||
let mut mounts = self.mounts.lock();
|
||||
mounts.push(mount);
|
||||
}
|
||||
|
||||
/// Remove mount from namespace
|
||||
pub fn remove_mount(&self, mountpoint: &str) -> Option<Arc<VfsMount>> {
|
||||
let mut mounts = self.mounts.lock();
|
||||
if let Some(pos) = mounts.iter().position(|m| m.mnt_mountpoint == mountpoint) {
|
||||
Some(mounts.remove(pos))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Find mount by path
|
||||
pub fn find_mount(&self, path: &str) -> Option<Arc<VfsMount>> {
|
||||
let mounts = self.mounts.lock();
|
||||
|
||||
// Find the longest matching mount point
|
||||
let mut best_match: Option<Arc<VfsMount>> = None;
|
||||
let mut best_len = 0;
|
||||
|
||||
for mount in mounts.iter() {
|
||||
let mount_path = mount.get_path();
|
||||
if path.starts_with(&mount_path) && mount_path.len() > best_len {
|
||||
best_match = Some(mount.clone());
|
||||
best_len = mount_path.len();
|
||||
}
|
||||
}
|
||||
|
||||
best_match
|
||||
}
|
||||
|
||||
/// Get all mount points
|
||||
pub fn get_mount_points(&self) -> Vec<String> {
|
||||
let mounts = self.mounts.lock();
|
||||
mounts.iter().map(|m| m.get_path()).collect()
|
||||
}
|
||||
|
||||
/// Set root mount
|
||||
pub fn set_root(&mut self, root: Arc<VfsMount>) {
|
||||
self.root = Some(root.clone());
|
||||
self.add_mount(root);
|
||||
}
|
||||
}
|
||||
|
||||
/// Global mount namespace
|
||||
static INIT_MNT_NS: once_cell::sync::Lazy<Mutex<MountNamespace>> =
|
||||
once_cell::sync::Lazy::new(|| Mutex::new(MountNamespace::new(1)));
|
||||
|
||||
/// Get the init mount namespace
|
||||
pub fn get_init_ns() -> &'static Mutex<MountNamespace> {
|
||||
&INIT_MNT_NS
|
||||
}
|
||||
|
||||
/// Mount a filesystem
|
||||
pub fn do_mount(
|
||||
dev_name: &str,
|
||||
dir_name: &str,
|
||||
type_name: &str,
|
||||
flags: u32,
|
||||
data: Option<&str>,
|
||||
) -> Result<()> {
|
||||
// TODO: Look up filesystem type
|
||||
// For now, create a basic mount
|
||||
let sb = Arc::new(super::SuperBlock::new(type_name)?);
|
||||
let mount = Arc::new(VfsMount::new(sb, dir_name, flags)?);
|
||||
|
||||
let ns = get_init_ns();
|
||||
let mut ns = ns.lock();
|
||||
ns.add_mount(mount);
|
||||
|
||||
crate::console::print_info(&format!("Mounted {} on {} (type {})\n", dev_name, dir_name, type_name));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unmount a filesystem
|
||||
pub fn do_umount(dir_name: &str, flags: u32) -> Result<()> {
|
||||
let ns = get_init_ns();
|
||||
let mut ns = ns.lock();
|
||||
|
||||
if let Some(mount) = ns.remove_mount(dir_name) {
|
||||
mount.mntput();
|
||||
crate::console::print_info(&format!("Unmounted {}\n", dir_name));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get mount information for a path
|
||||
pub fn path_get_mount(path: &str) -> Option<Arc<VfsMount>> {
|
||||
let ns = get_init_ns();
|
||||
let ns = ns.lock();
|
||||
ns.find_mount(path)
|
||||
}
|
||||
|
||||
/// Check if a path is a mount point
|
||||
pub fn is_mountpoint(path: &str) -> bool {
|
||||
let ns = get_init_ns();
|
||||
let ns = ns.lock();
|
||||
let mounts = ns.mounts.lock();
|
||||
mounts.iter().any(|m| m.get_path() == path)
|
||||
}
|
||||
|
||||
/// Get all mount points
|
||||
pub fn get_all_mounts() -> Vec<String> {
|
||||
let ns = get_init_ns();
|
||||
let ns = ns.lock();
|
||||
ns.get_mount_points()
|
||||
}
|
||||
|
||||
/// Remount a filesystem with new flags
|
||||
pub fn do_remount(dir_name: &str, flags: u32, data: Option<&str>) -> Result<()> {
|
||||
let ns = get_init_ns();
|
||||
let ns = ns.lock();
|
||||
|
||||
if let Some(mount) = ns.find_mount(dir_name) {
|
||||
mount.mnt_flags.store(flags, Ordering::Relaxed);
|
||||
|
||||
// Also remount the superblock
|
||||
if let Some(ref ops) = mount.mnt_sb.s_op {
|
||||
ops.remount_fs(&mount.mnt_sb, flags, data)?;
|
||||
}
|
||||
|
||||
crate::console::print_info(&format!("Remounted {} with flags {:#x}\n", dir_name, flags));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
/// Bind mount - create a bind mount
|
||||
pub fn do_bind_mount(old_path: &str, new_path: &str, flags: u32) -> Result<()> {
|
||||
let ns = get_init_ns();
|
||||
let mut ns = ns.lock();
|
||||
|
||||
if let Some(old_mount) = ns.find_mount(old_path) {
|
||||
let new_mount = Arc::new(VfsMount::new(old_mount.mnt_sb.clone(), new_path, flags | super::super_block::MS_BIND)?);
|
||||
ns.add_mount(new_mount);
|
||||
|
||||
crate::console::print_info(&format!("Bind mounted {} to {}\n", old_path, new_path));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
}
|
||||
419
kernel/src/fs/operations.rs
Archivo normal
419
kernel/src/fs/operations.rs
Archivo normal
@@ -0,0 +1,419 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Various VFS operations and utilities
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sync::Arc;
|
||||
use crate::memory::{UserPtr, UserSlicePtr};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Address space operations trait - similar to Linux address_space_operations
|
||||
pub trait AddressSpaceOperations: Send + Sync {
|
||||
/// Write a page
|
||||
fn writepage(&self, page: &crate::memory::Page) -> Result<()>;
|
||||
|
||||
/// Read a page
|
||||
fn readpage(&self, file: Option<&super::File>, page: &crate::memory::Page) -> Result<()>;
|
||||
|
||||
/// Sync pages
|
||||
fn sync_page(&self, page: &crate::memory::Page) -> Result<()>;
|
||||
|
||||
/// Write pages
|
||||
fn writepages(&self, mapping: &AddressSpace, wbc: &WritebackControl) -> Result<()>;
|
||||
|
||||
/// Set page dirty
|
||||
fn set_page_dirty(&self, page: &crate::memory::Page) -> Result<bool>;
|
||||
|
||||
/// Read pages ahead
|
||||
fn readpages(&self, file: Option<&super::File>, pages: &[&crate::memory::Page]) -> Result<()>;
|
||||
|
||||
/// Write begin
|
||||
fn write_begin(&self, file: &super::File, pos: u64, len: u32) -> Result<()>;
|
||||
|
||||
/// Write end
|
||||
fn write_end(&self, file: &super::File, pos: u64, len: u32, copied: u32) -> Result<u32>;
|
||||
|
||||
/// Direct I/O
|
||||
fn direct_io(&self, file: &super::File, pos: u64, buf: UserSlicePtr, len: usize, write: bool) -> Result<isize>;
|
||||
}
|
||||
|
||||
/// Address space structure
|
||||
pub struct AddressSpace {
|
||||
/// Host inode
|
||||
pub host: Option<Arc<super::Inode>>,
|
||||
/// Address space operations
|
||||
pub a_ops: Option<Arc<dyn AddressSpaceOperations>>,
|
||||
/// Number of pages
|
||||
pub nrpages: core::sync::atomic::AtomicUsize,
|
||||
/// Flags
|
||||
pub flags: core::sync::atomic::AtomicU32,
|
||||
/// Private data
|
||||
pub private_data: Option<*mut u8>,
|
||||
}
|
||||
|
||||
impl AddressSpace {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
host: None,
|
||||
a_ops: None,
|
||||
nrpages: core::sync::atomic::AtomicUsize::new(0),
|
||||
flags: core::sync::atomic::AtomicU32::new(0),
|
||||
private_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writeback control structure
|
||||
pub struct WritebackControl {
|
||||
pub start: u64,
|
||||
pub end: u64,
|
||||
pub sync_mode: WritebackSyncMode,
|
||||
pub nr_to_write: u64,
|
||||
pub tagged_writepages: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum WritebackSyncMode {
|
||||
None,
|
||||
All,
|
||||
Memory,
|
||||
}
|
||||
|
||||
/// Generic file operations implementation
|
||||
pub struct GenericFileOperations;
|
||||
|
||||
impl super::FileOperations for GenericFileOperations {
|
||||
fn read(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Generic read implementation using page cache
|
||||
// TODO: Implement proper page cache read
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Generic write implementation using page cache
|
||||
// TODO: Implement proper page cache write
|
||||
Ok(count as isize)
|
||||
}
|
||||
|
||||
fn seek(&self, file: &super::File, offset: i64, whence: i32) -> Result<i64> {
|
||||
let current_pos = file.get_pos();
|
||||
let new_pos = match whence {
|
||||
super::SEEK_SET => offset,
|
||||
super::SEEK_CUR => current_pos + offset,
|
||||
super::SEEK_END => {
|
||||
// Get file size from inode
|
||||
if let Some(ref inode) = file.inode {
|
||||
inode.get_size() as i64 + offset
|
||||
} else {
|
||||
offset
|
||||
}
|
||||
}
|
||||
_ => return Err(Error::EINVAL),
|
||||
};
|
||||
|
||||
if new_pos < 0 {
|
||||
return Err(Error::EINVAL);
|
||||
}
|
||||
|
||||
file.set_pos(new_pos);
|
||||
Ok(new_pos)
|
||||
}
|
||||
|
||||
fn ioctl(&self, file: &super::File, cmd: u32, arg: usize) -> Result<isize> {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, file: &super::File, vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
// Generic mmap implementation
|
||||
// TODO: Implement proper memory mapping
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn fsync(&self, file: &super::File, datasync: bool) -> Result<()> {
|
||||
// Sync file data to storage
|
||||
if let Some(ref inode) = file.inode {
|
||||
// TODO: Sync inode and data blocks
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, file: &super::File, wait: &mut super::PollWait) -> Result<u32> {
|
||||
// Regular files are always ready for I/O
|
||||
Ok(super::POLLIN | super::POLLOUT)
|
||||
}
|
||||
|
||||
fn readdir(&self, file: &super::File, ctx: &mut super::file::DirContext) -> Result<()> {
|
||||
// This shouldn't be called for regular files
|
||||
Err(Error::ENOTDIR)
|
||||
}
|
||||
}
|
||||
|
||||
/// Directory file operations
|
||||
pub struct DirectoryOperations;
|
||||
|
||||
impl super::FileOperations for DirectoryOperations {
|
||||
fn read(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Can't read directory as regular file
|
||||
Err(Error::EISDIR)
|
||||
}
|
||||
|
||||
fn write(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Can't write to directory as regular file
|
||||
Err(Error::EISDIR)
|
||||
}
|
||||
|
||||
fn seek(&self, file: &super::File, offset: i64, whence: i32) -> Result<i64> {
|
||||
// Directory seeking
|
||||
match whence {
|
||||
super::SEEK_SET => {
|
||||
if offset < 0 {
|
||||
return Err(Error::EINVAL);
|
||||
}
|
||||
file.set_pos(offset);
|
||||
Ok(offset)
|
||||
}
|
||||
super::SEEK_CUR => {
|
||||
let new_pos = file.get_pos() + offset;
|
||||
if new_pos < 0 {
|
||||
return Err(Error::EINVAL);
|
||||
}
|
||||
file.set_pos(new_pos);
|
||||
Ok(new_pos)
|
||||
}
|
||||
super::SEEK_END => {
|
||||
// Seek to end of directory
|
||||
file.set_pos(i64::MAX);
|
||||
Ok(i64::MAX)
|
||||
}
|
||||
_ => Err(Error::EINVAL),
|
||||
}
|
||||
}
|
||||
|
||||
fn ioctl(&self, file: &super::File, cmd: u32, arg: usize) -> Result<isize> {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, file: &super::File, vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn fsync(&self, file: &super::File, datasync: bool) -> Result<()> {
|
||||
// Sync directory
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, file: &super::File, wait: &mut super::PollWait) -> Result<u32> {
|
||||
// Directories are always ready
|
||||
Ok(super::POLLIN)
|
||||
}
|
||||
|
||||
fn readdir(&self, file: &super::File, ctx: &mut super::file::DirContext) -> Result<()> {
|
||||
// Read directory entries
|
||||
if let Some(ref inode) = file.inode {
|
||||
if let Some(ref dentry) = file.dentry {
|
||||
let subdirs = dentry.d_subdirs.lock();
|
||||
for (i, child) in subdirs.iter().enumerate() {
|
||||
if i >= ctx.pos as usize {
|
||||
let d_type = if let Some(ref child_inode) = child.d_inode {
|
||||
let mode = child_inode.i_mode.load(core::sync::atomic::Ordering::Relaxed);
|
||||
if super::mode::s_isdir(mode) {
|
||||
super::DT_DIR
|
||||
} else if super::mode::s_isreg(mode) {
|
||||
super::DT_REG
|
||||
} else if super::mode::s_islnk(mode) {
|
||||
super::DT_LNK
|
||||
} else if super::mode::s_ischr(mode) {
|
||||
super::DT_CHR
|
||||
} else if super::mode::s_isblk(mode) {
|
||||
super::DT_BLK
|
||||
} else if super::mode::s_isfifo(mode) {
|
||||
super::DT_FIFO
|
||||
} else if super::mode::s_issock(mode) {
|
||||
super::DT_SOCK
|
||||
} else {
|
||||
super::DT_UNKNOWN
|
||||
}
|
||||
} else {
|
||||
super::DT_UNKNOWN
|
||||
};
|
||||
|
||||
let ino = if let Some(ref child_inode) = child.d_inode {
|
||||
child_inode.i_ino
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
ctx.add_entry(ino, &child.d_name, d_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Special file operations (for device files)
|
||||
pub struct SpecialFileOperations;
|
||||
|
||||
impl super::FileOperations for SpecialFileOperations {
|
||||
fn read(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Delegate to device driver
|
||||
if let Some(ref inode) = file.inode {
|
||||
if inode.is_char_device() || inode.is_block_device() {
|
||||
// TODO: Call device driver read function
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn write(&self, file: &super::File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
// Delegate to device driver
|
||||
if let Some(ref inode) = file.inode {
|
||||
if inode.is_char_device() || inode.is_block_device() {
|
||||
// TODO: Call device driver write function
|
||||
return Ok(count as isize);
|
||||
}
|
||||
}
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn seek(&self, file: &super::File, offset: i64, whence: i32) -> Result<i64> {
|
||||
// Most device files don't support seeking
|
||||
Err(Error::ESPIPE)
|
||||
}
|
||||
|
||||
fn ioctl(&self, file: &super::File, cmd: u32, arg: usize) -> Result<isize> {
|
||||
// Delegate to device driver
|
||||
if let Some(ref inode) = file.inode {
|
||||
if inode.is_char_device() || inode.is_block_device() {
|
||||
// TODO: Call device driver ioctl function
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, file: &super::File, vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
// Some device files support mmap
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn fsync(&self, file: &super::File, datasync: bool) -> Result<()> {
|
||||
// Nothing to sync for device files
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, file: &super::File, wait: &mut super::PollWait) -> Result<u32> {
|
||||
// Delegate to device driver
|
||||
if let Some(ref inode) = file.inode {
|
||||
if inode.is_char_device() {
|
||||
// TODO: Call device driver poll function
|
||||
return Ok(super::POLLIN | super::POLLOUT);
|
||||
}
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// No-op address space operations
|
||||
pub struct NoOpAddressSpaceOps;
|
||||
|
||||
impl AddressSpaceOperations for NoOpAddressSpaceOps {
|
||||
fn writepage(&self, page: &crate::memory::Page) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn readpage(&self, file: Option<&super::File>, page: &crate::memory::Page) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sync_page(&self, page: &crate::memory::Page) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn writepages(&self, mapping: &AddressSpace, wbc: &WritebackControl) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_page_dirty(&self, page: &crate::memory::Page) -> Result<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn readpages(&self, file: Option<&super::File>, pages: &[&crate::memory::Page]) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_begin(&self, file: &super::File, pos: u64, len: u32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_end(&self, file: &super::File, pos: u64, len: u32, copied: u32) -> Result<u32> {
|
||||
Ok(copied)
|
||||
}
|
||||
|
||||
fn direct_io(&self, file: &super::File, pos: u64, buf: UserSlicePtr, len: usize, write: bool) -> Result<isize> {
|
||||
if write {
|
||||
Ok(len as isize)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper functions for VFS operations
|
||||
|
||||
/// Get file operations for an inode
|
||||
pub fn get_file_operations(inode: &super::Inode) -> Arc<dyn super::FileOperations> {
|
||||
if let Some(ref fop) = inode.i_fop {
|
||||
fop.clone()
|
||||
} else {
|
||||
let mode = inode.i_mode.load(core::sync::atomic::Ordering::Relaxed);
|
||||
if super::mode::s_isreg(mode) {
|
||||
Arc::new(GenericFileOperations)
|
||||
} else if super::mode::s_isdir(mode) {
|
||||
Arc::new(DirectoryOperations)
|
||||
} else if super::mode::s_ischr(mode) || super::mode::s_isblk(mode) {
|
||||
Arc::new(SpecialFileOperations)
|
||||
} else {
|
||||
Arc::new(super::GenericFileOps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check file permissions
|
||||
pub fn check_permissions(inode: &super::Inode, mask: u32) -> Result<()> {
|
||||
// TODO: Implement proper permission checking
|
||||
// For now, allow all operations
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update access time
|
||||
pub fn update_atime(inode: &super::Inode) {
|
||||
inode.update_atime();
|
||||
}
|
||||
|
||||
/// Update modification time
|
||||
pub fn update_mtime(inode: &super::Inode) {
|
||||
inode.update_mtime();
|
||||
}
|
||||
|
||||
/// Truncate file to specified size
|
||||
pub fn do_truncate(inode: &super::Inode, size: u64) -> Result<()> {
|
||||
if let Some(ref ops) = inode.i_op {
|
||||
ops.truncate(inode, size)
|
||||
} else {
|
||||
inode.set_size(size);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify directory change
|
||||
pub fn notify_change(inode: &super::Inode, attr: &super::inode::InodeAttr) -> Result<()> {
|
||||
if let Some(ref ops) = inode.i_op {
|
||||
ops.setattr(inode, attr)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
391
kernel/src/fs/path.rs
Archivo normal
391
kernel/src/fs/path.rs
Archivo normal
@@ -0,0 +1,391 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Path resolution and manipulation - Linux compatible
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sync::Arc;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Path structure for path resolution
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Path {
|
||||
/// Mount point
|
||||
pub mnt: Option<Arc<super::VfsMount>>,
|
||||
/// Dentry
|
||||
pub dentry: Option<Arc<super::Dentry>>,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Create a new path
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mnt: None,
|
||||
dentry: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create path from mount and dentry
|
||||
pub fn from_mount_dentry(mnt: Arc<super::VfsMount>, dentry: Arc<super::Dentry>) -> Self {
|
||||
Self {
|
||||
mnt: Some(mnt),
|
||||
dentry: Some(dentry),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the full path string
|
||||
pub fn to_string(&self) -> String {
|
||||
if let Some(ref dentry) = self.dentry {
|
||||
dentry.get_path()
|
||||
} else {
|
||||
String::from("/")
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if path is absolute
|
||||
pub fn is_absolute(&self) -> bool {
|
||||
self.to_string().starts_with('/')
|
||||
}
|
||||
|
||||
/// Get parent path
|
||||
pub fn parent(&self) -> Option<Path> {
|
||||
if let Some(ref dentry) = self.dentry {
|
||||
if let Some(ref parent) = dentry.d_parent {
|
||||
Some(Path {
|
||||
mnt: self.mnt.clone(),
|
||||
dentry: Some(parent.clone()),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get filename component
|
||||
pub fn filename(&self) -> Option<String> {
|
||||
if let Some(ref dentry) = self.dentry {
|
||||
Some(dentry.d_name.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Join with another path component
|
||||
pub fn join(&self, component: &str) -> Result<Path> {
|
||||
if let Some(ref dentry) = self.dentry {
|
||||
if let Some(child) = dentry.find_child(component) {
|
||||
Ok(Path {
|
||||
mnt: self.mnt.clone(),
|
||||
dentry: Some(child),
|
||||
})
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Path lookup flags
|
||||
pub const LOOKUP_FOLLOW: u32 = 0x0001;
|
||||
pub const LOOKUP_DIRECTORY: u32 = 0x0002;
|
||||
pub const LOOKUP_AUTOMOUNT: u32 = 0x0004;
|
||||
pub const LOOKUP_EMPTY: u32 = 0x0008;
|
||||
pub const LOOKUP_OPEN: u32 = 0x0010;
|
||||
pub const LOOKUP_CREATE: u32 = 0x0020;
|
||||
pub const LOOKUP_EXCL: u32 = 0x0040;
|
||||
pub const LOOKUP_RENAME_TARGET: u32 = 0x0080;
|
||||
|
||||
/// Name data structure for path resolution
|
||||
pub struct NameData {
|
||||
/// Path components
|
||||
pub path: String,
|
||||
/// Current position in path
|
||||
pub pos: usize,
|
||||
/// Lookup flags
|
||||
pub flags: u32,
|
||||
/// Root directory
|
||||
pub root: Option<Path>,
|
||||
/// Current working directory
|
||||
pub pwd: Option<Path>,
|
||||
/// Result path
|
||||
pub result: Option<Path>,
|
||||
/// Intent (for create/open operations)
|
||||
pub intent: Option<Intent>,
|
||||
}
|
||||
|
||||
/// Intent for path operations
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Intent {
|
||||
Open {
|
||||
flags: u32,
|
||||
mode: u32,
|
||||
},
|
||||
Create {
|
||||
mode: u32,
|
||||
},
|
||||
Lookup,
|
||||
}
|
||||
|
||||
impl NameData {
|
||||
/// Create new name data for path resolution
|
||||
pub fn new(path: String, flags: u32) -> Self {
|
||||
Self {
|
||||
path,
|
||||
pos: 0,
|
||||
flags,
|
||||
root: None,
|
||||
pwd: None,
|
||||
result: None,
|
||||
intent: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set root directory
|
||||
pub fn with_root(mut self, root: Path) -> Self {
|
||||
self.root = Some(root);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set current working directory
|
||||
pub fn with_pwd(mut self, pwd: Path) -> Self {
|
||||
self.pwd = Some(pwd);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set intent
|
||||
pub fn with_intent(mut self, intent: Intent) -> Self {
|
||||
self.intent = Some(intent);
|
||||
self
|
||||
}
|
||||
|
||||
/// Get next path component
|
||||
pub fn next_component(&mut self) -> Option<String> {
|
||||
if self.pos >= self.path.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Skip leading slashes
|
||||
while self.pos < self.path.len() && self.path.chars().nth(self.pos) == Some('/') {
|
||||
self.pos += 1;
|
||||
}
|
||||
|
||||
if self.pos >= self.path.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Find end of component
|
||||
let start = self.pos;
|
||||
while self.pos < self.path.len() && self.path.chars().nth(self.pos) != Some('/') {
|
||||
self.pos += 1;
|
||||
}
|
||||
|
||||
Some(self.path[start..self.pos].to_string())
|
||||
}
|
||||
|
||||
/// Check if path is finished
|
||||
pub fn is_finished(&self) -> bool {
|
||||
self.pos >= self.path.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a path to a dentry
|
||||
pub fn path_lookup(pathname: &str, flags: u32) -> Result<Path> {
|
||||
let mut nd = NameData::new(String::from(pathname), flags);
|
||||
|
||||
// Set root directory (for now, use a dummy root)
|
||||
// TODO: Get actual root from current process
|
||||
|
||||
// Start from root or current directory
|
||||
let mut current_path = if pathname.starts_with('/') {
|
||||
// Absolute path - start from root
|
||||
if let Some(root) = nd.root.clone() {
|
||||
root
|
||||
} else {
|
||||
// Create dummy root path
|
||||
Path::new()
|
||||
}
|
||||
} else {
|
||||
// Relative path - start from current directory
|
||||
if let Some(pwd) = nd.pwd.clone() {
|
||||
pwd
|
||||
} else {
|
||||
// Create dummy current directory
|
||||
Path::new()
|
||||
}
|
||||
};
|
||||
|
||||
// Resolve each component
|
||||
while let Some(component) = nd.next_component() {
|
||||
match component.as_str() {
|
||||
"." => {
|
||||
// Current directory - no change
|
||||
continue;
|
||||
}
|
||||
".." => {
|
||||
// Parent directory
|
||||
if let Some(parent) = current_path.parent() {
|
||||
current_path = parent;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
// Regular component
|
||||
current_path = current_path.join(&component)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for mount points
|
||||
if let Some(mount) = super::mount::path_get_mount(¤t_path.to_string()) {
|
||||
current_path.mnt = Some(mount);
|
||||
}
|
||||
|
||||
// Handle symlinks if LOOKUP_FOLLOW is set
|
||||
if (flags & LOOKUP_FOLLOW) != 0 {
|
||||
if let Some(ref dentry) = current_path.dentry {
|
||||
if let Some(ref inode) = dentry.d_inode {
|
||||
if super::mode::s_islnk(inode.i_mode.load(core::sync::atomic::Ordering::Relaxed)) {
|
||||
// TODO: Follow symbolic link
|
||||
// For now, just continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nd.result = Some(current_path.clone());
|
||||
Ok(current_path)
|
||||
}
|
||||
|
||||
/// Resolve parent directory and filename
|
||||
pub fn path_parent_and_name(pathname: &str) -> Result<(Path, String)> {
|
||||
let path = Path::new();
|
||||
|
||||
// Split pathname into parent and filename
|
||||
if let Some(last_slash) = pathname.rfind('/') {
|
||||
let parent_path = &pathname[..last_slash];
|
||||
let filename = &pathname[last_slash + 1..];
|
||||
|
||||
if parent_path.is_empty() {
|
||||
// Root directory
|
||||
Ok((path, String::from(filename)))
|
||||
} else {
|
||||
let parent = path_lookup(parent_path, 0)?;
|
||||
Ok((parent, String::from(filename)))
|
||||
}
|
||||
} else {
|
||||
// No slash - filename in current directory
|
||||
Ok((path, String::from(pathname)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalize a path (remove . and .. components)
|
||||
pub fn normalize_path(path: &str) -> String {
|
||||
let mut components = Vec::new();
|
||||
|
||||
for component in path.split('/') {
|
||||
match component {
|
||||
"" | "." => {
|
||||
// Skip empty and current directory components
|
||||
continue;
|
||||
}
|
||||
".." => {
|
||||
// Parent directory - remove last component
|
||||
components.pop();
|
||||
}
|
||||
_ => {
|
||||
// Regular component
|
||||
components.push(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = components.join("/");
|
||||
if path.starts_with('/') {
|
||||
format!("/{}", result)
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a path is safe (no .. escapes)
|
||||
pub fn is_safe_path(path: &str) -> bool {
|
||||
let normalized = normalize_path(path);
|
||||
|
||||
// Check for .. at the beginning
|
||||
if normalized.starts_with("..") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for /../ sequences
|
||||
if normalized.contains("/../") {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Join two paths
|
||||
pub fn join_paths(base: &str, path: &str) -> String {
|
||||
if path.starts_with('/') {
|
||||
// Absolute path
|
||||
String::from(path)
|
||||
} else {
|
||||
// Relative path
|
||||
let base = base.trim_end_matches('/');
|
||||
if base.is_empty() {
|
||||
format!("/{}", path)
|
||||
} else {
|
||||
format!("{}/{}", base, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the directory part of a path
|
||||
pub fn dirname(path: &str) -> &str {
|
||||
if let Some(last_slash) = path.rfind('/') {
|
||||
if last_slash == 0 {
|
||||
"/"
|
||||
} else {
|
||||
&path[..last_slash]
|
||||
}
|
||||
} else {
|
||||
"."
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the filename part of a path
|
||||
pub fn basename(path: &str) -> &str {
|
||||
if let Some(last_slash) = path.rfind('/') {
|
||||
&path[last_slash + 1..]
|
||||
} else {
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
/// Get file extension
|
||||
pub fn extension(path: &str) -> Option<&str> {
|
||||
let filename = basename(path);
|
||||
if let Some(last_dot) = filename.rfind('.') {
|
||||
if last_dot > 0 {
|
||||
Some(&filename[last_dot + 1..])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if path is absolute
|
||||
pub fn is_absolute(path: &str) -> bool {
|
||||
path.starts_with('/')
|
||||
}
|
||||
|
||||
/// Check if path is relative
|
||||
pub fn is_relative(path: &str) -> bool {
|
||||
!is_absolute(path)
|
||||
}
|
||||
488
kernel/src/fs/procfs.rs
Archivo normal
488
kernel/src/fs/procfs.rs
Archivo normal
@@ -0,0 +1,488 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Proc filesystem implementation - Linux compatible
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::fs::*;
|
||||
use crate::sync::{Arc, Mutex};
|
||||
use crate::memory::UserSlicePtr;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::format;
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// Proc filesystem entry
|
||||
pub struct ProcEntry {
|
||||
/// Entry name
|
||||
pub name: String,
|
||||
/// Entry type
|
||||
pub entry_type: ProcEntryType,
|
||||
/// Mode
|
||||
pub mode: u32,
|
||||
/// Read function
|
||||
pub read: Option<fn(&ProcEntry, &mut String) -> Result<()>>,
|
||||
/// Write function
|
||||
pub write: Option<fn(&ProcEntry, &str) -> Result<()>>,
|
||||
/// Child entries (for directories)
|
||||
pub children: Mutex<Vec<Arc<ProcEntry>>>,
|
||||
/// Parent entry
|
||||
pub parent: Option<Arc<ProcEntry>>,
|
||||
/// Private data
|
||||
pub private_data: Option<*mut u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ProcEntryType {
|
||||
File,
|
||||
Directory,
|
||||
Symlink,
|
||||
}
|
||||
|
||||
impl ProcEntry {
|
||||
pub fn new_file(name: String, mode: u32, read_fn: fn(&ProcEntry, &mut String) -> Result<()>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
entry_type: ProcEntryType::File,
|
||||
mode,
|
||||
read: Some(read_fn),
|
||||
write: None,
|
||||
children: Mutex::new(Vec::new()),
|
||||
parent: None,
|
||||
private_data: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dir(name: String, mode: u32) -> Self {
|
||||
Self {
|
||||
name,
|
||||
entry_type: ProcEntryType::Directory,
|
||||
mode,
|
||||
read: None,
|
||||
write: None,
|
||||
children: Mutex::new(Vec::new()),
|
||||
parent: None,
|
||||
private_data: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_child(self: &Arc<Self>, child: Arc<ProcEntry>) {
|
||||
let mut children = self.children.lock();
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
pub fn find_child(&self, name: &str) -> Option<Arc<ProcEntry>> {
|
||||
let children = self.children.lock();
|
||||
for child in children.iter() {
|
||||
if child.name == name {
|
||||
return Some(child.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for ProcEntry {}
|
||||
unsafe impl Sync for ProcEntry {}
|
||||
|
||||
/// Proc filesystem
|
||||
pub struct ProcFs {
|
||||
/// Root entry
|
||||
pub root: Arc<ProcEntry>,
|
||||
/// Next inode number
|
||||
next_ino: AtomicU64,
|
||||
/// Entry to inode mapping
|
||||
entries: Mutex<BTreeMap<*const ProcEntry, u64>>,
|
||||
}
|
||||
|
||||
impl ProcFs {
|
||||
pub fn new() -> Self {
|
||||
let root = Arc::new(ProcEntry::new_dir(String::from("proc"), 0o755));
|
||||
let mut fs = Self {
|
||||
root,
|
||||
next_ino: AtomicU64::new(1),
|
||||
entries: Mutex::new(BTreeMap::new()),
|
||||
};
|
||||
|
||||
fs.create_default_entries();
|
||||
fs
|
||||
}
|
||||
|
||||
fn alloc_ino(&self) -> u64 {
|
||||
self.next_ino.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn get_or_create_ino(&self, entry: &ProcEntry) -> u64 {
|
||||
let entry_ptr = entry as *const ProcEntry;
|
||||
let mut entries = self.entries.lock();
|
||||
|
||||
if let Some(&ino) = entries.get(&entry_ptr) {
|
||||
ino
|
||||
} else {
|
||||
let ino = self.alloc_ino();
|
||||
entries.insert(entry_ptr, ino);
|
||||
ino
|
||||
}
|
||||
}
|
||||
|
||||
fn create_default_entries(&mut self) {
|
||||
// Create /proc/version
|
||||
let version_entry = Arc::new(ProcEntry::new_file(
|
||||
String::from("version"),
|
||||
0o444,
|
||||
proc_version_read,
|
||||
));
|
||||
self.root.add_child(version_entry);
|
||||
|
||||
// Create /proc/meminfo
|
||||
let meminfo_entry = Arc::new(ProcEntry::new_file(
|
||||
String::from("meminfo"),
|
||||
0o444,
|
||||
proc_meminfo_read,
|
||||
));
|
||||
self.root.add_child(meminfo_entry);
|
||||
|
||||
// Create /proc/cpuinfo
|
||||
let cpuinfo_entry = Arc::new(ProcEntry::new_file(
|
||||
String::from("cpuinfo"),
|
||||
0o444,
|
||||
proc_cpuinfo_read,
|
||||
));
|
||||
self.root.add_child(cpuinfo_entry);
|
||||
|
||||
// Create /proc/uptime
|
||||
let uptime_entry = Arc::new(ProcEntry::new_file(
|
||||
String::from("uptime"),
|
||||
0o444,
|
||||
proc_uptime_read,
|
||||
));
|
||||
self.root.add_child(uptime_entry);
|
||||
|
||||
// Create /proc/loadavg
|
||||
let loadavg_entry = Arc::new(ProcEntry::new_file(
|
||||
String::from("loadavg"),
|
||||
0o444,
|
||||
proc_loadavg_read,
|
||||
));
|
||||
self.root.add_child(loadavg_entry);
|
||||
|
||||
// Create /proc/stat
|
||||
let stat_entry = Arc::new(ProcEntry::new_file(
|
||||
String::from("stat"),
|
||||
0o444,
|
||||
proc_stat_read,
|
||||
));
|
||||
self.root.add_child(stat_entry);
|
||||
|
||||
// Create /proc/mounts
|
||||
let mounts_entry = Arc::new(ProcEntry::new_file(
|
||||
String::from("mounts"),
|
||||
0o444,
|
||||
proc_mounts_read,
|
||||
));
|
||||
self.root.add_child(mounts_entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// Proc filesystem file operations
|
||||
pub struct ProcFileOps {
|
||||
entry: Arc<ProcEntry>,
|
||||
}
|
||||
|
||||
impl ProcFileOps {
|
||||
pub fn new(entry: Arc<ProcEntry>) -> Self {
|
||||
Self { entry }
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOperations for ProcFileOps {
|
||||
fn read(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
match self.entry.entry_type {
|
||||
ProcEntryType::File => {
|
||||
if let Some(read_fn) = self.entry.read {
|
||||
let mut content = String::new();
|
||||
read_fn(&self.entry, &mut content)?;
|
||||
|
||||
let pos = file.get_pos() as usize;
|
||||
if pos >= content.len() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let to_copy = core::cmp::min(count, content.len() - pos);
|
||||
let data = &content.as_bytes()[pos..pos + to_copy];
|
||||
|
||||
buf.copy_from_slice(data)?;
|
||||
file.set_pos(file.get_pos() + to_copy as i64);
|
||||
|
||||
Ok(to_copy as isize)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
_ => Err(Error::EISDIR),
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, file: &File, buf: UserSlicePtr, count: usize) -> Result<isize> {
|
||||
if let Some(write_fn) = self.entry.write {
|
||||
let mut data = vec![0u8; count];
|
||||
buf.copy_to_slice(&mut data)?;
|
||||
let content = String::from_utf8(data).map_err(|_| Error::EINVAL)?;
|
||||
write_fn(&self.entry, &content)?;
|
||||
Ok(count as isize)
|
||||
} else {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
}
|
||||
|
||||
fn seek(&self, file: &File, offset: i64, whence: i32) -> Result<i64> {
|
||||
let new_pos = match whence {
|
||||
SEEK_SET => offset,
|
||||
SEEK_CUR => file.get_pos() + offset,
|
||||
SEEK_END => 0, // Proc files are typically small
|
||||
_ => return Err(Error::EINVAL),
|
||||
};
|
||||
|
||||
if new_pos < 0 {
|
||||
return Err(Error::EINVAL);
|
||||
}
|
||||
|
||||
file.set_pos(new_pos);
|
||||
Ok(new_pos)
|
||||
}
|
||||
|
||||
fn ioctl(&self, file: &File, cmd: u32, arg: usize) -> Result<isize> {
|
||||
Err(Error::ENOTTY)
|
||||
}
|
||||
|
||||
fn mmap(&self, file: &File, vma: &mut crate::memory::VmaArea) -> Result<()> {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
|
||||
fn fsync(&self, file: &File, datasync: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, file: &File, wait: &mut PollWait) -> Result<u32> {
|
||||
Ok(POLLIN | POLLOUT)
|
||||
}
|
||||
}
|
||||
|
||||
/// Proc filesystem inode operations
|
||||
pub struct ProcInodeOps {
|
||||
fs: *const ProcFs,
|
||||
}
|
||||
|
||||
impl ProcInodeOps {
|
||||
pub fn new(fs: &ProcFs) -> Self {
|
||||
Self {
|
||||
fs: fs as *const ProcFs,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fs(&self) -> &ProcFs {
|
||||
unsafe { &*self.fs }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for ProcInodeOps {}
|
||||
unsafe impl Sync for ProcInodeOps {}
|
||||
|
||||
impl InodeOperations for ProcInodeOps {
|
||||
fn lookup(&self, dir: &Inode, name: &str) -> Result<Arc<Inode>> {
|
||||
let fs = self.get_fs();
|
||||
// Find the proc entry for this inode
|
||||
// This is a simplified implementation
|
||||
if let Some(child) = fs.root.find_child(name) {
|
||||
let ino = fs.get_or_create_ino(&child);
|
||||
let mode = match child.entry_type {
|
||||
ProcEntryType::File => mode::S_IFREG | child.mode,
|
||||
ProcEntryType::Directory => mode::S_IFDIR | child.mode,
|
||||
ProcEntryType::Symlink => mode::S_IFLNK | child.mode,
|
||||
};
|
||||
|
||||
let mut inode = Inode::new(ino, mode);
|
||||
inode.set_operations(Arc::new(ProcInodeOps::new(fs)));
|
||||
inode.set_file_operations(Arc::new(ProcFileOps::new(child)));
|
||||
|
||||
Ok(Arc::new(inode))
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
fn create(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
Err(Error::EPERM) // Proc filesystem is read-only
|
||||
}
|
||||
|
||||
fn mkdir(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn unlink(&self, dir: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn rmdir(&self, dir: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn symlink(&self, dir: &Inode, name: &str, target: &str) -> Result<Arc<Inode>> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn rename(&self, old_dir: &Inode, old_name: &str, new_dir: &Inode, new_name: &str) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn setattr(&self, inode: &Inode, attr: &InodeAttr) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn getattr(&self, inode: &Inode) -> Result<InodeAttr> {
|
||||
let generic_ops = GenericInodeOps;
|
||||
generic_ops.getattr(inode)
|
||||
}
|
||||
|
||||
fn readlink(&self, inode: &Inode) -> Result<String> {
|
||||
Err(Error::EINVAL)
|
||||
}
|
||||
|
||||
fn follow_link(&self, inode: &Inode) -> Result<Arc<Inode>> {
|
||||
Err(Error::EINVAL)
|
||||
}
|
||||
|
||||
fn truncate(&self, inode: &Inode, size: u64) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn getxattr(&self, inode: &Inode, name: &str) -> Result<Vec<u8>> {
|
||||
Err(Error::ENODATA)
|
||||
}
|
||||
|
||||
fn setxattr(&self, inode: &Inode, name: &str, value: &[u8], flags: u32) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
|
||||
fn listxattr(&self, inode: &Inode) -> Result<Vec<String>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn removexattr(&self, inode: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::EPERM)
|
||||
}
|
||||
}
|
||||
|
||||
// Proc file read functions
|
||||
|
||||
fn proc_version_read(_entry: &ProcEntry, content: &mut String) -> Result<()> {
|
||||
content.push_str(&format!("{} version {} ({})\n",
|
||||
crate::NAME, crate::VERSION, "rustc"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn proc_meminfo_read(_entry: &ProcEntry, content: &mut String) -> Result<()> {
|
||||
// TODO: Get actual memory statistics
|
||||
let total_mem = 128 * 1024; // 128 MB placeholder
|
||||
let free_mem = 64 * 1024; // 64 MB placeholder
|
||||
|
||||
content.push_str(&format!(
|
||||
"MemTotal: {} kB\n\
|
||||
MemFree: {} kB\n\
|
||||
MemAvailable: {} kB\n\
|
||||
Buffers: {} kB\n\
|
||||
Cached: {} kB\n",
|
||||
total_mem, free_mem, free_mem, 0, 0
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn proc_cpuinfo_read(_entry: &ProcEntry, content: &mut String) -> Result<()> {
|
||||
// TODO: Get actual CPU information
|
||||
content.push_str(
|
||||
"processor\t: 0\n\
|
||||
vendor_id\t: RustKernel\n\
|
||||
cpu family\t: 1\n\
|
||||
model\t\t: 1\n\
|
||||
model name\t: Rust Kernel CPU\n\
|
||||
stepping\t: 1\n\
|
||||
microcode\t: 0x1\n\
|
||||
cpu MHz\t\t: 1000.000\n\
|
||||
cache size\t: 1024 KB\n\
|
||||
flags\t\t: rust\n\n"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn proc_uptime_read(_entry: &ProcEntry, content: &mut String) -> Result<()> {
|
||||
// TODO: Get actual uptime
|
||||
let uptime = 100.0; // 100 seconds placeholder
|
||||
let idle = 90.0; // 90 seconds idle placeholder
|
||||
|
||||
content.push_str(&format!("{:.2} {:.2}\n", uptime, idle));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn proc_loadavg_read(_entry: &ProcEntry, content: &mut String) -> Result<()> {
|
||||
// TODO: Get actual load average
|
||||
content.push_str("0.00 0.00 0.00 1/1 1\n");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn proc_stat_read(_entry: &ProcEntry, content: &mut String) -> Result<()> {
|
||||
// TODO: Get actual system statistics
|
||||
content.push_str(
|
||||
"cpu 0 0 0 1000 0 0 0 0 0 0\n\
|
||||
cpu0 0 0 0 1000 0 0 0 0 0 0\n\
|
||||
intr 0\n\
|
||||
ctxt 0\n\
|
||||
btime 0\n\
|
||||
processes 1\n\
|
||||
procs_running 1\n\
|
||||
procs_blocked 0\n"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn proc_mounts_read(_entry: &ProcEntry, content: &mut String) -> Result<()> {
|
||||
// TODO: Get actual mount information
|
||||
let mounts = crate::fs::mount::get_all_mounts();
|
||||
for mount in mounts {
|
||||
content.push_str(&format!("none {} ramfs rw 0 0\n", mount));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mount proc filesystem
|
||||
pub fn mount_procfs(_dev_name: &str, _flags: u32, _data: Option<&str>) -> Result<Arc<SuperBlock>> {
|
||||
let mut sb = SuperBlock::new("proc")?;
|
||||
sb.s_magic = 0x9fa0; // PROC_SUPER_MAGIC
|
||||
|
||||
let procfs = Box::leak(Box::new(ProcFs::new()));
|
||||
sb.s_fs_info = Some(procfs as *mut ProcFs as *mut u8);
|
||||
|
||||
// Create root inode
|
||||
let root_inode = Arc::new({
|
||||
let mut inode = Inode::new(1, mode::S_IFDIR | 0o755);
|
||||
inode.set_operations(Arc::new(ProcInodeOps::new(procfs)));
|
||||
inode
|
||||
});
|
||||
|
||||
let root_dentry = Arc::new(Dentry::new(String::from("/"), Some(root_inode)));
|
||||
sb.s_root = Some(root_dentry);
|
||||
|
||||
Ok(Arc::new(sb))
|
||||
}
|
||||
|
||||
/// Register proc filesystem
|
||||
pub fn register_procfs() -> Result<()> {
|
||||
let procfs_type = FileSystemType::new(
|
||||
String::from("proc"),
|
||||
|_fstype, flags, _dev_name, data| mount_procfs(_dev_name, flags, data),
|
||||
|_sb| Ok(()),
|
||||
);
|
||||
|
||||
// TODO: Register with VFS
|
||||
crate::console::print_info("Registered proc filesystem\n");
|
||||
Ok(())
|
||||
}
|
||||
379
kernel/src/fs/ramfs.rs
Archivo normal
379
kernel/src/fs/ramfs.rs
Archivo normal
@@ -0,0 +1,379 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Simple RAM filesystem implementation
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::fs::*;
|
||||
use crate::sync::{Arc, Mutex};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::collections::BTreeMap;
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// RAM filesystem superblock
|
||||
pub struct RamFs {
|
||||
/// Next inode number
|
||||
next_ino: AtomicU64,
|
||||
/// Inode storage
|
||||
inodes: Mutex<BTreeMap<u64, Arc<Inode>>>,
|
||||
/// Directory entries
|
||||
entries: Mutex<BTreeMap<u64, Vec<Arc<Dentry>>>>,
|
||||
}
|
||||
|
||||
impl RamFs {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
next_ino: AtomicU64::new(1),
|
||||
inodes: Mutex::new(BTreeMap::new()),
|
||||
entries: Mutex::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_ino(&self) -> u64 {
|
||||
self.next_ino.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn create_inode(&self, mode: u32) -> Arc<Inode> {
|
||||
let ino = self.alloc_ino();
|
||||
let mut inode = Inode::new(ino, mode);
|
||||
inode.set_operations(Arc::new(RamFsInodeOps::new(self)));
|
||||
|
||||
let inode = Arc::new(inode);
|
||||
|
||||
let mut inodes = self.inodes.lock();
|
||||
inodes.insert(ino, inode.clone());
|
||||
|
||||
if mode::s_isdir(mode) {
|
||||
let mut entries = self.entries.lock();
|
||||
entries.insert(ino, Vec::new());
|
||||
}
|
||||
|
||||
inode
|
||||
}
|
||||
|
||||
fn get_inode(&self, ino: u64) -> Option<Arc<Inode>> {
|
||||
let inodes = self.inodes.lock();
|
||||
inodes.get(&ino).cloned()
|
||||
}
|
||||
|
||||
fn add_entry(&self, dir_ino: u64, name: String, child_ino: u64) -> Result<()> {
|
||||
let child_inode = self.get_inode(child_ino).ok_or(Error::ENOENT)?;
|
||||
let dentry = Arc::new(Dentry::new(name, Some(child_inode)));
|
||||
|
||||
let mut entries = self.entries.lock();
|
||||
if let Some(dir_entries) = entries.get_mut(&dir_ino) {
|
||||
dir_entries.push(dentry);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ENOTDIR)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_entry(&self, dir_ino: u64, name: &str) -> Option<Arc<Dentry>> {
|
||||
let entries = self.entries.lock();
|
||||
if let Some(dir_entries) = entries.get(&dir_ino) {
|
||||
for entry in dir_entries {
|
||||
if entry.d_name == name {
|
||||
return Some(entry.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn remove_entry(&self, dir_ino: u64, name: &str) -> Result<()> {
|
||||
let mut entries = self.entries.lock();
|
||||
if let Some(dir_entries) = entries.get_mut(&dir_ino) {
|
||||
if let Some(pos) = dir_entries.iter().position(|e| e.d_name == name) {
|
||||
dir_entries.remove(pos);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
} else {
|
||||
Err(Error::ENOTDIR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RAM filesystem inode operations
|
||||
pub struct RamFsInodeOps {
|
||||
fs: *const RamFs,
|
||||
}
|
||||
|
||||
impl RamFsInodeOps {
|
||||
fn new(fs: &RamFs) -> Self {
|
||||
Self {
|
||||
fs: fs as *const RamFs,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fs(&self) -> &RamFs {
|
||||
unsafe { &*self.fs }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for RamFsInodeOps {}
|
||||
unsafe impl Sync for RamFsInodeOps {}
|
||||
|
||||
impl InodeOperations for RamFsInodeOps {
|
||||
fn lookup(&self, dir: &Inode, name: &str) -> Result<Arc<Inode>> {
|
||||
let fs = self.get_fs();
|
||||
if let Some(entry) = fs.find_entry(dir.i_ino, name) {
|
||||
if let Some(inode) = entry.d_inode {
|
||||
Ok(inode)
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
fn create(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
let fs = self.get_fs();
|
||||
|
||||
// Check if entry already exists
|
||||
if fs.find_entry(dir.i_ino, name).is_some() {
|
||||
return Err(Error::EEXIST);
|
||||
}
|
||||
|
||||
let inode = fs.create_inode(mode | mode::S_IFREG);
|
||||
fs.add_entry(dir.i_ino, String::from(name), inode.i_ino)?;
|
||||
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
fn mkdir(&self, dir: &Inode, name: &str, mode: u32) -> Result<Arc<Inode>> {
|
||||
let fs = self.get_fs();
|
||||
|
||||
// Check if entry already exists
|
||||
if fs.find_entry(dir.i_ino, name).is_some() {
|
||||
return Err(Error::EEXIST);
|
||||
}
|
||||
|
||||
let inode = fs.create_inode(mode | mode::S_IFDIR);
|
||||
fs.add_entry(dir.i_ino, String::from(name), inode.i_ino)?;
|
||||
|
||||
// Add . and .. entries
|
||||
fs.add_entry(inode.i_ino, String::from("."), inode.i_ino)?;
|
||||
fs.add_entry(inode.i_ino, String::from(".."), dir.i_ino)?;
|
||||
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
fn unlink(&self, dir: &Inode, name: &str) -> Result<()> {
|
||||
let fs = self.get_fs();
|
||||
fs.remove_entry(dir.i_ino, name)
|
||||
}
|
||||
|
||||
fn rmdir(&self, dir: &Inode, name: &str) -> Result<()> {
|
||||
let fs = self.get_fs();
|
||||
|
||||
// Check if directory is empty (only . and .. entries)
|
||||
let entries = fs.entries.lock();
|
||||
if let Some(target_inode) = fs.find_entry(dir.i_ino, name)
|
||||
.and_then(|e| e.d_inode.clone()) {
|
||||
if let Some(dir_entries) = entries.get(&target_inode.i_ino) {
|
||||
if dir_entries.len() > 2 {
|
||||
return Err(Error::ENOTEMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(entries);
|
||||
|
||||
fs.remove_entry(dir.i_ino, name)
|
||||
}
|
||||
|
||||
fn symlink(&self, dir: &Inode, name: &str, target: &str) -> Result<Arc<Inode>> {
|
||||
let fs = self.get_fs();
|
||||
|
||||
// Check if entry already exists
|
||||
if fs.find_entry(dir.i_ino, name).is_some() {
|
||||
return Err(Error::EEXIST);
|
||||
}
|
||||
|
||||
let inode = fs.create_inode(mode::S_IFLNK | 0o777);
|
||||
// TODO: Store symlink target
|
||||
fs.add_entry(dir.i_ino, String::from(name), inode.i_ino)?;
|
||||
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
fn rename(&self, old_dir: &Inode, old_name: &str, new_dir: &Inode, new_name: &str) -> Result<()> {
|
||||
let fs = self.get_fs();
|
||||
|
||||
// Find the entry to rename
|
||||
if let Some(entry) = fs.find_entry(old_dir.i_ino, old_name) {
|
||||
// Remove from old location
|
||||
fs.remove_entry(old_dir.i_ino, old_name)?;
|
||||
|
||||
// Add to new location
|
||||
if let Some(inode) = entry.d_inode {
|
||||
fs.add_entry(new_dir.i_ino, String::from(new_name), inode.i_ino)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
fn setattr(&self, inode: &Inode, attr: &InodeAttr) -> Result<()> {
|
||||
// Apply basic attributes using generic implementation
|
||||
let generic_ops = GenericInodeOps;
|
||||
generic_ops.setattr(inode, attr)
|
||||
}
|
||||
|
||||
fn getattr(&self, inode: &Inode) -> Result<InodeAttr> {
|
||||
let generic_ops = GenericInodeOps;
|
||||
generic_ops.getattr(inode)
|
||||
}
|
||||
|
||||
fn readlink(&self, inode: &Inode) -> Result<String> {
|
||||
// TODO: Return stored symlink target
|
||||
Err(Error::EINVAL)
|
||||
}
|
||||
|
||||
fn follow_link(&self, inode: &Inode) -> Result<Arc<Inode>> {
|
||||
// TODO: Follow symlink to target
|
||||
Err(Error::EINVAL)
|
||||
}
|
||||
|
||||
fn truncate(&self, inode: &Inode, size: u64) -> Result<()> {
|
||||
inode.set_size(size);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn getxattr(&self, inode: &Inode, name: &str) -> Result<Vec<u8>> {
|
||||
Err(Error::ENODATA)
|
||||
}
|
||||
|
||||
fn setxattr(&self, inode: &Inode, name: &str, value: &[u8], flags: u32) -> Result<()> {
|
||||
Err(Error::ENOSYS)
|
||||
}
|
||||
|
||||
fn listxattr(&self, inode: &Inode) -> Result<Vec<String>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn removexattr(&self, inode: &Inode, name: &str) -> Result<()> {
|
||||
Err(Error::ENODATA)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mount RAM filesystem
|
||||
pub fn mount_ramfs(_dev_name: &str, _flags: u32, _data: Option<&str>) -> Result<Arc<SuperBlock>> {
|
||||
let mut sb = SuperBlock::new("ramfs")?;
|
||||
sb.s_magic = 0x858458f6; // RAMFS magic
|
||||
sb.set_operations(Arc::new(RamFsSuperOps));
|
||||
|
||||
let ramfs = Box::leak(Box::new(RamFs::new()));
|
||||
sb.s_fs_info = Some(ramfs as *mut RamFs as *mut u8);
|
||||
|
||||
// Create root directory
|
||||
let root_inode = ramfs.create_inode(mode::S_IFDIR | 0o755);
|
||||
let root_dentry = Arc::new(Dentry::new(String::from("/"), Some(root_inode)));
|
||||
|
||||
let sb = Arc::new(sb);
|
||||
Ok(sb)
|
||||
}
|
||||
|
||||
/// RAM filesystem superblock operations
|
||||
pub struct RamFsSuperOps;
|
||||
|
||||
impl SuperOperations for RamFsSuperOps {
|
||||
fn alloc_inode(&self, sb: &SuperBlock) -> Result<Arc<Inode>> {
|
||||
let ramfs = unsafe { &*(sb.s_fs_info.unwrap() as *const RamFs) };
|
||||
Ok(ramfs.create_inode(0o644))
|
||||
}
|
||||
|
||||
fn destroy_inode(&self, inode: &Inode) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_inode(&self, inode: &Inode, sync: bool) -> Result<()> {
|
||||
// RAM filesystem doesn't need to write inodes
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_inode(&self, inode: &Inode) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn put_super(&self, sb: &SuperBlock) -> Result<()> {
|
||||
if let Some(fs_info) = sb.s_fs_info {
|
||||
unsafe {
|
||||
let ramfs = Box::from_raw(fs_info as *mut RamFs);
|
||||
drop(ramfs);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_super(&self, sb: &SuperBlock) -> Result<()> {
|
||||
// Nothing to write for RAM filesystem
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sync_fs(&self, sb: &SuperBlock, wait: bool) -> Result<()> {
|
||||
// Nothing to sync for RAM filesystem
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn freeze_fs(&self, sb: &SuperBlock) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unfreeze_fs(&self, sb: &SuperBlock) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn statfs(&self, sb: &SuperBlock) -> Result<KStatFs> {
|
||||
Ok(KStatFs {
|
||||
f_type: sb.s_magic as u64,
|
||||
f_bsize: sb.s_blocksize as u64,
|
||||
f_blocks: 0, // Unlimited
|
||||
f_bfree: 0, // Unlimited
|
||||
f_bavail: 0, // Unlimited
|
||||
f_files: 0, // Dynamic
|
||||
f_ffree: 0, // Unlimited
|
||||
f_fsid: [0, 0],
|
||||
f_namelen: NAME_MAX as u64,
|
||||
f_frsize: sb.s_blocksize as u64,
|
||||
f_flags: 0,
|
||||
f_spare: [0; 4],
|
||||
})
|
||||
}
|
||||
|
||||
fn remount_fs(&self, sb: &SuperBlock, flags: u32, data: Option<&str>) -> Result<()> {
|
||||
sb.s_flags.store(flags, Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_options(&self, sb: &SuperBlock) -> Result<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// Kill RAM filesystem superblock
|
||||
pub fn kill_ramfs(sb: &SuperBlock) -> Result<()> {
|
||||
if let Some(ref ops) = sb.s_op {
|
||||
ops.put_super(sb)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Register RAM filesystem
|
||||
pub fn register_ramfs() -> Result<()> {
|
||||
let ramfs_type = FileSystemType::new(
|
||||
String::from("ramfs"),
|
||||
|_fstype, flags, _dev_name, data| mount_ramfs(_dev_name, flags, data),
|
||||
|sb| kill_ramfs(sb),
|
||||
);
|
||||
|
||||
// TODO: Register with VFS
|
||||
crate::console::print_info("Registered ramfs filesystem\n");
|
||||
Ok(())
|
||||
}
|
||||
349
kernel/src/fs/super_block.rs
Archivo normal
349
kernel/src/fs/super_block.rs
Archivo normal
@@ -0,0 +1,349 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Superblock abstraction - Linux compatible
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sync::{Arc, Mutex, RwLock};
|
||||
use crate::device::DeviceNumber;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
|
||||
|
||||
/// Superblock structure - similar to Linux struct super_block
|
||||
pub struct SuperBlock {
|
||||
/// Device number
|
||||
pub s_dev: DeviceNumber,
|
||||
/// Block size
|
||||
pub s_blocksize: u32,
|
||||
/// Block size bits
|
||||
pub s_blocksize_bits: u8,
|
||||
/// Maximum file size
|
||||
pub s_maxbytes: u64,
|
||||
/// File system type
|
||||
pub s_type: Option<Arc<FileSystemType>>,
|
||||
/// Superblock operations
|
||||
pub s_op: Option<Arc<dyn SuperOperations>>,
|
||||
/// Root dentry
|
||||
pub s_root: Option<Arc<super::Dentry>>,
|
||||
/// Mount point
|
||||
pub s_mount: Option<Arc<super::VfsMount>>,
|
||||
/// File system flags
|
||||
pub s_flags: AtomicU32,
|
||||
/// File system magic number
|
||||
pub s_magic: u32,
|
||||
/// List of inodes
|
||||
pub s_inodes: Mutex<Vec<Arc<super::Inode>>>,
|
||||
/// Next inode number
|
||||
pub s_next_ino: AtomicU64,
|
||||
/// Private data
|
||||
pub s_fs_info: Option<*mut u8>,
|
||||
/// Dirty inodes
|
||||
pub s_dirty: Mutex<Vec<Arc<super::Inode>>>,
|
||||
/// Reference count
|
||||
pub s_count: AtomicU32,
|
||||
/// File system name
|
||||
pub s_id: String,
|
||||
}
|
||||
|
||||
impl SuperBlock {
|
||||
/// Create a new superblock
|
||||
pub fn new(fstype: &str) -> Result<Self> {
|
||||
Ok(Self {
|
||||
s_dev: DeviceNumber::new(0, 0),
|
||||
s_blocksize: 4096,
|
||||
s_blocksize_bits: 12,
|
||||
s_maxbytes: 0x7fffffffffffffff,
|
||||
s_type: None,
|
||||
s_op: None,
|
||||
s_root: None,
|
||||
s_mount: None,
|
||||
s_flags: AtomicU32::new(0),
|
||||
s_magic: 0,
|
||||
s_inodes: Mutex::new(Vec::new()),
|
||||
s_next_ino: AtomicU64::new(1),
|
||||
s_fs_info: None,
|
||||
s_dirty: Mutex::new(Vec::new()),
|
||||
s_count: AtomicU32::new(1),
|
||||
s_id: String::from(fstype),
|
||||
})
|
||||
}
|
||||
|
||||
/// Set superblock operations
|
||||
pub fn set_operations(&mut self, ops: Arc<dyn SuperOperations>) {
|
||||
self.s_op = Some(ops);
|
||||
}
|
||||
|
||||
/// Allocate a new inode
|
||||
pub fn alloc_inode(&self, mode: u32) -> Result<Arc<super::Inode>> {
|
||||
let ino = self.s_next_ino.fetch_add(1, Ordering::Relaxed);
|
||||
let mut inode = super::Inode::new(ino, mode);
|
||||
inode.i_sb = Some(Arc::new(unsafe {
|
||||
// SAFETY: We're creating a weak reference to avoid cycles
|
||||
core::ptr::read(self as *const Self)
|
||||
}));
|
||||
|
||||
let inode = Arc::new(inode);
|
||||
|
||||
// Add to inode list
|
||||
let mut inodes = self.s_inodes.lock();
|
||||
inodes.push(inode.clone());
|
||||
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
/// Write superblock to disk
|
||||
pub fn write_super(&self) -> Result<()> {
|
||||
if let Some(ref ops) = self.s_op {
|
||||
ops.write_super(self)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Put superblock (decrement reference count)
|
||||
pub fn put_super(&self) -> Result<()> {
|
||||
let old_count = self.s_count.fetch_sub(1, Ordering::Relaxed);
|
||||
if old_count == 1 {
|
||||
// Last reference, cleanup
|
||||
if let Some(ref ops) = self.s_op {
|
||||
ops.put_super(self)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get superblock statistics
|
||||
pub fn statfs(&self) -> Result<super::KStatFs> {
|
||||
if let Some(ref ops) = self.s_op {
|
||||
ops.statfs(self)
|
||||
} else {
|
||||
// Default statistics
|
||||
Ok(super::KStatFs {
|
||||
f_type: self.s_magic as u64,
|
||||
f_bsize: self.s_blocksize as u64,
|
||||
f_blocks: 0,
|
||||
f_bfree: 0,
|
||||
f_bavail: 0,
|
||||
f_files: 0,
|
||||
f_ffree: 0,
|
||||
f_fsid: [0, 0],
|
||||
f_namelen: super::NAME_MAX as u64,
|
||||
f_frsize: self.s_blocksize as u64,
|
||||
f_flags: self.s_flags.load(Ordering::Relaxed) as u64,
|
||||
f_spare: [0; 4],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync filesystem
|
||||
pub fn sync_fs(&self, wait: bool) -> Result<()> {
|
||||
if let Some(ref ops) = self.s_op {
|
||||
ops.sync_fs(self, wait)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Freeze filesystem
|
||||
pub fn freeze_fs(&self) -> Result<()> {
|
||||
if let Some(ref ops) = self.s_op {
|
||||
ops.freeze_fs(self)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Unfreeze filesystem
|
||||
pub fn unfreeze_fs(&self) -> Result<()> {
|
||||
if let Some(ref ops) = self.s_op {
|
||||
ops.unfreeze_fs(self)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark inode as dirty
|
||||
pub fn mark_dirty(&self, inode: Arc<super::Inode>) {
|
||||
let mut dirty = self.s_dirty.lock();
|
||||
dirty.push(inode);
|
||||
}
|
||||
|
||||
/// Write back dirty inodes
|
||||
pub fn write_dirty(&self) -> Result<()> {
|
||||
let mut dirty = self.s_dirty.lock();
|
||||
for inode in dirty.drain(..) {
|
||||
// TODO: Write inode to disk
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for SuperBlock {}
|
||||
unsafe impl Sync for SuperBlock {}
|
||||
|
||||
/// Superblock operations trait - similar to Linux super_operations
|
||||
pub trait SuperOperations: Send + Sync {
|
||||
/// Allocate inode
|
||||
fn alloc_inode(&self, sb: &SuperBlock) -> Result<Arc<super::Inode>>;
|
||||
|
||||
/// Destroy inode
|
||||
fn destroy_inode(&self, inode: &super::Inode) -> Result<()>;
|
||||
|
||||
/// Write inode
|
||||
fn write_inode(&self, inode: &super::Inode, sync: bool) -> Result<()>;
|
||||
|
||||
/// Delete inode
|
||||
fn delete_inode(&self, inode: &super::Inode) -> Result<()>;
|
||||
|
||||
/// Put superblock
|
||||
fn put_super(&self, sb: &SuperBlock) -> Result<()>;
|
||||
|
||||
/// Write superblock
|
||||
fn write_super(&self, sb: &SuperBlock) -> Result<()>;
|
||||
|
||||
/// Sync filesystem
|
||||
fn sync_fs(&self, sb: &SuperBlock, wait: bool) -> Result<()>;
|
||||
|
||||
/// Freeze filesystem
|
||||
fn freeze_fs(&self, sb: &SuperBlock) -> Result<()>;
|
||||
|
||||
/// Unfreeze filesystem
|
||||
fn unfreeze_fs(&self, sb: &SuperBlock) -> Result<()>;
|
||||
|
||||
/// Get filesystem statistics
|
||||
fn statfs(&self, sb: &SuperBlock) -> Result<super::KStatFs>;
|
||||
|
||||
/// Remount filesystem
|
||||
fn remount_fs(&self, sb: &SuperBlock, flags: u32, data: Option<&str>) -> Result<()>;
|
||||
|
||||
/// Show mount options
|
||||
fn show_options(&self, sb: &SuperBlock) -> Result<String>;
|
||||
}
|
||||
|
||||
/// File system type structure - similar to Linux file_system_type
|
||||
pub struct FileSystemType {
|
||||
/// File system name
|
||||
pub name: String,
|
||||
/// File system flags
|
||||
pub fs_flags: u32,
|
||||
/// Mount function
|
||||
pub mount: fn(fstype: &FileSystemType, flags: u32, dev_name: &str, data: Option<&str>) -> Result<Arc<SuperBlock>>,
|
||||
/// Kill superblock function
|
||||
pub kill_sb: fn(sb: &SuperBlock) -> Result<()>,
|
||||
/// Owner module
|
||||
pub owner: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl FileSystemType {
|
||||
/// Create a new file system type
|
||||
pub fn new(
|
||||
name: String,
|
||||
mount: fn(&FileSystemType, u32, &str, Option<&str>) -> Result<Arc<SuperBlock>>,
|
||||
kill_sb: fn(&SuperBlock) -> Result<()>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
fs_flags: 0,
|
||||
mount,
|
||||
kill_sb,
|
||||
owner: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Mount this filesystem type
|
||||
pub fn do_mount(&self, flags: u32, dev_name: &str, data: Option<&str>) -> Result<Arc<SuperBlock>> {
|
||||
(self.mount)(self, flags, dev_name, data)
|
||||
}
|
||||
|
||||
/// Kill superblock
|
||||
pub fn do_kill_sb(&self, sb: &SuperBlock) -> Result<()> {
|
||||
(self.kill_sb)(sb)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic superblock operations
|
||||
pub struct GenericSuperOps;
|
||||
|
||||
impl SuperOperations for GenericSuperOps {
|
||||
fn alloc_inode(&self, sb: &SuperBlock) -> Result<Arc<super::Inode>> {
|
||||
sb.alloc_inode(0o644)
|
||||
}
|
||||
|
||||
fn destroy_inode(&self, inode: &super::Inode) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_inode(&self, inode: &super::Inode, sync: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_inode(&self, inode: &super::Inode) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn put_super(&self, sb: &SuperBlock) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_super(&self, sb: &SuperBlock) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sync_fs(&self, sb: &SuperBlock, wait: bool) -> Result<()> {
|
||||
sb.write_dirty()
|
||||
}
|
||||
|
||||
fn freeze_fs(&self, sb: &SuperBlock) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unfreeze_fs(&self, sb: &SuperBlock) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn statfs(&self, sb: &SuperBlock) -> Result<super::KStatFs> {
|
||||
sb.statfs()
|
||||
}
|
||||
|
||||
fn remount_fs(&self, sb: &SuperBlock, flags: u32, data: Option<&str>) -> Result<()> {
|
||||
sb.s_flags.store(flags, Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_options(&self, sb: &SuperBlock) -> Result<String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// File system flags
|
||||
pub const FS_REQUIRES_DEV: u32 = 1;
|
||||
pub const FS_BINARY_MOUNTDATA: u32 = 2;
|
||||
pub const FS_HAS_SUBTYPE: u32 = 4;
|
||||
pub const FS_USERNS_MOUNT: u32 = 8;
|
||||
pub const FS_DISALLOW_NOTIFY_PERM: u32 = 16;
|
||||
pub const FS_RENAME_DOES_D_MOVE: u32 = 32;
|
||||
|
||||
/// Mount flags
|
||||
pub const MS_RDONLY: u32 = 1;
|
||||
pub const MS_NOSUID: u32 = 2;
|
||||
pub const MS_NODEV: u32 = 4;
|
||||
pub const MS_NOEXEC: u32 = 8;
|
||||
pub const MS_SYNCHRONOUS: u32 = 16;
|
||||
pub const MS_REMOUNT: u32 = 32;
|
||||
pub const MS_MANDLOCK: u32 = 64;
|
||||
pub const MS_DIRSYNC: u32 = 128;
|
||||
pub const MS_NOATIME: u32 = 1024;
|
||||
pub const MS_NODIRATIME: u32 = 2048;
|
||||
pub const MS_BIND: u32 = 4096;
|
||||
pub const MS_MOVE: u32 = 8192;
|
||||
pub const MS_REC: u32 = 16384;
|
||||
pub const MS_SILENT: u32 = 32768;
|
||||
pub const MS_POSIXACL: u32 = 1 << 16;
|
||||
pub const MS_UNBINDABLE: u32 = 1 << 17;
|
||||
pub const MS_PRIVATE: u32 = 1 << 18;
|
||||
pub const MS_SLAVE: u32 = 1 << 19;
|
||||
pub const MS_SHARED: u32 = 1 << 20;
|
||||
pub const MS_RELATIME: u32 = 1 << 21;
|
||||
pub const MS_KERNMOUNT: u32 = 1 << 22;
|
||||
pub const MS_I_VERSION: u32 = 1 << 23;
|
||||
pub const MS_STRICTATIME: u32 = 1 << 24;
|
||||
83
kernel/src/init.rs
Archivo normal
83
kernel/src/init.rs
Archivo normal
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Kernel initialization
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::{info, error};
|
||||
|
||||
/// Early kernel initialization
|
||||
pub fn early_init() {
|
||||
info!("Starting Rust Kernel v{}", crate::VERSION);
|
||||
info!("Early initialization phase");
|
||||
|
||||
// Initialize basic console output
|
||||
if let Err(e) = crate::console::init() {
|
||||
// Can't print error since console isn't initialized yet
|
||||
loop {}
|
||||
}
|
||||
|
||||
info!("Console initialized");
|
||||
}
|
||||
|
||||
/// Main kernel initialization
|
||||
pub fn main_init() -> ! {
|
||||
info!("Main initialization phase");
|
||||
|
||||
// Initialize memory management
|
||||
if let Err(e) = crate::memory::init() {
|
||||
error!("Failed to initialize memory management: {}", e);
|
||||
panic!("Memory initialization failed");
|
||||
}
|
||||
info!("Memory management initialized");
|
||||
|
||||
// Initialize interrupt handling
|
||||
if let Err(e) = crate::interrupt::init() {
|
||||
error!("Failed to initialize interrupts: {}", e);
|
||||
panic!("Interrupt initialization failed");
|
||||
}
|
||||
info!("Interrupt handling initialized");
|
||||
|
||||
// Initialize scheduler
|
||||
if let Err(e) = crate::scheduler::init() {
|
||||
error!("Failed to initialize scheduler: {}", e);
|
||||
panic!("Scheduler initialization failed");
|
||||
}
|
||||
info!("Scheduler initialized");
|
||||
|
||||
// Initialize device subsystem
|
||||
if let Err(e) = crate::device::init() {
|
||||
error!("Failed to initialize devices: {}", e);
|
||||
panic!("Device initialization failed");
|
||||
}
|
||||
info!("Device subsystem initialized");
|
||||
|
||||
// Initialize VFS (Virtual File System)
|
||||
if let Err(e) = crate::fs::init() {
|
||||
error!("Failed to initialize VFS: {}", e);
|
||||
panic!("VFS initialization failed");
|
||||
}
|
||||
info!("VFS initialized");
|
||||
|
||||
info!("Kernel initialization completed");
|
||||
info!("Starting idle loop");
|
||||
|
||||
// Start the idle loop
|
||||
idle_loop();
|
||||
}
|
||||
|
||||
/// Kernel idle loop
|
||||
fn idle_loop() -> ! {
|
||||
loop {
|
||||
// TODO: Power management - halt CPU until interrupt
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
core::arch::asm!("hlt");
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
core::hint::spin_loop();
|
||||
|
||||
// TODO: Check for scheduled tasks
|
||||
// TODO: Handle background tasks
|
||||
}
|
||||
}
|
||||
388
kernel/src/interrupt.rs
Archivo normal
388
kernel/src/interrupt.rs
Archivo normal
@@ -0,0 +1,388 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Interrupt handling compatible with Linux kernel
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sync::Spinlock;
|
||||
use crate::types::Irq;
|
||||
use alloc::{vec::Vec, collections::BTreeMap};
|
||||
use core::fmt;
|
||||
|
||||
/// IRQ flags - compatible with Linux kernel
|
||||
pub mod irq_flags {
|
||||
pub const IRQF_SHARED: u32 = 0x00000080;
|
||||
pub const IRQF_TRIGGER_NONE: u32 = 0x00000000;
|
||||
pub const IRQF_TRIGGER_RISING: u32 = 0x00000001;
|
||||
pub const IRQF_TRIGGER_FALLING: u32 = 0x00000002;
|
||||
pub const IRQF_TRIGGER_HIGH: u32 = 0x00000004;
|
||||
pub const IRQF_TRIGGER_LOW: u32 = 0x00000008;
|
||||
pub const IRQF_ONESHOT: u32 = 0x00002000;
|
||||
pub const IRQF_NO_SUSPEND: u32 = 0x00004000;
|
||||
pub const IRQF_FORCE_RESUME: u32 = 0x00008000;
|
||||
pub const IRQF_NO_THREAD: u32 = 0x00010000;
|
||||
pub const IRQF_EARLY_RESUME: u32 = 0x00020000;
|
||||
pub const IRQF_COND_SUSPEND: u32 = 0x00040000;
|
||||
}
|
||||
|
||||
/// Interrupt return values - Linux compatible
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum IrqReturn {
|
||||
None, // IRQ_NONE
|
||||
Handled, // IRQ_HANDLED
|
||||
WakeThread, // IRQ_WAKE_THREAD
|
||||
}
|
||||
|
||||
impl fmt::Display for IrqReturn {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
IrqReturn::None => write!(f, "IRQ_NONE"),
|
||||
IrqReturn::Handled => write!(f, "IRQ_HANDLED"),
|
||||
IrqReturn::WakeThread => write!(f, "IRQ_WAKE_THREAD"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt handler function type - Linux compatible
|
||||
pub type IrqHandler = fn(irq: u32, dev_id: *mut u8) -> IrqReturn;
|
||||
|
||||
/// Interrupt action structure - similar to Linux irqaction
|
||||
#[derive(Debug)]
|
||||
pub struct IrqAction {
|
||||
pub handler: IrqHandler,
|
||||
pub flags: u32,
|
||||
pub name: &'static str,
|
||||
pub dev_id: *mut u8,
|
||||
pub next: Option<Box<IrqAction>>,
|
||||
}
|
||||
|
||||
impl IrqAction {
|
||||
pub fn new(handler: IrqHandler, flags: u32, name: &'static str, dev_id: *mut u8) -> Self {
|
||||
Self {
|
||||
handler,
|
||||
flags,
|
||||
name,
|
||||
dev_id,
|
||||
next: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt descriptor - similar to Linux irq_desc
|
||||
#[derive(Debug)]
|
||||
pub struct IrqDescriptor {
|
||||
pub irq: u32,
|
||||
pub action: Option<Box<IrqAction>>,
|
||||
pub depth: u32, // nesting depth for disable/enable
|
||||
pub wake_depth: u32, // wake nesting depth
|
||||
pub irq_count: u64, // number of interrupts
|
||||
pub irqs_unhandled: u64, // number of unhandled interrupts
|
||||
pub name: &'static str,
|
||||
pub status: u32,
|
||||
}
|
||||
|
||||
impl IrqDescriptor {
|
||||
pub fn new(irq: u32, name: &'static str) -> Self {
|
||||
Self {
|
||||
irq,
|
||||
action: None,
|
||||
depth: 1, // starts disabled
|
||||
wake_depth: 0,
|
||||
irq_count: 0,
|
||||
irqs_unhandled: 0,
|
||||
name,
|
||||
status: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.depth == 0
|
||||
}
|
||||
|
||||
pub fn enable(&mut self) {
|
||||
if self.depth > 0 {
|
||||
self.depth -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable(&mut self) {
|
||||
self.depth += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Global interrupt subsystem
|
||||
static INTERRUPT_SUBSYSTEM: Spinlock<InterruptSubsystem> = Spinlock::new(InterruptSubsystem::new());
|
||||
|
||||
/// Interrupt subsystem state
|
||||
struct InterruptSubsystem {
|
||||
descriptors: BTreeMap<u32, IrqDescriptor>,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl InterruptSubsystem {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
descriptors: BTreeMap::new(),
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_descriptor(&mut self, desc: IrqDescriptor) {
|
||||
let irq = desc.irq;
|
||||
self.descriptors.insert(irq, desc);
|
||||
}
|
||||
|
||||
fn get_descriptor(&mut self, irq: u32) -> Option<&mut IrqDescriptor> {
|
||||
self.descriptors.get_mut(&irq)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize interrupt handling
|
||||
pub fn init() -> Result<()> {
|
||||
let mut subsystem = INTERRUPT_SUBSYSTEM.lock();
|
||||
|
||||
// Set up standard x86 interrupt vectors
|
||||
init_standard_interrupts(&mut subsystem)?;
|
||||
|
||||
// Initialize interrupt controller (PIC/APIC)
|
||||
init_interrupt_controller()?;
|
||||
|
||||
// Set up exception handlers
|
||||
init_exception_handlers()?;
|
||||
|
||||
subsystem.enabled = true;
|
||||
crate::info!("Interrupt subsystem initialized");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize standard x86 interrupts
|
||||
fn init_standard_interrupts(subsystem: &mut InterruptSubsystem) -> Result<()> {
|
||||
// Timer interrupt (IRQ 0)
|
||||
let timer_desc = IrqDescriptor::new(0, "timer");
|
||||
subsystem.add_descriptor(timer_desc);
|
||||
|
||||
// Keyboard interrupt (IRQ 1)
|
||||
let keyboard_desc = IrqDescriptor::new(1, "keyboard");
|
||||
subsystem.add_descriptor(keyboard_desc);
|
||||
|
||||
// Cascade for slave PIC (IRQ 2)
|
||||
let cascade_desc = IrqDescriptor::new(2, "cascade");
|
||||
subsystem.add_descriptor(cascade_desc);
|
||||
|
||||
// Serial port 2/4 (IRQ 3)
|
||||
let serial_desc = IrqDescriptor::new(3, "serial");
|
||||
subsystem.add_descriptor(serial_desc);
|
||||
|
||||
// Serial port 1/3 (IRQ 4)
|
||||
let serial2_desc = IrqDescriptor::new(4, "serial");
|
||||
subsystem.add_descriptor(serial2_desc);
|
||||
|
||||
// Parallel port (IRQ 7)
|
||||
let parallel_desc = IrqDescriptor::new(7, "parallel");
|
||||
subsystem.add_descriptor(parallel_desc);
|
||||
|
||||
// Real-time clock (IRQ 8)
|
||||
let rtc_desc = IrqDescriptor::new(8, "rtc");
|
||||
subsystem.add_descriptor(rtc_desc);
|
||||
|
||||
// Mouse (IRQ 12)
|
||||
let mouse_desc = IrqDescriptor::new(12, "mouse");
|
||||
subsystem.add_descriptor(mouse_desc);
|
||||
|
||||
// IDE primary (IRQ 14)
|
||||
let ide1_desc = IrqDescriptor::new(14, "ide");
|
||||
subsystem.add_descriptor(ide1_desc);
|
||||
|
||||
// IDE secondary (IRQ 15)
|
||||
let ide2_desc = IrqDescriptor::new(15, "ide");
|
||||
subsystem.add_descriptor(ide2_desc);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize interrupt controller
|
||||
fn init_interrupt_controller() -> Result<()> {
|
||||
// TODO: Initialize PIC or APIC
|
||||
// For now, just set up basic PIC configuration
|
||||
unsafe {
|
||||
// Remap PIC interrupts to avoid conflicts with CPU exceptions
|
||||
crate::arch::x86_64::pic::init_pic();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize exception handlers
|
||||
fn init_exception_handlers() -> Result<()> {
|
||||
// TODO: Set up IDT with exception handlers
|
||||
// For now, placeholder
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register an interrupt handler - Linux compatible
|
||||
pub fn request_irq(
|
||||
irq: u32,
|
||||
handler: IrqHandler,
|
||||
flags: u32,
|
||||
name: &'static str,
|
||||
dev_id: *mut u8,
|
||||
) -> Result<()> {
|
||||
let mut subsystem = INTERRUPT_SUBSYSTEM.lock();
|
||||
|
||||
if let Some(desc) = subsystem.get_descriptor(irq) {
|
||||
let action = IrqAction::new(handler, flags, name, dev_id);
|
||||
|
||||
// Check if IRQ is shared
|
||||
if flags & irq_flags::IRQF_SHARED != 0 {
|
||||
// Add to action chain
|
||||
let mut current = &mut desc.action;
|
||||
while let Some(ref mut act) = current {
|
||||
current = &mut act.next;
|
||||
}
|
||||
*current = Some(Box::new(action));
|
||||
} else {
|
||||
// Replace existing action
|
||||
desc.action = Some(Box::new(action));
|
||||
}
|
||||
|
||||
// Enable the interrupt
|
||||
desc.enable();
|
||||
|
||||
crate::info!("Registered IRQ {} handler: {}", irq, name);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unregister an interrupt handler - Linux compatible
|
||||
pub fn free_irq(irq: u32, dev_id: *mut u8) -> Result<()> {
|
||||
let mut subsystem = INTERRUPT_SUBSYSTEM.lock();
|
||||
|
||||
if let Some(desc) = subsystem.get_descriptor(irq) {
|
||||
// Remove action with matching dev_id
|
||||
let mut current = &mut desc.action;
|
||||
let mut found = false;
|
||||
|
||||
while let Some(ref mut action) = current {
|
||||
if action.dev_id == dev_id {
|
||||
*current = action.next.take();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
current = &mut action.next;
|
||||
}
|
||||
|
||||
if found {
|
||||
// If no more actions, disable the interrupt
|
||||
if desc.action.is_none() {
|
||||
desc.disable();
|
||||
}
|
||||
|
||||
crate::info!("Freed IRQ {} handler", irq);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable interrupts globally
|
||||
pub fn enable() {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
core::arch::asm!("sti");
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable interrupts globally
|
||||
pub fn disable() {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
core::arch::asm!("cli");
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable a specific interrupt line
|
||||
pub fn enable_irq(irq: u32) -> Result<()> {
|
||||
let mut subsystem = INTERRUPT_SUBSYSTEM.lock();
|
||||
|
||||
if let Some(desc) = subsystem.get_descriptor(irq) {
|
||||
desc.enable();
|
||||
crate::debug!("Enabled IRQ {}", irq);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable a specific interrupt line
|
||||
pub fn disable_irq(irq: u32) -> Result<()> {
|
||||
let mut subsystem = INTERRUPT_SUBSYSTEM.lock();
|
||||
|
||||
if let Some(desc) = subsystem.get_descriptor(irq) {
|
||||
desc.disable();
|
||||
crate::debug!("Disabled IRQ {}", irq);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle an interrupt - called from low-level interrupt handlers
|
||||
pub fn handle_interrupt(irq: u32) {
|
||||
let mut subsystem = INTERRUPT_SUBSYSTEM.lock();
|
||||
|
||||
if let Some(desc) = subsystem.get_descriptor(irq) {
|
||||
desc.irq_count += 1;
|
||||
|
||||
if !desc.is_enabled() {
|
||||
// Interrupt is disabled, shouldn't happen
|
||||
crate::warn!("Received disabled interrupt {}", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call all handlers in the action chain
|
||||
let mut current = desc.action.as_ref();
|
||||
let mut handled = false;
|
||||
|
||||
while let Some(action) = current {
|
||||
let result = (action.handler)(irq, action.dev_id);
|
||||
match result {
|
||||
IrqReturn::Handled => {
|
||||
handled = true;
|
||||
}
|
||||
IrqReturn::WakeThread => {
|
||||
handled = true;
|
||||
// TODO: Wake threaded interrupt handler
|
||||
}
|
||||
IrqReturn::None => {
|
||||
// Handler didn't handle this interrupt
|
||||
}
|
||||
}
|
||||
current = action.next.as_ref();
|
||||
}
|
||||
|
||||
if !handled {
|
||||
desc.irqs_unhandled += 1;
|
||||
if desc.irqs_unhandled > 100 {
|
||||
crate::warn!("Too many unhandled interrupts on IRQ {}", irq);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
crate::warn!("Spurious interrupt {}", irq);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interrupt statistics
|
||||
pub fn get_irq_stats() -> Vec<(u32, &'static str, u64, u64)> {
|
||||
let subsystem = INTERRUPT_SUBSYSTEM.lock();
|
||||
let mut stats = Vec::new();
|
||||
|
||||
for (irq, desc) in &subsystem.descriptors {
|
||||
stats.push((*irq, desc.name, desc.irq_count, desc.irqs_unhandled));
|
||||
}
|
||||
|
||||
stats
|
||||
}
|
||||
97
kernel/src/lib.rs
Archivo normal
97
kernel/src/lib.rs
Archivo normal
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! The Rust kernel crate.
|
||||
//!
|
||||
//! This crate provides the core kernel APIs and functionality for the Rust kernel.
|
||||
//! It is inspired by the Linux kernel's Rust infrastructure but designed as a
|
||||
//! standalone kernel implementation.
|
||||
|
||||
#![no_std]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(asm_const)]
|
||||
#![feature(const_mut_refs)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![test_runner(crate::test_runner)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod allocator;
|
||||
pub mod arch;
|
||||
pub mod boot;
|
||||
pub mod console;
|
||||
pub mod cpu;
|
||||
pub mod device;
|
||||
pub mod driver;
|
||||
pub mod error;
|
||||
pub mod fs;
|
||||
pub mod init;
|
||||
pub mod interrupt;
|
||||
pub mod memory;
|
||||
pub mod module;
|
||||
pub mod panic;
|
||||
pub mod prelude;
|
||||
pub mod process;
|
||||
pub mod scheduler;
|
||||
pub mod sync;
|
||||
pub mod syscall;
|
||||
pub mod task;
|
||||
pub mod time;
|
||||
pub mod types;
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
/// Kernel version information
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const NAME: &str = "Rust Kernel";
|
||||
|
||||
/// Kernel entry point called from architecture-specific code
|
||||
#[no_mangle]
|
||||
pub extern "C" fn kernel_main() -> ! {
|
||||
init::early_init();
|
||||
init::main_init();
|
||||
|
||||
// Should not return from main_init
|
||||
panic!("kernel_main returned unexpectedly");
|
||||
}
|
||||
|
||||
/// Test runner for kernel tests
|
||||
#[cfg(test)]
|
||||
fn test_runner(tests: &[&dyn Fn()]) {
|
||||
println!("Running {} tests", tests.len());
|
||||
for test in tests {
|
||||
test();
|
||||
}
|
||||
exit_qemu(QemuExitCode::Success);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum QemuExitCode {
|
||||
Success = 0x10,
|
||||
Failed = 0x11,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn exit_qemu(exit_code: QemuExitCode) {
|
||||
use arch::x86_64::port::Port;
|
||||
|
||||
unsafe {
|
||||
let mut port = Port::new(0xf4);
|
||||
port.write(exit_code as u32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Kernel panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
panic::panic_handler(info)
|
||||
}
|
||||
|
||||
/// Global allocator error handler
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
|
||||
panic!("allocation error: {:?}", layout)
|
||||
}
|
||||
32
kernel/src/memory/allocator.rs
Archivo normal
32
kernel/src/memory/allocator.rs
Archivo normal
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Heap allocator implementation
|
||||
|
||||
use crate::memory::ALLOCATOR;
|
||||
use crate::types::{VirtAddr, PAGE_SIZE};
|
||||
use crate::error::Result;
|
||||
|
||||
/// Heap start address (will be set during initialization)
|
||||
static mut HEAP_START: usize = 0;
|
||||
static mut HEAP_SIZE: usize = 0;
|
||||
|
||||
/// Initialize the heap allocator
|
||||
pub fn init() -> Result<()> {
|
||||
// TODO: Get heap region from memory map
|
||||
// For now, use a fixed region
|
||||
let heap_start = 0x_4444_4444_0000;
|
||||
let heap_size = 100 * 1024; // 100 KB
|
||||
|
||||
unsafe {
|
||||
HEAP_START = heap_start;
|
||||
HEAP_SIZE = heap_size;
|
||||
ALLOCATOR.lock().init(heap_start, heap_size);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get heap statistics
|
||||
pub fn heap_stats() -> (usize, usize) {
|
||||
unsafe { (HEAP_START, HEAP_SIZE) }
|
||||
}
|
||||
16
kernel/src/memory/kmalloc.rs
Archivo normal
16
kernel/src/memory/kmalloc.rs
Archivo normal
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Kernel memory allocation (kmalloc)
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
/// Allocate kernel memory
|
||||
pub fn kmalloc(size: usize) -> Result<*mut u8> {
|
||||
// TODO: implement proper kmalloc
|
||||
Ok(core::ptr::null_mut())
|
||||
}
|
||||
|
||||
/// Free kernel memory
|
||||
pub fn kfree(ptr: *mut u8) {
|
||||
// TODO: implement proper kfree
|
||||
}
|
||||
230
kernel/src/memory/mod.rs
Archivo normal
230
kernel/src/memory/mod.rs
Archivo normal
@@ -0,0 +1,230 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Memory management subsystem
|
||||
|
||||
pub mod allocator;
|
||||
pub mod page;
|
||||
pub mod vmalloc;
|
||||
pub mod kmalloc;
|
||||
|
||||
use crate::types::{PhysAddr, VirtAddr, Pfn};
|
||||
use crate::error::{Error, Result};
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use linked_list_allocator::LockedHeap;
|
||||
|
||||
/// GFP (Get Free Pages) flags - compatible with Linux kernel
|
||||
pub mod gfp {
|
||||
pub const GFP_KERNEL: u32 = 0;
|
||||
pub const GFP_ATOMIC: u32 = 1;
|
||||
pub const GFP_USER: u32 = 2;
|
||||
pub const GFP_HIGHUSER: u32 = 3;
|
||||
pub const GFP_DMA: u32 = 4;
|
||||
pub const GFP_DMA32: u32 = 8;
|
||||
pub const GFP_NOWAIT: u32 = 16;
|
||||
pub const GFP_NOIO: u32 = 32;
|
||||
pub const GFP_NOFS: u32 = 64;
|
||||
pub const GFP_ZERO: u32 = 128;
|
||||
}
|
||||
|
||||
/// Global heap allocator
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||
|
||||
/// Linux-compatible allocation flags
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct AllocFlags(u32);
|
||||
|
||||
impl AllocFlags {
|
||||
pub const fn new(flags: u32) -> Self {
|
||||
Self(flags)
|
||||
}
|
||||
|
||||
pub fn as_raw(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn contains(self, flags: AllocFlags) -> bool {
|
||||
(self.0 & flags.0) == flags.0
|
||||
}
|
||||
}
|
||||
|
||||
/// GFP flags constants
|
||||
pub const GFP_KERNEL: AllocFlags = AllocFlags::new(gfp::GFP_KERNEL);
|
||||
pub const GFP_ATOMIC: AllocFlags = AllocFlags::new(gfp::GFP_ATOMIC);
|
||||
pub const GFP_USER: AllocFlags = AllocFlags::new(gfp::GFP_USER);
|
||||
|
||||
/// Initialize the memory management subsystem with proper Linux-style initialization
|
||||
pub fn init() -> Result<()> {
|
||||
allocator::init()?;
|
||||
page::init()?;
|
||||
|
||||
// Initialize zone allocator
|
||||
init_zones()?;
|
||||
|
||||
// Set up buddy allocator
|
||||
init_buddy_allocator()?;
|
||||
|
||||
// Initialize slab allocator
|
||||
init_slab_allocator()?;
|
||||
|
||||
crate::info!("Memory management initialized");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize memory zones (DMA, Normal, High)
|
||||
fn init_zones() -> Result<()> {
|
||||
// TODO: Set up memory zones based on architecture
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize buddy allocator for page allocation
|
||||
fn init_buddy_allocator() -> Result<()> {
|
||||
// TODO: Set up buddy allocator
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initialize slab allocator for object caching
|
||||
fn init_slab_allocator() -> Result<()> {
|
||||
// TODO: Set up SLAB/SLUB allocator
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Physical memory information
|
||||
#[derive(Debug)]
|
||||
pub struct MemoryInfo {
|
||||
pub total_pages: usize,
|
||||
pub free_pages: usize,
|
||||
pub used_pages: usize,
|
||||
pub kernel_pages: usize,
|
||||
}
|
||||
|
||||
/// Get current memory information
|
||||
pub fn memory_info() -> MemoryInfo {
|
||||
MemoryInfo {
|
||||
total_pages: 0, // TODO: implement
|
||||
free_pages: 0,
|
||||
used_pages: 0,
|
||||
kernel_pages: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a page of physical memory
|
||||
pub fn alloc_page() -> Result<PhysAddr> {
|
||||
page::alloc_page()
|
||||
}
|
||||
|
||||
/// Free a page of physical memory
|
||||
pub fn free_page(addr: PhysAddr) {
|
||||
page::free_page(addr)
|
||||
}
|
||||
|
||||
/// Map a virtual address to a physical address
|
||||
pub fn map_page(virt: VirtAddr, phys: PhysAddr) -> Result<()> {
|
||||
// TODO: implement page table mapping
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unmap a virtual address
|
||||
pub fn unmap_page(virt: VirtAddr) -> Result<()> {
|
||||
// TODO: implement page table unmapping
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convert virtual address to physical address
|
||||
pub fn virt_to_phys(virt: VirtAddr) -> Result<PhysAddr> {
|
||||
// TODO: implement address translation
|
||||
Ok(PhysAddr::new(virt.as_usize()))
|
||||
}
|
||||
|
||||
/// Convert physical address to virtual address
|
||||
pub fn phys_to_virt(phys: PhysAddr) -> Result<VirtAddr> {
|
||||
// TODO: implement address translation
|
||||
Ok(VirtAddr::new(phys.as_usize()))
|
||||
}
|
||||
|
||||
/// Page table entry
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PageTableEntry(pub u64);
|
||||
|
||||
impl PageTableEntry {
|
||||
pub const fn new() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
pub fn present(self) -> bool {
|
||||
self.0 & 1 != 0
|
||||
}
|
||||
|
||||
pub fn writable(self) -> bool {
|
||||
self.0 & 2 != 0
|
||||
}
|
||||
|
||||
pub fn user_accessible(self) -> bool {
|
||||
self.0 & 4 != 0
|
||||
}
|
||||
|
||||
pub fn frame(self) -> Pfn {
|
||||
Pfn((self.0 >> 12) as usize)
|
||||
}
|
||||
|
||||
pub fn set_present(&mut self, present: bool) {
|
||||
if present {
|
||||
self.0 |= 1;
|
||||
} else {
|
||||
self.0 &= !1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_writable(&mut self, writable: bool) {
|
||||
if writable {
|
||||
self.0 |= 2;
|
||||
} else {
|
||||
self.0 &= !2;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_user_accessible(&mut self, user: bool) {
|
||||
if user {
|
||||
self.0 |= 4;
|
||||
} else {
|
||||
self.0 &= !4;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_frame(&mut self, frame: Pfn) {
|
||||
self.0 = (self.0 & 0xfff) | ((frame.0 as u64) << 12);
|
||||
}
|
||||
}
|
||||
|
||||
/// Page table
|
||||
#[repr(align(4096))]
|
||||
pub struct PageTable {
|
||||
entries: [PageTableEntry; 512],
|
||||
}
|
||||
|
||||
impl PageTable {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
entries: [PageTableEntry::new(); 512],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zero(&mut self) {
|
||||
for entry in self.entries.iter_mut() {
|
||||
*entry = PageTableEntry::new();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory mapping flags
|
||||
bitflags::bitflags! {
|
||||
pub struct MapFlags: u32 {
|
||||
const READ = 1 << 0;
|
||||
const WRITE = 1 << 1;
|
||||
const EXECUTE = 1 << 2;
|
||||
const USER = 1 << 3;
|
||||
const GLOBAL = 1 << 4;
|
||||
const CACHED = 1 << 5;
|
||||
const DEVICE = 1 << 6;
|
||||
}
|
||||
}
|
||||
93
kernel/src/memory/page.rs
Archivo normal
93
kernel/src/memory/page.rs
Archivo normal
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Page frame allocator
|
||||
|
||||
use crate::types::{PhysAddr, Pfn};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sync::Spinlock;
|
||||
use alloc::collections::BTreeSet;
|
||||
|
||||
/// Page frame allocator
|
||||
static PAGE_ALLOCATOR: Spinlock<PageAllocator> = Spinlock::new(PageAllocator::new());
|
||||
|
||||
/// Page allocator implementation
|
||||
struct PageAllocator {
|
||||
free_pages: BTreeSet<Pfn>,
|
||||
total_pages: usize,
|
||||
allocated_pages: usize,
|
||||
}
|
||||
|
||||
impl PageAllocator {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
free_pages: BTreeSet::new(),
|
||||
total_pages: 0,
|
||||
allocated_pages: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a range of pages to the free list
|
||||
fn add_free_range(&mut self, start: Pfn, count: usize) {
|
||||
for i in 0..count {
|
||||
self.free_pages.insert(Pfn(start.0 + i));
|
||||
}
|
||||
self.total_pages += count;
|
||||
}
|
||||
|
||||
/// Allocate a single page
|
||||
fn alloc_page(&mut self) -> Result<Pfn> {
|
||||
if let Some(pfn) = self.free_pages.iter().next().copied() {
|
||||
self.free_pages.remove(&pfn);
|
||||
self.allocated_pages += 1;
|
||||
Ok(pfn)
|
||||
} else {
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
}
|
||||
|
||||
/// Free a single page
|
||||
fn free_page(&mut self, pfn: Pfn) {
|
||||
if self.free_pages.insert(pfn) {
|
||||
self.allocated_pages -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get statistics
|
||||
fn stats(&self) -> (usize, usize, usize) {
|
||||
(self.total_pages, self.allocated_pages, self.free_pages.len())
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the page allocator
|
||||
pub fn init() -> Result<()> {
|
||||
let mut allocator = PAGE_ALLOCATOR.lock();
|
||||
|
||||
// TODO: Get memory map from bootloader/firmware
|
||||
// For now, add a dummy range
|
||||
let start_pfn = Pfn(0x1000); // Start at 16MB
|
||||
let count = 0x10000; // 256MB worth of pages
|
||||
|
||||
allocator.add_free_range(start_pfn, count);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Allocate a page of physical memory
|
||||
pub fn alloc_page() -> Result<PhysAddr> {
|
||||
let mut allocator = PAGE_ALLOCATOR.lock();
|
||||
let pfn = allocator.alloc_page()?;
|
||||
Ok(pfn.to_phys_addr())
|
||||
}
|
||||
|
||||
/// Free a page of physical memory
|
||||
pub fn free_page(addr: PhysAddr) {
|
||||
let pfn = Pfn::from_phys_addr(addr);
|
||||
let mut allocator = PAGE_ALLOCATOR.lock();
|
||||
allocator.free_page(pfn);
|
||||
}
|
||||
|
||||
/// Get page allocator statistics
|
||||
pub fn stats() -> (usize, usize, usize) {
|
||||
let allocator = PAGE_ALLOCATOR.lock();
|
||||
allocator.stats()
|
||||
}
|
||||
17
kernel/src/memory/vmalloc.rs
Archivo normal
17
kernel/src/memory/vmalloc.rs
Archivo normal
@@ -0,0 +1,17 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Virtual memory allocation
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::types::VirtAddr;
|
||||
|
||||
/// Allocate virtual memory
|
||||
pub fn vmalloc(size: usize) -> Result<VirtAddr> {
|
||||
// TODO: implement proper vmalloc
|
||||
Ok(VirtAddr::new(0))
|
||||
}
|
||||
|
||||
/// Free virtual memory
|
||||
pub fn vfree(addr: VirtAddr) {
|
||||
// TODO: implement proper vfree
|
||||
}
|
||||
24
kernel/src/module.rs
Archivo normal
24
kernel/src/module.rs
Archivo normal
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Kernel module support
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
/// Module metadata
|
||||
pub struct ThisModule {
|
||||
pub name: &'static str,
|
||||
pub author: &'static str,
|
||||
pub description: &'static str,
|
||||
pub license: &'static str,
|
||||
}
|
||||
|
||||
/// Trait for kernel modules
|
||||
pub trait Module: Sized {
|
||||
/// Initialize the module
|
||||
fn init(module: &'static ThisModule) -> Result<Self>;
|
||||
|
||||
/// Clean up the module
|
||||
fn exit(module: &'static ThisModule) {
|
||||
// Default implementation does nothing
|
||||
}
|
||||
}
|
||||
70
kernel/src/panic.rs
Archivo normal
70
kernel/src/panic.rs
Archivo normal
@@ -0,0 +1,70 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Kernel panic handler
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use core::fmt::Write;
|
||||
|
||||
/// Panic handler
|
||||
pub fn panic_handler(info: &PanicInfo) -> ! {
|
||||
// Disable interrupts
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
core::arch::asm!("cli");
|
||||
}
|
||||
|
||||
// Print panic information
|
||||
let mut writer = PanicWriter;
|
||||
writeln!(writer, "\n\n=== KERNEL PANIC ===").ok();
|
||||
|
||||
if let Some(location) = info.location() {
|
||||
writeln!(
|
||||
writer,
|
||||
"Panic at {}:{}:{}",
|
||||
location.file(),
|
||||
location.line(),
|
||||
location.column()
|
||||
).ok();
|
||||
}
|
||||
|
||||
if let Some(message) = info.message() {
|
||||
writeln!(writer, "Message: {}", message).ok();
|
||||
}
|
||||
|
||||
writeln!(writer, "===================\n").ok();
|
||||
|
||||
// TODO: Print stack trace
|
||||
// TODO: Save panic information to log
|
||||
|
||||
// Halt the system
|
||||
loop {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
core::arch::asm!("hlt");
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Writer for panic messages
|
||||
struct PanicWriter;
|
||||
|
||||
impl Write for PanicWriter {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
// Write directly to VGA buffer or serial port
|
||||
for byte in s.bytes() {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
// Write to serial port (COM1)
|
||||
core::arch::asm!(
|
||||
"out dx, al",
|
||||
in("dx") 0x3f8u16,
|
||||
in("al") byte,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
111
kernel/src/prelude.rs
Archivo normal
111
kernel/src/prelude.rs
Archivo normal
@@ -0,0 +1,111 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Kernel prelude - commonly used types and traits
|
||||
|
||||
pub use crate::error::{Error, Result};
|
||||
pub use crate::types::*;
|
||||
pub use crate::sync::{Mutex, RwLock, Spinlock};
|
||||
pub use crate::memory::{PhysAddr, VirtAddr, Page, PageTable};
|
||||
pub use crate::device::Device;
|
||||
pub use crate::driver::{Driver, DriverOps};
|
||||
pub use crate::process::{Process, Thread};
|
||||
pub use crate::task::Task;
|
||||
|
||||
// Re-export common alloc types
|
||||
pub use alloc::{
|
||||
boxed::Box,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
format,
|
||||
vec,
|
||||
};
|
||||
|
||||
// Re-export core types
|
||||
pub use core::{
|
||||
mem,
|
||||
ptr,
|
||||
slice,
|
||||
str,
|
||||
fmt,
|
||||
result::Result as CoreResult,
|
||||
option::Option::{self, None, Some},
|
||||
};
|
||||
|
||||
/// Print macros for kernel logging
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::console::_print(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! kprint {
|
||||
($($arg:tt)*) => ($crate::console::_kprint(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! kprintln {
|
||||
() => ($crate::kprint!("\n"));
|
||||
($($arg:tt)*) => ($crate::kprint!("[KERNEL] {}\n", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)*) => {
|
||||
#[cfg(feature = "debug")]
|
||||
$crate::kprintln!("[DEBUG] {}", format_args!($($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)*) => ($crate::kprintln!("[INFO] {}", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($($arg:tt)*) => ($crate::kprintln!("[WARN] {}", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => ($crate::kprintln!("[ERROR] {}", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
/// Module definition macro
|
||||
#[macro_export]
|
||||
macro_rules! module {
|
||||
(
|
||||
type: $type:ty,
|
||||
name: $name:expr,
|
||||
author: $author:expr,
|
||||
description: $description:expr,
|
||||
license: $license:expr $(,)?
|
||||
) => {
|
||||
static __THIS_MODULE: $crate::module::ThisModule = $crate::module::ThisModule {
|
||||
name: $name,
|
||||
author: $author,
|
||||
description: $description,
|
||||
license: $license,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn init_module() -> core::ffi::c_int {
|
||||
match <$type as $crate::module::Module>::init(&__THIS_MODULE) {
|
||||
Ok(_) => 0,
|
||||
Err(e) => e.to_errno(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn cleanup_module() {
|
||||
<$type as $crate::module::Module>::exit(&__THIS_MODULE)
|
||||
}
|
||||
};
|
||||
}
|
||||
274
kernel/src/process.rs
Archivo normal
274
kernel/src/process.rs
Archivo normal
@@ -0,0 +1,274 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Process and thread management
|
||||
|
||||
use crate::types::{Pid, Tid, Uid, Gid};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::sync::Spinlock;
|
||||
use crate::memory::VirtAddr;
|
||||
use alloc::{string::String, vec::Vec, collections::BTreeMap};
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
/// Process state - compatible with Linux kernel
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ProcessState {
|
||||
Running,
|
||||
Sleeping,
|
||||
Stopped,
|
||||
Zombie,
|
||||
Dead,
|
||||
}
|
||||
|
||||
/// Process structure - similar to Linux task_struct
|
||||
#[derive(Debug)]
|
||||
pub struct Process {
|
||||
pub pid: Pid,
|
||||
pub parent: Option<Pid>,
|
||||
pub state: ProcessState,
|
||||
pub uid: Uid,
|
||||
pub gid: Gid,
|
||||
pub name: String,
|
||||
pub threads: Vec<Thread>,
|
||||
pub memory_map: Option<VirtAddr>, // Points to mm_struct equivalent
|
||||
pub files: Vec<u32>, // File descriptor table
|
||||
pub signal_pending: bool,
|
||||
pub exit_code: i32,
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub fn new(pid: Pid, name: String, uid: Uid, gid: Gid) -> Self {
|
||||
Self {
|
||||
pid,
|
||||
parent: None,
|
||||
state: ProcessState::Running,
|
||||
uid,
|
||||
gid,
|
||||
name,
|
||||
threads: Vec::new(),
|
||||
memory_map: None,
|
||||
files: Vec::new(),
|
||||
signal_pending: false,
|
||||
exit_code: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a thread to this process
|
||||
pub fn add_thread(&mut self, thread: Thread) {
|
||||
self.threads.push(thread);
|
||||
}
|
||||
|
||||
/// Get the main thread
|
||||
pub fn main_thread(&self) -> Option<&Thread> {
|
||||
self.threads.first()
|
||||
}
|
||||
|
||||
/// Set process state
|
||||
pub fn set_state(&mut self, state: ProcessState) {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
/// Check if process is running
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.state == ProcessState::Running
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread structure - Linux-compatible
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Thread {
|
||||
pub tid: Tid,
|
||||
pub process_pid: Pid,
|
||||
pub state: ProcessState,
|
||||
pub stack_pointer: VirtAddr,
|
||||
pub instruction_pointer: VirtAddr,
|
||||
pub priority: i32,
|
||||
pub nice: i32, // Nice value (-20 to 19)
|
||||
pub cpu_time: u64, // Nanoseconds
|
||||
pub context: ThreadContext,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
pub fn new(tid: Tid, process_pid: Pid, priority: i32) -> Self {
|
||||
Self {
|
||||
tid,
|
||||
process_pid,
|
||||
state: ProcessState::Running,
|
||||
stack_pointer: VirtAddr::new(0),
|
||||
instruction_pointer: VirtAddr::new(0),
|
||||
priority,
|
||||
nice: 0,
|
||||
cpu_time: 0,
|
||||
context: ThreadContext::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set thread state
|
||||
pub fn set_state(&mut self, state: ProcessState) {
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
/// Update CPU time
|
||||
pub fn add_cpu_time(&mut self, time: u64) {
|
||||
self.cpu_time += time;
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread context for context switching
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ThreadContext {
|
||||
// x86_64 registers
|
||||
pub rax: u64,
|
||||
pub rbx: u64,
|
||||
pub rcx: u64,
|
||||
pub rdx: u64,
|
||||
pub rsi: u64,
|
||||
pub rdi: u64,
|
||||
pub rbp: u64,
|
||||
pub rsp: u64,
|
||||
pub r8: u64,
|
||||
pub r9: u64,
|
||||
pub r10: u64,
|
||||
pub r11: u64,
|
||||
pub r12: u64,
|
||||
pub r13: u64,
|
||||
pub r14: u64,
|
||||
pub r15: u64,
|
||||
pub rip: u64,
|
||||
pub rflags: u64,
|
||||
// Segment registers
|
||||
pub cs: u16,
|
||||
pub ds: u16,
|
||||
pub es: u16,
|
||||
pub fs: u16,
|
||||
pub gs: u16,
|
||||
pub ss: u16,
|
||||
}
|
||||
|
||||
impl ThreadContext {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Global process table
|
||||
static PROCESS_TABLE: Spinlock<ProcessTable> = Spinlock::new(ProcessTable::new());
|
||||
static NEXT_PID: AtomicU32 = AtomicU32::new(1);
|
||||
static NEXT_TID: AtomicU32 = AtomicU32::new(1);
|
||||
|
||||
/// Process table implementation
|
||||
struct ProcessTable {
|
||||
processes: BTreeMap<Pid, Process>,
|
||||
current_process: Option<Pid>,
|
||||
}
|
||||
|
||||
impl ProcessTable {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
processes: BTreeMap::new(),
|
||||
current_process: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_process(&mut self, process: Process) {
|
||||
let pid = process.pid;
|
||||
self.processes.insert(pid, process);
|
||||
if self.current_process.is_none() {
|
||||
self.current_process = Some(pid);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_process(&self, pid: Pid) -> Option<&Process> {
|
||||
self.processes.get(&pid)
|
||||
}
|
||||
|
||||
fn get_process_mut(&mut self, pid: Pid) -> Option<&mut Process> {
|
||||
self.processes.get_mut(&pid)
|
||||
}
|
||||
|
||||
fn remove_process(&mut self, pid: Pid) -> Option<Process> {
|
||||
let process = self.processes.remove(&pid);
|
||||
if self.current_process == Some(pid) {
|
||||
self.current_process = self.processes.keys().next().copied();
|
||||
}
|
||||
process
|
||||
}
|
||||
|
||||
fn list_processes(&self) -> Vec<Pid> {
|
||||
self.processes.keys().copied().collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a new PID
|
||||
pub fn allocate_pid() -> Pid {
|
||||
Pid(NEXT_PID.fetch_add(1, Ordering::SeqCst))
|
||||
}
|
||||
|
||||
/// Allocate a new TID
|
||||
pub fn allocate_tid() -> Tid {
|
||||
Tid(NEXT_TID.fetch_add(1, Ordering::SeqCst))
|
||||
}
|
||||
|
||||
/// Create a new process
|
||||
pub fn create_process(name: String, uid: Uid, gid: Gid) -> Result<Pid> {
|
||||
let pid = allocate_pid();
|
||||
let mut process = Process::new(pid, name, uid, gid);
|
||||
|
||||
// Create main thread
|
||||
let tid = allocate_tid();
|
||||
let main_thread = Thread::new(tid, pid, 0);
|
||||
process.add_thread(main_thread);
|
||||
|
||||
let mut table = PROCESS_TABLE.lock();
|
||||
table.add_process(process);
|
||||
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
/// Get current process
|
||||
pub fn current_process() -> Option<Pid> {
|
||||
let table = PROCESS_TABLE.lock();
|
||||
table.current_process
|
||||
}
|
||||
|
||||
/// Get process by PID
|
||||
pub fn get_process(pid: Pid) -> Option<Process> {
|
||||
let table = PROCESS_TABLE.lock();
|
||||
table.get_process(pid).cloned()
|
||||
}
|
||||
|
||||
/// Kill a process
|
||||
pub fn kill_process(pid: Pid, signal: i32) -> Result<()> {
|
||||
let mut table = PROCESS_TABLE.lock();
|
||||
if let Some(process) = table.get_process_mut(pid) {
|
||||
process.set_state(ProcessState::Dead);
|
||||
process.exit_code = signal;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// List all processes
|
||||
pub fn list_processes() -> Vec<Pid> {
|
||||
let table = PROCESS_TABLE.lock();
|
||||
table.list_processes()
|
||||
}
|
||||
|
||||
/// Initialize the process subsystem
|
||||
pub fn init() -> Result<()> {
|
||||
// Create kernel process (PID 0)
|
||||
let _kernel_pid = create_process(
|
||||
String::from("kernel"),
|
||||
Uid(0),
|
||||
Gid(0)
|
||||
)?;
|
||||
|
||||
// Create init process (PID 1)
|
||||
let _init_pid = create_process(
|
||||
String::from("init"),
|
||||
Uid(0),
|
||||
Gid(0)
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
569
kernel/src/scheduler.rs
Archivo normal
569
kernel/src/scheduler.rs
Archivo normal
@@ -0,0 +1,569 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Task scheduler compatible with Linux kernel CFS (Completely Fair Scheduler)
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::process::{Process, Thread, ProcessState, ThreadContext};
|
||||
use crate::types::{Pid, Tid, Nanoseconds};
|
||||
use crate::sync::Spinlock;
|
||||
use crate::time;
|
||||
use alloc::{collections::{BTreeMap, VecDeque}, vec::Vec};
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// Scheduler policies - Linux compatible
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SchedulerPolicy {
|
||||
Normal = 0, // SCHED_NORMAL
|
||||
Fifo = 1, // SCHED_FIFO
|
||||
RoundRobin = 2, // SCHED_RR
|
||||
Batch = 3, // SCHED_BATCH
|
||||
Idle = 5, // SCHED_IDLE
|
||||
Deadline = 6, // SCHED_DEADLINE
|
||||
}
|
||||
|
||||
/// Scheduler priority levels
|
||||
pub const MAX_PRIO: i32 = 140;
|
||||
pub const MAX_USER_RT_PRIO: i32 = 100;
|
||||
pub const MAX_RT_PRIO: i32 = MAX_USER_RT_PRIO;
|
||||
pub const DEFAULT_PRIO: i32 = MAX_RT_PRIO + 20;
|
||||
pub const MIN_NICE: i32 = -20;
|
||||
pub const MAX_NICE: i32 = 19;
|
||||
|
||||
/// Convert nice value to priority
|
||||
pub fn nice_to_prio(nice: i32) -> i32 {
|
||||
DEFAULT_PRIO + nice
|
||||
}
|
||||
|
||||
/// Convert priority to nice value
|
||||
pub fn prio_to_nice(prio: i32) -> i32 {
|
||||
prio - DEFAULT_PRIO
|
||||
}
|
||||
|
||||
/// Scheduler entity - represents a schedulable unit
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SchedEntity {
|
||||
pub tid: Tid,
|
||||
pub policy: SchedulerPolicy,
|
||||
pub priority: i32,
|
||||
pub nice: i32,
|
||||
pub vruntime: u64, // Virtual runtime for CFS
|
||||
pub exec_start: u64, // Last execution start time
|
||||
pub sum_exec_runtime: u64, // Total execution time
|
||||
pub prev_sum_exec_runtime: u64,
|
||||
pub load_weight: u32, // Load weight for this entity
|
||||
pub runnable_weight: u32,
|
||||
pub on_rq: bool, // On run queue?
|
||||
}
|
||||
|
||||
impl SchedEntity {
|
||||
pub fn new(tid: Tid, policy: SchedulerPolicy, nice: i32) -> Self {
|
||||
let priority = nice_to_prio(nice);
|
||||
Self {
|
||||
tid,
|
||||
policy,
|
||||
priority,
|
||||
nice,
|
||||
vruntime: 0,
|
||||
exec_start: 0,
|
||||
sum_exec_runtime: 0,
|
||||
prev_sum_exec_runtime: 0,
|
||||
load_weight: nice_to_weight(nice),
|
||||
runnable_weight: nice_to_weight(nice),
|
||||
on_rq: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update virtual runtime
|
||||
pub fn update_vruntime(&mut self, delta: u64) {
|
||||
// Virtual runtime is weighted by load
|
||||
let weighted_delta = delta * 1024 / self.load_weight as u64;
|
||||
self.vruntime += weighted_delta;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert nice value to load weight (Linux compatible)
|
||||
fn nice_to_weight(nice: i32) -> u32 {
|
||||
// Linux nice-to-weight table (simplified)
|
||||
match nice {
|
||||
-20 => 88761,
|
||||
-19 => 71755,
|
||||
-18 => 56483,
|
||||
-17 => 46273,
|
||||
-16 => 36291,
|
||||
-15 => 29154,
|
||||
-14 => 23254,
|
||||
-13 => 18705,
|
||||
-12 => 14949,
|
||||
-11 => 11916,
|
||||
-10 => 9548,
|
||||
-9 => 7620,
|
||||
-8 => 6100,
|
||||
-7 => 4904,
|
||||
-6 => 3906,
|
||||
-5 => 3121,
|
||||
-4 => 2501,
|
||||
-3 => 1991,
|
||||
-2 => 1586,
|
||||
-1 => 1277,
|
||||
0 => 1024, // Default weight
|
||||
1 => 820,
|
||||
2 => 655,
|
||||
3 => 526,
|
||||
4 => 423,
|
||||
5 => 335,
|
||||
6 => 272,
|
||||
7 => 215,
|
||||
8 => 172,
|
||||
9 => 137,
|
||||
10 => 110,
|
||||
11 => 87,
|
||||
12 => 70,
|
||||
13 => 56,
|
||||
14 => 45,
|
||||
15 => 36,
|
||||
16 => 29,
|
||||
17 => 23,
|
||||
18 => 18,
|
||||
19 => 15,
|
||||
_ => 1024, // Default for out-of-range values
|
||||
}
|
||||
}
|
||||
|
||||
/// CFS (Completely Fair Scheduler) run queue
|
||||
#[derive(Debug)]
|
||||
pub struct CfsRunQueue {
|
||||
tasks_timeline: BTreeMap<u64, SchedEntity>, // Red-black tree equivalent
|
||||
min_vruntime: u64,
|
||||
nr_running: u32,
|
||||
load_weight: u64,
|
||||
runnable_weight: u64,
|
||||
}
|
||||
|
||||
impl CfsRunQueue {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tasks_timeline: BTreeMap::new(),
|
||||
min_vruntime: 0,
|
||||
nr_running: 0,
|
||||
load_weight: 0,
|
||||
runnable_weight: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add task to run queue
|
||||
pub fn enqueue_task(&mut self, mut se: SchedEntity) {
|
||||
// Place entity in timeline
|
||||
if !se.on_rq {
|
||||
se.on_rq = true;
|
||||
|
||||
// Ensure vruntime is not too far behind
|
||||
if se.vruntime < self.min_vruntime {
|
||||
se.vruntime = self.min_vruntime;
|
||||
}
|
||||
|
||||
self.tasks_timeline.insert(se.vruntime, se.clone());
|
||||
self.nr_running += 1;
|
||||
self.load_weight += se.load_weight as u64;
|
||||
self.runnable_weight += se.runnable_weight as u64;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove task from run queue
|
||||
pub fn dequeue_task(&mut self, se: &SchedEntity) -> bool {
|
||||
if se.on_rq {
|
||||
self.tasks_timeline.remove(&se.vruntime);
|
||||
self.nr_running -= 1;
|
||||
self.load_weight -= se.load_weight as u64;
|
||||
self.runnable_weight -= se.runnable_weight as u64;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Pick next task to run
|
||||
pub fn pick_next_task(&mut self) -> Option<SchedEntity> {
|
||||
// Pick leftmost task (smallest vruntime)
|
||||
if let Some((vruntime, se)) = self.tasks_timeline.iter().next() {
|
||||
let se = se.clone();
|
||||
let vruntime = *vruntime;
|
||||
self.tasks_timeline.remove(&vruntime);
|
||||
self.nr_running -= 1;
|
||||
self.load_weight -= se.load_weight as u64;
|
||||
self.runnable_weight -= se.runnable_weight as u64;
|
||||
|
||||
// Update min_vruntime
|
||||
if let Some((next_vruntime, _)) = self.tasks_timeline.iter().next() {
|
||||
self.min_vruntime = core::cmp::max(self.min_vruntime, *next_vruntime);
|
||||
} else {
|
||||
self.min_vruntime = se.vruntime;
|
||||
}
|
||||
|
||||
Some(se)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if run queue is empty
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.nr_running == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Real-time run queue (for FIFO/RR scheduling)
|
||||
#[derive(Debug)]
|
||||
pub struct RtRunQueue {
|
||||
active: [VecDeque<SchedEntity>; MAX_RT_PRIO as usize],
|
||||
rt_nr_running: u32,
|
||||
highest_prio: i32,
|
||||
}
|
||||
|
||||
impl RtRunQueue {
|
||||
pub fn new() -> Self {
|
||||
const EMPTY_QUEUE: VecDeque<SchedEntity> = VecDeque::new();
|
||||
Self {
|
||||
active: [EMPTY_QUEUE; MAX_RT_PRIO as usize],
|
||||
rt_nr_running: 0,
|
||||
highest_prio: MAX_RT_PRIO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enqueue_task(&mut self, se: SchedEntity) {
|
||||
let prio = se.priority as usize;
|
||||
if prio < MAX_RT_PRIO as usize {
|
||||
self.active[prio].push_back(se);
|
||||
self.rt_nr_running += 1;
|
||||
if (prio as i32) < self.highest_prio {
|
||||
self.highest_prio = prio as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dequeue_task(&mut self, se: &SchedEntity) -> bool {
|
||||
let prio = se.priority as usize;
|
||||
if prio < MAX_RT_PRIO as usize {
|
||||
if let Some(pos) = self.active[prio].iter().position(|x| x.tid == se.tid) {
|
||||
self.active[prio].remove(pos);
|
||||
self.rt_nr_running -= 1;
|
||||
|
||||
// Update highest_prio if this queue is now empty
|
||||
if self.active[prio].is_empty() && prio as i32 == self.highest_prio {
|
||||
self.update_highest_prio();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn pick_next_task(&mut self) -> Option<SchedEntity> {
|
||||
if self.rt_nr_running > 0 {
|
||||
for prio in self.highest_prio as usize..MAX_RT_PRIO as usize {
|
||||
if let Some(se) = self.active[prio].pop_front() {
|
||||
self.rt_nr_running -= 1;
|
||||
|
||||
// For round-robin, re-enqueue at the end
|
||||
if se.policy == SchedulerPolicy::RoundRobin {
|
||||
self.active[prio].push_back(se.clone());
|
||||
self.rt_nr_running += 1;
|
||||
}
|
||||
|
||||
if self.active[prio].is_empty() && prio as i32 == self.highest_prio {
|
||||
self.update_highest_prio();
|
||||
}
|
||||
|
||||
return Some(se);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn update_highest_prio(&mut self) {
|
||||
self.highest_prio = MAX_RT_PRIO;
|
||||
for prio in 0..MAX_RT_PRIO as usize {
|
||||
if !self.active[prio].is_empty() {
|
||||
self.highest_prio = prio as i32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.rt_nr_running == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-CPU run queue
|
||||
#[derive(Debug)]
|
||||
pub struct RunQueue {
|
||||
pub cpu: u32,
|
||||
pub nr_running: u32,
|
||||
pub current: Option<SchedEntity>,
|
||||
pub cfs: CfsRunQueue,
|
||||
pub rt: RtRunQueue,
|
||||
pub idle_task: Option<SchedEntity>,
|
||||
pub clock: u64,
|
||||
pub clock_task: u64,
|
||||
}
|
||||
|
||||
impl RunQueue {
|
||||
pub fn new(cpu: u32) -> Self {
|
||||
Self {
|
||||
cpu,
|
||||
nr_running: 0,
|
||||
current: None,
|
||||
cfs: CfsRunQueue::new(),
|
||||
rt: RtRunQueue::new(),
|
||||
idle_task: None,
|
||||
clock: 0,
|
||||
clock_task: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update run queue clock
|
||||
pub fn update_rq_clock(&mut self) {
|
||||
self.clock = time::get_time_ns();
|
||||
self.clock_task = self.clock;
|
||||
}
|
||||
|
||||
/// Enqueue a task
|
||||
pub fn enqueue_task(&mut self, se: SchedEntity) {
|
||||
match se.policy {
|
||||
SchedulerPolicy::Normal | SchedulerPolicy::Batch | SchedulerPolicy::Idle => {
|
||||
self.cfs.enqueue_task(se);
|
||||
}
|
||||
SchedulerPolicy::Fifo | SchedulerPolicy::RoundRobin => {
|
||||
self.rt.enqueue_task(se);
|
||||
}
|
||||
SchedulerPolicy::Deadline => {
|
||||
// TODO: implement deadline scheduler
|
||||
self.cfs.enqueue_task(se);
|
||||
}
|
||||
}
|
||||
self.nr_running += 1;
|
||||
}
|
||||
|
||||
/// Dequeue a task
|
||||
pub fn dequeue_task(&mut self, se: &SchedEntity) -> bool {
|
||||
let result = match se.policy {
|
||||
SchedulerPolicy::Normal | SchedulerPolicy::Batch | SchedulerPolicy::Idle => {
|
||||
self.cfs.dequeue_task(se)
|
||||
}
|
||||
SchedulerPolicy::Fifo | SchedulerPolicy::RoundRobin => {
|
||||
self.rt.dequeue_task(se)
|
||||
}
|
||||
SchedulerPolicy::Deadline => {
|
||||
self.cfs.dequeue_task(se)
|
||||
}
|
||||
};
|
||||
|
||||
if result {
|
||||
self.nr_running -= 1;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Pick next task to run
|
||||
pub fn pick_next_task(&mut self) -> Option<SchedEntity> {
|
||||
// Real-time tasks have priority
|
||||
if let Some(se) = self.rt.pick_next_task() {
|
||||
return Some(se);
|
||||
}
|
||||
|
||||
// Then CFS tasks
|
||||
if let Some(se) = self.cfs.pick_next_task() {
|
||||
return Some(se);
|
||||
}
|
||||
|
||||
// Finally, idle task
|
||||
self.idle_task.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Global scheduler state
|
||||
static SCHEDULER: Spinlock<Scheduler> = Spinlock::new(Scheduler::new());
|
||||
static SCHEDULE_CLOCK: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
/// Main scheduler structure
|
||||
struct Scheduler {
|
||||
run_queues: Vec<RunQueue>,
|
||||
nr_cpus: u32,
|
||||
entities: BTreeMap<Tid, SchedEntity>,
|
||||
need_resched: bool,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
run_queues: Vec::new(),
|
||||
nr_cpus: 1, // Single CPU for now
|
||||
entities: BTreeMap::new(),
|
||||
need_resched: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&mut self) -> Result<()> {
|
||||
// Create run queues for each CPU
|
||||
for cpu in 0..self.nr_cpus {
|
||||
let mut rq = RunQueue::new(cpu);
|
||||
|
||||
// Create idle task for this CPU
|
||||
let idle_se = SchedEntity::new(
|
||||
crate::process::allocate_tid(),
|
||||
SchedulerPolicy::Idle,
|
||||
MAX_NICE
|
||||
);
|
||||
rq.idle_task = Some(idle_se);
|
||||
|
||||
self.run_queues.push(rq);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_task(&mut self, tid: Tid, policy: SchedulerPolicy, nice: i32) {
|
||||
let se = SchedEntity::new(tid, policy, nice);
|
||||
self.entities.insert(tid, se.clone());
|
||||
|
||||
// Add to CPU 0's run queue for simplicity
|
||||
if let Some(rq) = self.run_queues.get_mut(0) {
|
||||
rq.enqueue_task(se);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_task(&mut self, tid: Tid) {
|
||||
if let Some(se) = self.entities.remove(&tid) {
|
||||
// Remove from all run queues
|
||||
for rq in &mut self.run_queues {
|
||||
rq.dequeue_task(&se);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule(&mut self) -> Option<Tid> {
|
||||
// Simple single-CPU scheduling for now
|
||||
if let Some(rq) = self.run_queues.get_mut(0) {
|
||||
rq.update_rq_clock();
|
||||
|
||||
if let Some(se) = rq.pick_next_task() {
|
||||
rq.current = Some(se.clone());
|
||||
return Some(se.tid);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn set_need_resched(&mut self) {
|
||||
self.need_resched = true;
|
||||
}
|
||||
|
||||
fn clear_need_resched(&mut self) {
|
||||
self.need_resched = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the scheduler
|
||||
pub fn init() -> Result<()> {
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
scheduler.init()?;
|
||||
|
||||
crate::info!("Scheduler initialized with {} CPUs", scheduler.nr_cpus);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a task to the scheduler
|
||||
pub fn add_task(tid: Tid, policy: SchedulerPolicy, nice: i32) {
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
scheduler.add_task(tid, policy, nice);
|
||||
}
|
||||
|
||||
/// Remove a task from the scheduler
|
||||
pub fn remove_task(tid: Tid) {
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
scheduler.remove_task(tid);
|
||||
}
|
||||
|
||||
/// Schedule the next task
|
||||
pub fn schedule() -> Option<Tid> {
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let result = scheduler.schedule();
|
||||
scheduler.clear_need_resched();
|
||||
result
|
||||
}
|
||||
|
||||
/// Yield the current thread
|
||||
pub fn yield_now() {
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
scheduler.set_need_resched();
|
||||
// In a real implementation, this would trigger a context switch
|
||||
}
|
||||
|
||||
/// Sleep for a given number of milliseconds
|
||||
pub fn sleep_ms(ms: u64) {
|
||||
// TODO: implement proper sleep mechanism with timer integration
|
||||
// For now, just yield
|
||||
yield_now();
|
||||
}
|
||||
|
||||
/// Set scheduler policy for a task
|
||||
pub fn set_scheduler_policy(tid: Tid, policy: SchedulerPolicy, nice: i32) -> Result<()> {
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
|
||||
if let Some(se) = scheduler.entities.get_mut(&tid) {
|
||||
se.policy = policy;
|
||||
se.nice = nice;
|
||||
se.priority = nice_to_prio(nice);
|
||||
se.load_weight = nice_to_weight(nice);
|
||||
se.runnable_weight = nice_to_weight(nice);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get scheduler statistics
|
||||
pub fn get_scheduler_stats() -> (u32, u32, bool) {
|
||||
let scheduler = SCHEDULER.lock();
|
||||
let total_tasks = scheduler.entities.len() as u32;
|
||||
let running_tasks = scheduler.run_queues.iter().map(|rq| rq.nr_running).sum();
|
||||
(total_tasks, running_tasks, scheduler.need_resched)
|
||||
}
|
||||
|
||||
/// Timer tick - called from timer interrupt
|
||||
pub fn scheduler_tick() {
|
||||
SCHEDULE_CLOCK.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
|
||||
// Update current task's runtime
|
||||
if let Some(rq) = scheduler.run_queues.get_mut(0) {
|
||||
if let Some(ref mut current) = rq.current {
|
||||
let now = rq.clock;
|
||||
let delta = now - current.exec_start;
|
||||
current.sum_exec_runtime += delta;
|
||||
current.update_vruntime(delta);
|
||||
current.exec_start = now;
|
||||
|
||||
// Check if we need to reschedule
|
||||
// For CFS, check if current task has run long enough
|
||||
if current.policy == SchedulerPolicy::Normal {
|
||||
let time_slice = calculate_time_slice(current);
|
||||
if current.sum_exec_runtime - current.prev_sum_exec_runtime >= time_slice {
|
||||
scheduler.set_need_resched();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate time slice for a task based on its weight
|
||||
fn calculate_time_slice(se: &SchedEntity) -> u64 {
|
||||
// Linux-like time slice calculation
|
||||
let sched_latency = 6_000_000; // 6ms in nanoseconds
|
||||
let min_granularity = 750_000; // 0.75ms in nanoseconds
|
||||
|
||||
// Time slice proportional to weight
|
||||
let time_slice = sched_latency * se.load_weight as u64 / 1024;
|
||||
core::cmp::max(time_slice, min_granularity)
|
||||
}
|
||||
160
kernel/src/sync.rs
Archivo normal
160
kernel/src/sync.rs
Archivo normal
@@ -0,0 +1,160 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Synchronization primitives
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
/// Spinlock implementation
|
||||
pub struct Spinlock<T> {
|
||||
locked: AtomicBool,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Sync for Spinlock<T> {}
|
||||
unsafe impl<T: Send> Send for Spinlock<T> {}
|
||||
|
||||
impl<T> Spinlock<T> {
|
||||
pub const fn new(data: T) -> Self {
|
||||
Self {
|
||||
locked: AtomicBool::new(false),
|
||||
data: UnsafeCell::new(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> SpinlockGuard<T> {
|
||||
while self.locked.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
|
||||
// Busy wait
|
||||
while self.locked.load(Ordering::Relaxed) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
SpinlockGuard { lock: self }
|
||||
}
|
||||
|
||||
pub fn try_lock(&self) -> Option<SpinlockGuard<T>> {
|
||||
if self.locked.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok() {
|
||||
Some(SpinlockGuard { lock: self })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SpinlockGuard<'a, T> {
|
||||
lock: &'a Spinlock<T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for SpinlockGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for SpinlockGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for SpinlockGuard<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.lock.locked.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutex implementation (placeholder - would need proper scheduler integration)
|
||||
pub struct Mutex<T> {
|
||||
inner: Spinlock<T>,
|
||||
}
|
||||
|
||||
impl<T> Mutex<T> {
|
||||
pub const fn new(data: T) -> Self {
|
||||
Self {
|
||||
inner: Spinlock::new(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> MutexGuard<T> {
|
||||
MutexGuard {
|
||||
guard: self.inner.lock(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MutexGuard<'a, T> {
|
||||
guard: SpinlockGuard<'a, T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for MutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.guard
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for MutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.guard
|
||||
}
|
||||
}
|
||||
|
||||
/// RwLock implementation (placeholder)
|
||||
pub struct RwLock<T> {
|
||||
inner: Spinlock<T>,
|
||||
}
|
||||
|
||||
impl<T> RwLock<T> {
|
||||
pub const fn new(data: T) -> Self {
|
||||
Self {
|
||||
inner: Spinlock::new(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self) -> RwLockReadGuard<T> {
|
||||
RwLockReadGuard {
|
||||
guard: self.inner.lock(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self) -> RwLockWriteGuard<T> {
|
||||
RwLockWriteGuard {
|
||||
guard: self.inner.lock(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RwLockReadGuard<'a, T> {
|
||||
guard: SpinlockGuard<'a, T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for RwLockReadGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.guard
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RwLockWriteGuard<'a, T> {
|
||||
guard: SpinlockGuard<'a, T>,
|
||||
}
|
||||
|
||||
impl<T> Deref for RwLockWriteGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&*self.guard
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for RwLockWriteGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut *self.guard
|
||||
}
|
||||
}
|
||||
8
kernel/src/syscall.rs
Archivo normal
8
kernel/src/syscall.rs
Archivo normal
@@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! System call interface
|
||||
use crate::error::Result;
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
10
kernel/src/task.rs
Archivo normal
10
kernel/src/task.rs
Archivo normal
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Task management
|
||||
use crate::error::Result;
|
||||
|
||||
pub struct Task;
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
293
kernel/src/time.rs
Archivo normal
293
kernel/src/time.rs
Archivo normal
@@ -0,0 +1,293 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Time management compatible with Linux kernel
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::types::{Nanoseconds, Microseconds, Milliseconds, Seconds, Jiffies};
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// System clock frequency (Hz) - typically 1000 for 1ms ticks
|
||||
pub const HZ: u64 = 1000;
|
||||
|
||||
/// Nanoseconds per second
|
||||
pub const NSEC_PER_SEC: u64 = 1_000_000_000;
|
||||
|
||||
/// Nanoseconds per millisecond
|
||||
pub const NSEC_PER_MSEC: u64 = 1_000_000;
|
||||
|
||||
/// Nanoseconds per microsecond
|
||||
pub const NSEC_PER_USEC: u64 = 1_000;
|
||||
|
||||
/// Nanoseconds per jiffy
|
||||
pub const NSEC_PER_JIFFY: u64 = NSEC_PER_SEC / HZ;
|
||||
|
||||
/// Global time counters
|
||||
static JIFFIES_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
static BOOTTIME_NS: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
/// Time structure - Linux compatible
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct TimeSpec {
|
||||
pub sec: i64,
|
||||
pub nsec: i64,
|
||||
}
|
||||
|
||||
impl TimeSpec {
|
||||
pub const fn new(sec: i64, nsec: i64) -> Self {
|
||||
Self { sec, nsec }
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self::new(0, 0)
|
||||
}
|
||||
|
||||
pub fn to_ns(&self) -> u64 {
|
||||
(self.sec as u64 * NSEC_PER_SEC) + self.nsec as u64
|
||||
}
|
||||
|
||||
pub fn from_ns(ns: u64) -> Self {
|
||||
let sec = (ns / NSEC_PER_SEC) as i64;
|
||||
let nsec = (ns % NSEC_PER_SEC) as i64;
|
||||
Self::new(sec, nsec)
|
||||
}
|
||||
|
||||
pub fn add_ns(&mut self, ns: u64) {
|
||||
let total_ns = self.to_ns() + ns;
|
||||
*self = Self::from_ns(total_ns);
|
||||
}
|
||||
|
||||
pub fn sub_ns(&mut self, ns: u64) {
|
||||
let total_ns = self.to_ns().saturating_sub(ns);
|
||||
*self = Self::from_ns(total_ns);
|
||||
}
|
||||
}
|
||||
|
||||
/// High resolution timer structure
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HrTimer {
|
||||
pub expires: TimeSpec,
|
||||
pub function: Option<fn()>,
|
||||
pub base: HrTimerBase,
|
||||
}
|
||||
|
||||
/// Timer bases - Linux compatible
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HrTimerBase {
|
||||
Monotonic,
|
||||
Realtime,
|
||||
Boottime,
|
||||
Tai,
|
||||
}
|
||||
|
||||
impl HrTimer {
|
||||
pub fn new(base: HrTimerBase) -> Self {
|
||||
Self {
|
||||
expires: TimeSpec::zero(),
|
||||
function: None,
|
||||
base,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_expires(&mut self, expires: TimeSpec) {
|
||||
self.expires = expires;
|
||||
}
|
||||
|
||||
pub fn set_function(&mut self, function: fn()) {
|
||||
self.function = Some(function);
|
||||
}
|
||||
|
||||
pub fn is_expired(&self) -> bool {
|
||||
let now = match self.base {
|
||||
HrTimerBase::Monotonic => get_monotonic_time(),
|
||||
HrTimerBase::Realtime => get_realtime(),
|
||||
HrTimerBase::Boottime => get_boottime(),
|
||||
HrTimerBase::Tai => get_realtime(), // Simplified
|
||||
};
|
||||
now >= self.expires
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize time management
|
||||
pub fn init() -> Result<()> {
|
||||
// Initialize system clocks
|
||||
let boot_time = read_hardware_clock();
|
||||
BOOTTIME_NS.store(boot_time, Ordering::Relaxed);
|
||||
|
||||
// TODO: Set up timer interrupts
|
||||
// TODO: Initialize high-resolution timers
|
||||
|
||||
crate::info!("Time management initialized, boot time: {} ns", boot_time);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read hardware clock (placeholder)
|
||||
fn read_hardware_clock() -> u64 {
|
||||
// TODO: Read from actual hardware clock (RTC, TSC, etc.)
|
||||
// For now, return a fixed value
|
||||
1609459200_000_000_000 // 2021-01-01 00:00:00 UTC in nanoseconds
|
||||
}
|
||||
|
||||
/// Get current jiffies count
|
||||
pub fn get_jiffies() -> Jiffies {
|
||||
Jiffies(JIFFIES_COUNTER.load(Ordering::Relaxed))
|
||||
}
|
||||
|
||||
/// Increment jiffies counter (called from timer interrupt)
|
||||
pub fn update_jiffies() {
|
||||
JIFFIES_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Get current time in nanoseconds since boot
|
||||
pub fn get_time_ns() -> u64 {
|
||||
// TODO: Read from high-resolution clock source (TSC, etc.)
|
||||
// For now, use jiffies-based approximation
|
||||
get_jiffies().0 * NSEC_PER_JIFFY
|
||||
}
|
||||
|
||||
/// Get monotonic time (time since boot)
|
||||
pub fn get_monotonic_time() -> TimeSpec {
|
||||
TimeSpec::from_ns(get_time_ns())
|
||||
}
|
||||
|
||||
/// Get boot time
|
||||
pub fn get_boottime() -> TimeSpec {
|
||||
let boot_ns = BOOTTIME_NS.load(Ordering::Relaxed);
|
||||
let current_ns = get_time_ns();
|
||||
TimeSpec::from_ns(boot_ns + current_ns)
|
||||
}
|
||||
|
||||
/// Get real time (wall clock time)
|
||||
pub fn get_realtime() -> TimeSpec {
|
||||
get_boottime()
|
||||
}
|
||||
|
||||
/// Convert nanoseconds to jiffies
|
||||
pub fn ns_to_jiffies(ns: u64) -> Jiffies {
|
||||
Jiffies(ns / NSEC_PER_JIFFY)
|
||||
}
|
||||
|
||||
/// Convert jiffies to nanoseconds
|
||||
pub fn jiffies_to_ns(jiffies: Jiffies) -> u64 {
|
||||
jiffies.0 * NSEC_PER_JIFFY
|
||||
}
|
||||
|
||||
/// Convert milliseconds to jiffies
|
||||
pub fn msecs_to_jiffies(ms: u64) -> Jiffies {
|
||||
ns_to_jiffies(ms * NSEC_PER_MSEC)
|
||||
}
|
||||
|
||||
/// Convert jiffies to milliseconds
|
||||
pub fn jiffies_to_msecs(jiffies: Jiffies) -> u64 {
|
||||
jiffies_to_ns(jiffies) / NSEC_PER_MSEC
|
||||
}
|
||||
|
||||
/// Convert microseconds to jiffies
|
||||
pub fn usecs_to_jiffies(us: u64) -> Jiffies {
|
||||
ns_to_jiffies(us * NSEC_PER_USEC)
|
||||
}
|
||||
|
||||
/// Convert jiffies to microseconds
|
||||
pub fn jiffies_to_usecs(jiffies: Jiffies) -> u64 {
|
||||
jiffies_to_ns(jiffies) / NSEC_PER_USEC
|
||||
}
|
||||
|
||||
/// Sleep functions - Linux compatible
|
||||
pub fn msleep(ms: u64) {
|
||||
let target_jiffies = get_jiffies().0 + msecs_to_jiffies(ms).0;
|
||||
while get_jiffies().0 < target_jiffies {
|
||||
crate::scheduler::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn usleep_range(min_us: u64, max_us: u64) {
|
||||
let us = (min_us + max_us) / 2; // Use average
|
||||
let target_jiffies = get_jiffies().0 + usecs_to_jiffies(us).0;
|
||||
while get_jiffies().0 < target_jiffies {
|
||||
crate::scheduler::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ndelay(ns: u64) {
|
||||
// Busy wait for nanoseconds (not recommended for long delays)
|
||||
let start = get_time_ns();
|
||||
while get_time_ns() - start < ns {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn udelay(us: u64) {
|
||||
ndelay(us * NSEC_PER_USEC);
|
||||
}
|
||||
|
||||
pub fn mdelay(ms: u64) {
|
||||
ndelay(ms * NSEC_PER_MSEC);
|
||||
}
|
||||
|
||||
/// Timer wheel for managing timers
|
||||
#[derive(Debug)]
|
||||
pub struct TimerWheel {
|
||||
levels: [Vec<HrTimer>; 8], // Multiple levels for different time ranges
|
||||
current_jiffies: u64,
|
||||
}
|
||||
|
||||
impl TimerWheel {
|
||||
pub fn new() -> Self {
|
||||
const EMPTY_VEC: Vec<HrTimer> = Vec::new();
|
||||
Self {
|
||||
levels: [EMPTY_VEC; 8],
|
||||
current_jiffies: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_timer(&mut self, timer: HrTimer) {
|
||||
// TODO: Add timer to appropriate level based on expiry time
|
||||
let level = 0; // Simplified
|
||||
self.levels[level].push(timer);
|
||||
}
|
||||
|
||||
pub fn run_timers(&mut self) {
|
||||
self.current_jiffies = get_jiffies().0;
|
||||
|
||||
// Check and run expired timers
|
||||
for level in &mut self.levels {
|
||||
level.retain(|timer| {
|
||||
if timer.is_expired() {
|
||||
if let Some(function) = timer.function {
|
||||
function();
|
||||
}
|
||||
false // Remove expired timer
|
||||
} else {
|
||||
true // Keep timer
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global timer wheel
|
||||
use crate::sync::Spinlock;
|
||||
static TIMER_WHEEL: Spinlock<TimerWheel> = Spinlock::new(TimerWheel::new());
|
||||
|
||||
/// Add a timer to the system
|
||||
pub fn add_timer(timer: HrTimer) {
|
||||
let mut wheel = TIMER_WHEEL.lock();
|
||||
wheel.add_timer(timer);
|
||||
}
|
||||
|
||||
/// Run expired timers (called from timer interrupt)
|
||||
pub fn run_timers() {
|
||||
let mut wheel = TIMER_WHEEL.lock();
|
||||
wheel.run_timers();
|
||||
}
|
||||
|
||||
/// Timer interrupt handler
|
||||
pub fn timer_interrupt() {
|
||||
// Update jiffies
|
||||
update_jiffies();
|
||||
|
||||
// Run expired timers
|
||||
run_timers();
|
||||
|
||||
// Update scheduler tick
|
||||
crate::scheduler::scheduler_tick();
|
||||
}
|
||||
158
kernel/src/types.rs
Archivo normal
158
kernel/src/types.rs
Archivo normal
@@ -0,0 +1,158 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Common kernel types
|
||||
|
||||
use core::fmt;
|
||||
use core::ops::{Add, Sub};
|
||||
|
||||
/// Process ID type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Pid(pub u32);
|
||||
|
||||
impl fmt::Display for Pid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread ID type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Tid(pub u32);
|
||||
|
||||
impl fmt::Display for Tid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// User ID type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Uid(pub u32);
|
||||
|
||||
/// Group ID type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Gid(pub u32);
|
||||
|
||||
/// Physical address type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct PhysAddr(pub usize);
|
||||
|
||||
impl PhysAddr {
|
||||
pub const fn new(addr: usize) -> Self {
|
||||
Self(addr)
|
||||
}
|
||||
|
||||
pub const fn as_u64(self) -> u64 {
|
||||
self.0 as u64
|
||||
}
|
||||
|
||||
pub const fn as_usize(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for PhysAddr {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
Self(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<usize> for PhysAddr {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: usize) -> Self::Output {
|
||||
Self(self.0 - rhs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtual address type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct VirtAddr(pub usize);
|
||||
|
||||
impl VirtAddr {
|
||||
pub const fn new(addr: usize) -> Self {
|
||||
Self(addr)
|
||||
}
|
||||
|
||||
pub const fn as_u64(self) -> u64 {
|
||||
self.0 as u64
|
||||
}
|
||||
|
||||
pub const fn as_usize(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub const fn as_ptr<T>(self) -> *const T {
|
||||
self.0 as *const T
|
||||
}
|
||||
|
||||
pub const fn as_mut_ptr<T>(self) -> *mut T {
|
||||
self.0 as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for VirtAddr {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
Self(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<usize> for VirtAddr {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: usize) -> Self::Output {
|
||||
Self(self.0 - rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VirtAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "0x{:x}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Page size constants
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
pub const PAGE_SHIFT: usize = 12;
|
||||
|
||||
/// Page frame number
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Pfn(pub usize);
|
||||
|
||||
impl Pfn {
|
||||
pub fn from_phys_addr(addr: PhysAddr) -> Self {
|
||||
Self(addr.0 >> PAGE_SHIFT)
|
||||
}
|
||||
|
||||
pub fn to_phys_addr(self) -> PhysAddr {
|
||||
PhysAddr(self.0 << PAGE_SHIFT)
|
||||
}
|
||||
}
|
||||
|
||||
/// CPU number type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct CpuId(pub u32);
|
||||
|
||||
/// IRQ number type
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Irq(pub u32);
|
||||
|
||||
/// Time types
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Jiffies(pub u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Nanoseconds(pub u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Microseconds(pub u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Milliseconds(pub u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Seconds(pub u64);
|
||||
18
modules/Cargo.toml
Archivo normal
18
modules/Cargo.toml
Archivo normal
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "modules"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Rust Kernel Contributors"]
|
||||
description = "Kernel modules"
|
||||
license = "GPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
kernel = { path = "../kernel" }
|
||||
|
||||
[[bin]]
|
||||
name = "hello_module"
|
||||
path = "src/hello.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "test_module"
|
||||
path = "src/test.rs"
|
||||
35
modules/src/hello.rs
Archivo normal
35
modules/src/hello.rs
Archivo normal
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Hello World kernel module
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use kernel::prelude::*;
|
||||
|
||||
struct HelloModule {
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl kernel::module::Module for HelloModule {
|
||||
fn init(_module: &'static kernel::module::ThisModule) -> Result<Self> {
|
||||
info!("Hello from Rust kernel module!");
|
||||
info!("This is a sample module demonstrating the Rust kernel framework");
|
||||
|
||||
Ok(HelloModule {
|
||||
message: String::from("Hello, Rust Kernel!"),
|
||||
})
|
||||
}
|
||||
|
||||
fn exit(_module: &'static kernel::module::ThisModule) {
|
||||
info!("Goodbye from Hello module");
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: HelloModule,
|
||||
name: "hello_module",
|
||||
author: "Rust Kernel Contributors",
|
||||
description: "A simple hello world module",
|
||||
license: "GPL-2.0",
|
||||
}
|
||||
53
modules/src/test.rs
Archivo normal
53
modules/src/test.rs
Archivo normal
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Test kernel module
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use kernel::prelude::*;
|
||||
|
||||
struct TestModule {
|
||||
test_data: Vec<u32>,
|
||||
counter: u32,
|
||||
}
|
||||
|
||||
impl kernel::module::Module for TestModule {
|
||||
fn init(_module: &'static kernel::module::ThisModule) -> Result<Self> {
|
||||
info!("Test module initializing...");
|
||||
|
||||
// Test memory allocation
|
||||
let mut test_data = Vec::new();
|
||||
for i in 0..10 {
|
||||
test_data.push(i * i);
|
||||
}
|
||||
|
||||
info!("Test data created: {:?}", test_data);
|
||||
|
||||
// Test synchronization
|
||||
let spinlock = Spinlock::new(42);
|
||||
{
|
||||
let guard = spinlock.lock();
|
||||
info!("Locked value: {}", *guard);
|
||||
}
|
||||
|
||||
info!("Test module initialized successfully");
|
||||
|
||||
Ok(TestModule {
|
||||
test_data,
|
||||
counter: 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn exit(_module: &'static kernel::module::ThisModule) {
|
||||
info!("Test module exiting, counter was: {}", self.counter);
|
||||
}
|
||||
}
|
||||
|
||||
module! {
|
||||
type: TestModule,
|
||||
name: "test_module",
|
||||
author: "Rust Kernel Contributors",
|
||||
description: "A test module for kernel functionality",
|
||||
license: "GPL-2.0",
|
||||
}
|
||||
30
rustfmt.toml
Archivo normal
30
rustfmt.toml
Archivo normal
@@ -0,0 +1,30 @@
|
||||
# Rust formatting configuration
|
||||
# Based on Linux kernel Rust formatting rules
|
||||
|
||||
max_width = 100
|
||||
hard_tabs = true
|
||||
tab_spaces = 8
|
||||
newline_style = "Unix"
|
||||
use_small_heuristics = "Default"
|
||||
|
||||
# Import organization
|
||||
imports_layout = "Mixed"
|
||||
group_imports = "StdExternalCrate"
|
||||
|
||||
# Function formatting
|
||||
fn_args_layout = "Tall"
|
||||
where_single_line = true
|
||||
|
||||
# Control flow
|
||||
control_brace_style = "AlwaysSameLine"
|
||||
indent_style = "Block"
|
||||
|
||||
# Comments
|
||||
comment_width = 80
|
||||
wrap_comments = true
|
||||
normalize_comments = true
|
||||
|
||||
# Misc
|
||||
format_code_in_doc_comments = true
|
||||
format_strings = false
|
||||
format_macro_matchers = true
|
||||
26
src/lib.rs
Archivo normal
26
src/lib.rs
Archivo normal
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Rust Kernel
|
||||
//!
|
||||
//! A modern kernel implementation in Rust, inspired by the Linux kernel
|
||||
//! and utilizing the Rust for Linux infrastructure.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
// Re-export the kernel crate
|
||||
pub use kernel;
|
||||
|
||||
/// Main kernel entry point
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _start() -> ! {
|
||||
kernel::kernel_main()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn basic_test() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
}
|
||||
}
|
||||
42
test.sh
Archivo ejecutable
42
test.sh
Archivo ejecutable
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Rust Kernel Test Script
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Rust Kernel Build Test ==="
|
||||
|
||||
# Check if rust is installed
|
||||
if ! command -v cargo &> /dev/null; then
|
||||
echo "Error: Rust/Cargo not found. Please install Rust first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Rust version:"
|
||||
rustc --version
|
||||
cargo --version
|
||||
|
||||
echo ""
|
||||
echo "=== Building kernel ==="
|
||||
cargo check --workspace
|
||||
|
||||
echo ""
|
||||
echo "=== Running tests ==="
|
||||
cargo test --workspace --lib
|
||||
|
||||
echo ""
|
||||
echo "=== Checking formatting ==="
|
||||
cargo fmt --check || echo "Warning: Code formatting issues found"
|
||||
|
||||
echo ""
|
||||
echo "=== Running clippy ==="
|
||||
cargo clippy --workspace -- -D warnings || echo "Warning: Clippy found issues"
|
||||
|
||||
echo ""
|
||||
echo "=== Building release version ==="
|
||||
cargo build --release
|
||||
|
||||
echo ""
|
||||
echo "=== Build completed successfully! ==="
|
||||
echo "Kernel artifacts can be found in target/release/"
|
||||
Referencia en una nueva incidencia
Block a user