initial commit

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-06-15 22:26:49 +02:00
commit 7409cd4d26
Se han modificado 60 ficheros con 9060 adiciones y 0 borrados

27
.gitignore vendido Archivo normal
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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;

Ver fichero

@@ -0,0 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
//! GDT stub
pub fn init() {}

Ver fichero

@@ -0,0 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
//! IDT stub
pub fn init() {}

Ver fichero

@@ -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;

Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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(&current_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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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
Ver fichero

@@ -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/"