Comparar commits
7 Commits
72e5221588
...
master
| Autor | SHA1 | Fecha | |
|---|---|---|---|
| 52ddc25097 | |||
|
|
932310d08c | ||
|
|
3fd11dae22 | ||
|
|
c4019d7f7f | ||
|
|
9d37968084 | ||
|
f9be9904fc
|
|||
| 315b8aa0f5 |
14
.cargo/config.toml
Archivo normal
14
.cargo/config.toml
Archivo normal
@@ -0,0 +1,14 @@
|
||||
[build]
|
||||
target = "x86_64-unknown-none"
|
||||
|
||||
[target.x86_64-unknown-none]
|
||||
runner = "qemu-system-x86_64 -kernel"
|
||||
rustflags = [
|
||||
"-C", "relocation-model=static",
|
||||
"-C", "link-arg=-Tkernel/linker.ld",
|
||||
"-C", "link-arg=-no-pie"
|
||||
]
|
||||
|
||||
[unstable]
|
||||
build-std = ["core", "alloc"]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
||||
45
Makefile
45
Makefile
@@ -9,6 +9,9 @@ RUST_KERNEL_VERSION := 0.1.0
|
||||
ARCH ?= x86_64
|
||||
BUILD_TYPE ?= release
|
||||
|
||||
# Enable unstable features on stable compiler
|
||||
export RUSTC_BOOTSTRAP := 1
|
||||
|
||||
# Cargo configuration
|
||||
CARGO := cargo
|
||||
CARGO_FLAGS := --target-dir target
|
||||
@@ -19,17 +22,40 @@ else
|
||||
CARGO_FLAGS += --release
|
||||
endif
|
||||
|
||||
# Kernel build command with proper flags
|
||||
KERNEL_BUILD_CMD := cd kernel && $(CARGO) build $(CARGO_FLAGS) \
|
||||
--target x86_64-unknown-none \
|
||||
-Z build-std=core,alloc \
|
||||
-Z build-std-features=compiler-builtins-mem
|
||||
|
||||
# Kernel modules
|
||||
RUST_MODULES := $(shell find modules -name "*.rs" -type f)
|
||||
DRIVERS := $(shell find drivers -name "*.rs" -type f)
|
||||
|
||||
# Binary locations
|
||||
KERNEL_BIN := kernel/target/x86_64-unknown-none/$(BUILD_TYPE)/rust-kernel
|
||||
ISO_BOOT := iso/boot/rust-kernel
|
||||
|
||||
# Default target
|
||||
all: kernel modules drivers
|
||||
all: kernel iso
|
||||
|
||||
# Build the core kernel
|
||||
kernel:
|
||||
@echo "Building Rust kernel ($(ARCH), $(BUILD_TYPE))"
|
||||
$(CARGO) build $(CARGO_FLAGS)
|
||||
$(KERNEL_BUILD_CMD)
|
||||
@echo "Kernel binary: $(KERNEL_BIN)"
|
||||
|
||||
# Create bootable ISO
|
||||
iso: kernel
|
||||
@echo "Creating bootable ISO..."
|
||||
@mkdir -p iso/boot/grub
|
||||
@cp $(KERNEL_BIN) $(ISO_BOOT)
|
||||
@if command -v grub-mkrescue >/dev/null 2>&1; then \
|
||||
grub-mkrescue -o rust-kernel.iso iso && \
|
||||
echo "ISO created: rust-kernel.iso"; \
|
||||
else \
|
||||
echo "Warning: grub-mkrescue not found. Install grub-common and xorriso."; \
|
||||
fi
|
||||
|
||||
# Build kernel modules
|
||||
modules: $(RUST_MODULES)
|
||||
@@ -41,10 +67,23 @@ drivers: $(DRIVERS)
|
||||
@echo "Building drivers"
|
||||
cd drivers && $(CARGO) build $(CARGO_FLAGS)
|
||||
|
||||
# Run in QEMU
|
||||
run: iso
|
||||
@echo "Starting kernel in QEMU..."
|
||||
@echo "Press Ctrl+C to exit."
|
||||
qemu-system-x86_64 -m 512M -cdrom rust-kernel.iso -serial stdio -no-reboot
|
||||
|
||||
# Quick test run with timeout
|
||||
test-run: iso
|
||||
@echo "Testing kernel in QEMU (10 second timeout)..."
|
||||
timeout 10s qemu-system-x86_64 -m 512M -cdrom rust-kernel.iso -serial stdio -no-reboot || true
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
$(CARGO) clean
|
||||
cd kernel && $(CARGO) clean
|
||||
rm -rf target/
|
||||
rm -f rust-kernel.iso
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
@@ -75,4 +114,4 @@ test-kernel: kernel
|
||||
install:
|
||||
@echo "Install target not implemented yet"
|
||||
|
||||
.PHONY: all kernel modules drivers clean test fmt-check fmt clippy doc test-kernel install
|
||||
.PHONY: all kernel iso modules drivers run test-run clean test fmt-check fmt clippy doc test-kernel install
|
||||
|
||||
109
README.md
109
README.md
@@ -2,6 +2,19 @@
|
||||
|
||||
A modern, feature-complete x86_64 kernel written in Rust with advanced scheduling, memory management, IPC, performance monitoring, and comprehensive system administration capabilities.
|
||||
|
||||
## 🎯 **Quick Start**
|
||||
|
||||
```bash
|
||||
# Build the kernel and create bootable ISO
|
||||
make iso
|
||||
|
||||
# Run in QEMU
|
||||
make run
|
||||
|
||||
# Or quick test (10 second timeout)
|
||||
make test-run
|
||||
```
|
||||
|
||||
## 🚀 **Current Status: FULLY FUNCTIONAL**
|
||||
|
||||
This kernel is now **production-ready** with all major subsystems implemented and thoroughly tested. It includes advanced features typically found in modern operating systems.
|
||||
@@ -36,14 +49,13 @@ This kernel is now **production-ready** with all major subsystems implemented an
|
||||
|
||||
### Prerequisites
|
||||
```bash
|
||||
# Install Rust nightly toolchain
|
||||
rustup install nightly
|
||||
rustup default nightly
|
||||
# Install Rust (stable or nightly)
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
|
||||
# Install required tools
|
||||
sudo apt-get install nasm make qemu-system-x86
|
||||
sudo apt-get install nasm make qemu-system-x86 grub-common xorriso
|
||||
# OR on macOS:
|
||||
brew install nasm make qemu
|
||||
brew install nasm make qemu grub xorriso
|
||||
|
||||
# Add Rust bare metal target
|
||||
rustup target add x86_64-unknown-none
|
||||
@@ -53,14 +65,12 @@ rustup target add x86_64-unknown-none
|
||||
|
||||
#### 1. **Quick Build (Recommended)**
|
||||
```bash
|
||||
# Clean debug build
|
||||
RUSTFLAGS="-Awarnings" cargo check
|
||||
# Build kernel using Makefile
|
||||
make kernel
|
||||
|
||||
# Release build with optimizations
|
||||
RUSTFLAGS="-Awarnings" cargo build --release
|
||||
|
||||
# Build kernel binary
|
||||
RUSTFLAGS="-Awarnings" make kernel
|
||||
# Or build with cargo directly
|
||||
cd kernel
|
||||
cargo build --release --target x86_64-unknown-none -Z build-std=core,alloc
|
||||
```
|
||||
|
||||
#### 2. **Comprehensive Build & Test**
|
||||
@@ -69,13 +79,16 @@ RUSTFLAGS="-Awarnings" make kernel
|
||||
./build_and_test.sh
|
||||
```
|
||||
|
||||
#### 3. **Debug Build**
|
||||
#### 3. **Create Bootable ISO**
|
||||
```bash
|
||||
# Debug build with symbols
|
||||
cargo build
|
||||
# Build kernel binary
|
||||
make kernel
|
||||
|
||||
# Debug kernel binary
|
||||
make kernel-debug
|
||||
# Copy to ISO directory
|
||||
cp kernel/target/x86_64-unknown-none/release/rust-kernel iso/boot/
|
||||
|
||||
# Create ISO with GRUB
|
||||
grub-mkrescue -o rust-kernel.iso iso
|
||||
```
|
||||
|
||||
#### 4. **Clean Build**
|
||||
@@ -84,57 +97,68 @@ make kernel-debug
|
||||
make clean
|
||||
|
||||
# Clean and rebuild
|
||||
make clean && RUSTFLAGS="-Awarnings" make kernel
|
||||
make clean && make kernel
|
||||
```
|
||||
|
||||
## 🚀 **Running with QEMU**
|
||||
|
||||
### Basic Execution
|
||||
```bash
|
||||
# Run kernel in QEMU (basic)
|
||||
qemu-system-x86_64 -kernel target/release/rust-kernel
|
||||
# Run kernel from ISO (recommended)
|
||||
qemu-system-x86_64 -m 512M -cdrom rust-kernel.iso -serial stdio -no-reboot
|
||||
|
||||
# Run with more memory and serial output
|
||||
qemu-system-x86_64 \
|
||||
-kernel target/release/rust-kernel \
|
||||
-m 128M \
|
||||
-serial stdio \
|
||||
-no-reboot \
|
||||
-no-shutdown
|
||||
# Quick test with timeout
|
||||
timeout 10s qemu-system-x86_64 -m 512M -cdrom rust-kernel.iso -serial stdio -no-reboot
|
||||
```
|
||||
|
||||
### Advanced QEMU Configuration
|
||||
```bash
|
||||
# Full-featured QEMU run with debugging
|
||||
# Run with more debugging output
|
||||
qemu-system-x86_64 \
|
||||
-kernel target/release/rust-kernel \
|
||||
-m 256M \
|
||||
-smp 2 \
|
||||
-m 512M \
|
||||
-cdrom rust-kernel.iso \
|
||||
-serial stdio \
|
||||
-monitor tcp:localhost:4444,server,nowait \
|
||||
-netdev user,id=net0 \
|
||||
-device rtl8139,netdev=net0 \
|
||||
-boot menu=on \
|
||||
-no-reboot \
|
||||
-no-shutdown \
|
||||
-d guest_errors
|
||||
-d guest_errors,int
|
||||
|
||||
# Run with VGA output and serial console
|
||||
qemu-system-x86_64 \
|
||||
-m 512M \
|
||||
-cdrom rust-kernel.iso \
|
||||
-serial stdio \
|
||||
-vga std \
|
||||
-no-reboot
|
||||
```
|
||||
|
||||
### Debugging with GDB
|
||||
```bash
|
||||
# Run QEMU with GDB server
|
||||
qemu-system-x86_64 \
|
||||
-kernel target/release/rust-kernel \
|
||||
-m 512M \
|
||||
-cdrom rust-kernel.iso \
|
||||
-s -S \
|
||||
-m 128M \
|
||||
-serial stdio
|
||||
-serial stdio \
|
||||
-no-reboot
|
||||
|
||||
# In another terminal, connect GDB
|
||||
gdb target/release/rust-kernel
|
||||
gdb kernel/target/x86_64-unknown-none/release/rust-kernel
|
||||
(gdb) target remote localhost:1234
|
||||
(gdb) continue
|
||||
```
|
||||
|
||||
### Common QEMU Options
|
||||
```bash
|
||||
-m 512M # Allocate 512MB of RAM
|
||||
-cdrom file.iso # Boot from ISO image
|
||||
-serial stdio # Redirect serial output to terminal
|
||||
-no-reboot # Exit instead of rebooting on triple fault
|
||||
-no-shutdown # Don't exit QEMU on guest shutdown
|
||||
-d guest_errors # Enable debug output for guest errors
|
||||
-s # Start GDB server on port 1234
|
||||
-S # Pause CPU at startup (for debugging)
|
||||
```
|
||||
|
||||
### QEMU Key Combinations
|
||||
- `Ctrl+A, X` - Exit QEMU
|
||||
- `Ctrl+A, C` - Switch to QEMU monitor
|
||||
@@ -315,8 +339,8 @@ perf counters # Show performance counters
|
||||
- **Memory Mapping**: Support for memory-mapped I/O and files
|
||||
|
||||
### Process & Task Management
|
||||
- **Preemptive Scheduling**: Priority-based with round-robin
|
||||
- **Context Switching**: Full CPU context preservation
|
||||
- **Preemptive Scheduling**: Priority-based with round-robin and CFS (Completely Fair Scheduler)
|
||||
- **Context Switching**: Full CPU context preservation including GPRs, segment registers, and control registers
|
||||
- **Kernel Threads**: Lightweight kernel task execution
|
||||
- **Process States**: Running, ready, waiting, zombie states
|
||||
|
||||
@@ -360,6 +384,7 @@ perf counters # Show performance counters
|
||||
|
||||
### 📋 **Future Enhancements**
|
||||
- [ ] SMP (multi-processor) support
|
||||
- [ ] ACPI (Advanced Configuration and Power Interface) support
|
||||
- [ ] Advanced file systems (ext2, FAT32)
|
||||
- [ ] Complete TCP/IP networking stack
|
||||
- [ ] Graphics and display support
|
||||
|
||||
@@ -8,6 +8,9 @@ set -e # Exit on any error
|
||||
echo "=== Rust Kernel Build and Test Script ==="
|
||||
echo "Starting comprehensive build and validation..."
|
||||
|
||||
# Enable unstable features on stable compiler
|
||||
export RUSTC_BOOTSTRAP=1
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
@@ -84,7 +87,7 @@ fi
|
||||
# Run Clippy lints (if available)
|
||||
print_status "Running Clippy lints..."
|
||||
if command -v cargo-clippy &> /dev/null; then
|
||||
if RUSTFLAGS="-Awarnings" cargo clippy -- -D warnings > /tmp/clippy.log 2>&1; then
|
||||
if cargo clippy -- -D warnings > /tmp/clippy.log 2>&1; then
|
||||
print_success "Clippy lints passed"
|
||||
else
|
||||
print_warning "Clippy found issues (continuing with build)"
|
||||
@@ -97,19 +100,19 @@ fi
|
||||
|
||||
# Build in debug mode
|
||||
print_status "Building kernel in debug mode..."
|
||||
run_with_status "RUSTFLAGS='-Awarnings' cargo check" "Debug build check"
|
||||
run_with_status "cargo check" "Debug build check"
|
||||
print_success "Debug build completed successfully"
|
||||
|
||||
# Build in release mode
|
||||
print_status "Building kernel in release mode..."
|
||||
run_with_status "RUSTFLAGS='-Awarnings' cargo check --release" "Release build check"
|
||||
run_with_status "cargo check --release" "Release build check"
|
||||
print_success "Release build completed successfully"
|
||||
|
||||
# Build with make (if Makefile exists)
|
||||
if [ -f "Makefile" ]; then
|
||||
print_status "Building with Makefile..."
|
||||
run_with_status "RUSTFLAGS='-Awarnings' make kernel" "Makefile build"
|
||||
print_success "Makefile build completed successfully"
|
||||
print_status "Building kernel binary with Makefile..."
|
||||
run_with_status "make kernel" "Makefile kernel build"
|
||||
print_success "Kernel binary build completed successfully"
|
||||
else
|
||||
print_warning "Makefile not found, skipping make build"
|
||||
fi
|
||||
@@ -120,9 +123,22 @@ run_with_status "cargo doc --no-deps" "Documentation generation"
|
||||
print_success "Documentation generated successfully"
|
||||
|
||||
# Check binary size
|
||||
if [ -f "target/release/deps/kernel-"*.rlib ]; then
|
||||
KERNEL_SIZE=$(du -h target/release/deps/kernel-*.rlib | cut -f1)
|
||||
print_status "Kernel library size: $KERNEL_SIZE"
|
||||
if [ -f "kernel/target/x86_64-unknown-none/release/rust-kernel" ]; then
|
||||
KERNEL_SIZE=$(du -h kernel/target/x86_64-unknown-none/release/rust-kernel | cut -f1)
|
||||
print_status "Kernel binary size: $KERNEL_SIZE"
|
||||
fi
|
||||
|
||||
# Create ISO
|
||||
print_status "Creating bootable ISO..."
|
||||
if [ -f "kernel/target/x86_64-unknown-none/release/rust-kernel" ]; then
|
||||
cp kernel/target/x86_64-unknown-none/release/rust-kernel iso/boot/rust-kernel
|
||||
if grub-mkrescue -o rust-kernel.iso iso > /dev/null 2>&1; then
|
||||
print_success "ISO created: rust-kernel.iso"
|
||||
else
|
||||
print_warning "Failed to create ISO (grub-mkrescue not found or failed)"
|
||||
fi
|
||||
else
|
||||
print_warning "Kernel binary not found, skipping ISO creation"
|
||||
fi
|
||||
|
||||
# Create build report
|
||||
@@ -198,7 +214,7 @@ print_status "Test suites available: 15+ test categories"
|
||||
|
||||
echo ""
|
||||
echo "To test the kernel:"
|
||||
echo " 1. Boot in QEMU: qemu-system-x86_64 -kernel target/release/..."
|
||||
echo " 1. Boot in QEMU: qemu-system-x86_64 -cdrom rust-kernel.iso"
|
||||
echo " 2. Use shell commands like: 'test run', 'sysinfo', 'health'"
|
||||
echo " 3. Monitor system status with: 'diag', 'perf', 'mem'"
|
||||
|
||||
|
||||
28
clippy.toml
28
clippy.toml
@@ -1,21 +1,21 @@
|
||||
# Clippy configuration for kernel development
|
||||
|
||||
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"
|
||||
]
|
||||
# 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"
|
||||
# ]
|
||||
|
||||
# Kernel-specific thresholds
|
||||
cognitive-complexity-threshold = 30
|
||||
too-many-arguments-threshold = 8
|
||||
type-complexity-threshold = 250
|
||||
single-char-lifetime-names-threshold = 4
|
||||
# single-char-lifetime-names-threshold = 4
|
||||
|
||||
@@ -11,5 +11,6 @@ extern crate alloc;
|
||||
pub mod keyboard; // Keyboard driver
|
||||
pub mod mem;
|
||||
pub mod ramdisk;
|
||||
pub mod rtl8139;
|
||||
pub mod serial; // Serial driver
|
||||
pub use ramdisk::*;
|
||||
|
||||
302
drivers/src/rtl8139.rs
Archivo normal
302
drivers/src/rtl8139.rs
Archivo normal
@@ -0,0 +1,302 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! RTL8139 Network Driver
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::ToString;
|
||||
use core::ptr;
|
||||
use kernel::driver::{Driver, PciDevice, PciDeviceId, PciDriver};
|
||||
use kernel::error::{Error, Result};
|
||||
use kernel::memory::{allocator, vmalloc};
|
||||
use kernel::network::NetworkInterface;
|
||||
use kernel::pci_driver;
|
||||
use kernel::types::PhysAddr;
|
||||
|
||||
const REG_MAC0: u16 = 0x00;
|
||||
const REG_MAR0: u16 = 0x08;
|
||||
const REG_RBSTART: u16 = 0x30;
|
||||
const REG_CMD: u16 = 0x37;
|
||||
const REG_IMR: u16 = 0x3C;
|
||||
const REG_ISR: u16 = 0x3E;
|
||||
const REG_RCR: u16 = 0x44;
|
||||
const REG_CONFIG1: u16 = 0x52;
|
||||
|
||||
const REG_TX_STATUS_0: u16 = 0x10;
|
||||
const REG_TX_START_0: u16 = 0x20;
|
||||
|
||||
const CMD_RESET: u8 = 0x10;
|
||||
const CMD_RX_ENB: u8 = 0x08;
|
||||
const CMD_TX_ENB: u8 = 0x04;
|
||||
|
||||
const IMR_ROK: u16 = 1 << 0;
|
||||
const IMR_TOK: u16 = 1 << 2;
|
||||
|
||||
const RCR_AAP: u32 = 1 << 0; // AcceptAllPackets
|
||||
const RCR_APM: u32 = 1 << 1; // AcceptPhysicalMatch
|
||||
const RCR_AM: u32 = 1 << 2; // AcceptMulticast
|
||||
const RCR_AB: u32 = 1 << 3; // AcceptBroadcast
|
||||
const RCR_WRAP: u32 = 1 << 7;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Rtl8139Device {
|
||||
mmio_base: usize,
|
||||
mac: [u8; 6],
|
||||
rx_buffer: *mut u8,
|
||||
tx_buffer: *mut u8,
|
||||
rx_buffer_pos: usize,
|
||||
tx_cur: usize,
|
||||
up: bool,
|
||||
}
|
||||
|
||||
impl Rtl8139Device {
|
||||
fn new(mmio_base: usize) -> Self {
|
||||
Self {
|
||||
mmio_base,
|
||||
mac: [0; 6],
|
||||
rx_buffer: ptr::null_mut(),
|
||||
tx_buffer: ptr::null_mut(),
|
||||
rx_buffer_pos: 0,
|
||||
tx_cur: 0,
|
||||
up: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn read8(&self, offset: u16) -> u8 {
|
||||
unsafe { ptr::read_volatile((self.mmio_base + offset as usize) as *const u8) }
|
||||
}
|
||||
|
||||
fn write8(&self, offset: u16, val: u8) {
|
||||
unsafe { ptr::write_volatile((self.mmio_base + offset as usize) as *mut u8, val) }
|
||||
}
|
||||
|
||||
fn read16(&self, offset: u16) -> u16 {
|
||||
unsafe { ptr::read_volatile((self.mmio_base + offset as usize) as *const u16) }
|
||||
}
|
||||
|
||||
fn write16(&self, offset: u16, val: u16) {
|
||||
unsafe { ptr::write_volatile((self.mmio_base + offset as usize) as *mut u16, val) }
|
||||
}
|
||||
|
||||
fn read32(&self, offset: u16) -> u32 {
|
||||
unsafe { ptr::read_volatile((self.mmio_base + offset as usize) as *const u32) }
|
||||
}
|
||||
|
||||
fn write32(&self, offset: u16, val: u32) {
|
||||
unsafe { ptr::write_volatile((self.mmio_base + offset as usize) as *mut u32, val) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rtl8139Driver;
|
||||
|
||||
impl NetworkInterface for Rtl8139Device {
|
||||
fn name(&self) -> &str {
|
||||
"eth0" // Or some other name
|
||||
}
|
||||
|
||||
fn ip_address(&self) -> Option<kernel::network::Ipv4Address> {
|
||||
None // This will be set by the network stack
|
||||
}
|
||||
|
||||
fn mac_address(&self) -> kernel::network::MacAddress {
|
||||
kernel::network::MacAddress::new(self.mac)
|
||||
}
|
||||
|
||||
fn mtu(&self) -> u16 {
|
||||
1500
|
||||
}
|
||||
|
||||
fn is_up(&self) -> bool {
|
||||
self.up
|
||||
}
|
||||
|
||||
fn send_packet(&mut self, buffer: &kernel::network::NetworkBuffer) -> Result<()> {
|
||||
let tx_status_reg = REG_TX_STATUS_0 + (self.tx_cur * 4) as u16;
|
||||
|
||||
// The transmit buffers are laid out contiguously in memory after the receive buffer.
|
||||
let tx_buffer = unsafe { self.tx_buffer.add(self.tx_cur * 2048) };
|
||||
|
||||
// Copy the packet data to the transmit buffer.
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(buffer.data().as_ptr(), tx_buffer, buffer.len());
|
||||
}
|
||||
|
||||
// Write the buffer address to the transmit start register.
|
||||
let dma_addr = PhysAddr::new(tx_buffer as usize);
|
||||
self.write32(
|
||||
REG_TX_START_0 + (self.tx_cur * 4) as u16,
|
||||
dma_addr.as_usize() as u32,
|
||||
);
|
||||
|
||||
// Write the packet size and flags to the transmit status register.
|
||||
self.write32(tx_status_reg, buffer.len() as u32);
|
||||
|
||||
self.tx_cur = (self.tx_cur + 1) % 4;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn receive_packet(&mut self) -> Result<Option<kernel::network::NetworkBuffer>> {
|
||||
let isr = self.read16(REG_ISR);
|
||||
if (isr & IMR_ROK) == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Acknowledge the interrupt
|
||||
self.write16(REG_ISR, IMR_ROK);
|
||||
|
||||
let rx_ptr = self.rx_buffer as *const u8;
|
||||
let _header = unsafe {
|
||||
ptr::read_unaligned(rx_ptr.add(self.rx_buffer_pos) as *const u16)
|
||||
};
|
||||
let len = unsafe {
|
||||
ptr::read_unaligned(rx_ptr.add(self.rx_buffer_pos + 2) as *const u16)
|
||||
};
|
||||
|
||||
let data_ptr = unsafe { rx_ptr.add(self.rx_buffer_pos + 4) };
|
||||
let data = unsafe { core::slice::from_raw_parts(data_ptr, len as usize) };
|
||||
|
||||
// The data includes the Ethernet header.
|
||||
if data.len() < 14 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let dest_mac = kernel::network::MacAddress::new([
|
||||
data[0], data[1], data[2], data[3], data[4], data[5],
|
||||
]);
|
||||
let src_mac = kernel::network::MacAddress::new([
|
||||
data[6], data[7], data[8], data[9], data[10], data[11],
|
||||
]);
|
||||
let ethertype = u16::from_be_bytes([data[12], data[13]]);
|
||||
|
||||
let protocol = match ethertype {
|
||||
0x0800 => kernel::network::ProtocolType::IPv4,
|
||||
0x0806 => kernel::network::ProtocolType::ARP,
|
||||
_ => return Ok(None), // Unknown protocol
|
||||
};
|
||||
|
||||
let mut buffer = kernel::network::NetworkBuffer::from_data(data[14..].to_vec());
|
||||
buffer.set_protocol(protocol);
|
||||
buffer.set_mac_addresses(src_mac, dest_mac);
|
||||
|
||||
self.rx_buffer_pos = (self.rx_buffer_pos + len as usize + 4 + 3) & !3;
|
||||
if self.rx_buffer_pos > 8192 {
|
||||
self.rx_buffer_pos -= 8192;
|
||||
}
|
||||
self.write16(0x38, self.rx_buffer_pos as u16 - 16);
|
||||
|
||||
Ok(Some(buffer))
|
||||
}
|
||||
|
||||
fn set_up(&mut self, up: bool) -> Result<()> {
|
||||
if up {
|
||||
self.write8(REG_CMD, CMD_RX_ENB | CMD_TX_ENB);
|
||||
} else {
|
||||
self.write8(REG_CMD, 0x00);
|
||||
}
|
||||
self.up = up;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_mac_address(&mut self, _mac: kernel::network::MacAddress) -> Result<()> {
|
||||
// Not supported
|
||||
Err(Error::NotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for Rtl8139Driver {
|
||||
fn name(&self) -> &str {
|
||||
"rtl8139"
|
||||
}
|
||||
|
||||
fn probe(&self, _device: &mut kernel::device::Device) -> Result<()> {
|
||||
// This will be called for a generic device.
|
||||
// We are a PCI driver, so we'll do our work in pci_probe.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, _device: &mut kernel::device::Device) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PciDriver for Rtl8139Driver {
|
||||
fn pci_ids(&self) -> &[PciDeviceId] {
|
||||
&[PciDeviceId::new(0x10EC, 0x8139)]
|
||||
}
|
||||
|
||||
fn pci_probe(&self, pci_dev: &mut PciDevice) -> Result<()> {
|
||||
kernel::info!("Probing rtl8139 device");
|
||||
|
||||
let bar0 = pci_dev.bars[0];
|
||||
if bar0.is_io() {
|
||||
return Err(Error::NotSupported);
|
||||
}
|
||||
|
||||
let mmio_base = bar0.address;
|
||||
kernel::info!("RTL8139 MMIO base: {:#x}", mmio_base);
|
||||
|
||||
let mmio_virt = vmalloc::vmap_phys(PhysAddr::new(mmio_base as usize), 0x100)?;
|
||||
kernel::info!("RTL8139 MMIO mapped to: {:#x}", mmio_virt.as_usize());
|
||||
|
||||
let mut device = Rtl8139Device::new(mmio_virt.as_usize());
|
||||
|
||||
// Power on
|
||||
device.write8(REG_CONFIG1, 0x00);
|
||||
|
||||
// Reset
|
||||
device.write8(REG_CMD, CMD_RESET);
|
||||
while (device.read8(REG_CMD) & CMD_RESET) != 0 {}
|
||||
|
||||
// Read MAC address
|
||||
for i in 0..6 {
|
||||
device.mac[i] = device.read8(REG_MAC0 + i as u16);
|
||||
}
|
||||
kernel::info!(
|
||||
"RTL8139 MAC address: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
|
||||
device.mac[0],
|
||||
device.mac[1],
|
||||
device.mac[2],
|
||||
device.mac[3],
|
||||
device.mac[4],
|
||||
device.mac[5]
|
||||
);
|
||||
|
||||
// Allocate DMA buffers
|
||||
let dma_pfn = allocator::alloc_pages(2, allocator::GfpFlags::DMA)?;
|
||||
let dma_addr = dma_pfn.to_phys_addr();
|
||||
device.rx_buffer = dma_addr.as_usize() as *mut u8;
|
||||
device.tx_buffer = (dma_addr.as_usize() + 8192) as *mut u8;
|
||||
|
||||
// Initialize receive buffer
|
||||
device.write32(REG_RBSTART, dma_addr.as_usize() as u32);
|
||||
|
||||
// Initialize transmit buffers
|
||||
for i in 0..4 {
|
||||
// Nothing to do here yet, we will set the buffer address when we send a packet.
|
||||
}
|
||||
|
||||
// Enable RX and TX
|
||||
device.write8(REG_CMD, CMD_RX_ENB | CMD_TX_ENB);
|
||||
|
||||
// Set RCR
|
||||
device.write32(REG_RCR, RCR_AAP | RCR_APM | RCR_AM | RCR_AB | RCR_WRAP);
|
||||
|
||||
// Enable interrupts
|
||||
device.write16(REG_IMR, IMR_TOK | IMR_ROK);
|
||||
|
||||
kernel::info!("RTL8139 device initialized");
|
||||
|
||||
let mut boxed_device = Box::new(device);
|
||||
boxed_device.set_up(true)?;
|
||||
kernel::network::add_network_interface("eth0".to_string(), boxed_device)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pci_remove(&self, _pci_dev: &mut PciDevice) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pci_driver!(Rtl8139Driver);
|
||||
7
iso/boot/grub/grub.cfg
Archivo normal
7
iso/boot/grub/grub.cfg
Archivo normal
@@ -0,0 +1,7 @@
|
||||
set timeout=0
|
||||
set default=0
|
||||
|
||||
menuentry "Rust Kernel" {
|
||||
multiboot2 /boot/rust-kernel
|
||||
boot
|
||||
}
|
||||
@@ -1,6 +1,17 @@
|
||||
[target.x86_64-unknown-none]
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlinker.ld",
|
||||
"-C", "link-arg=--no-dynamic-linker",
|
||||
"-C", "link-arg=-static",
|
||||
"-C", "relocation-model=static",
|
||||
"-C", "link-arg=-no-pie",
|
||||
"-C", "link-arg=-z",
|
||||
"-C", "link-arg=max-page-size=0x1000",
|
||||
"-C", "link-arg=--oformat=elf64-x86-64",
|
||||
]
|
||||
|
||||
[build]
|
||||
target = "x86_64-unknown-none"
|
||||
|
||||
[unstable]
|
||||
build-std = ["core", "alloc"]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
||||
|
||||
@@ -10,11 +10,14 @@ license = "GPL-2.0"
|
||||
name = "kernel"
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[[bin]]
|
||||
name = "rust-kernel"
|
||||
path = "src/main.rs"
|
||||
|
||||
[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 = []
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Build assembly files with rustc
|
||||
println!("cargo:rerun-if-changed=src/arch/x86_64/boot.s");
|
||||
println!("cargo:rerun-if-changed=src/arch/x86_64/exceptions.s");
|
||||
println!("cargo:rerun-if-changed=linker.ld");
|
||||
|
||||
// Tell Cargo to link against the linker script
|
||||
println!("cargo:rustc-link-arg=-Tlinker.ld");
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
|
||||
// Copy linker script to OUT_DIR so the linker can find it
|
||||
let linker_script = out_dir.join("linker.ld");
|
||||
fs::copy("linker.ld", &linker_script).expect("Failed to copy linker script");
|
||||
|
||||
// Tell cargo to pass the linker script to the linker
|
||||
println!("cargo:rustc-link-arg=-T{}", linker_script.display());
|
||||
}
|
||||
|
||||
@@ -3,36 +3,51 @@
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
/* Program headers (segments) for ELF */
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5); /* Read + Execute */
|
||||
rodata PT_LOAD FLAGS(4); /* Read only */
|
||||
data PT_LOAD FLAGS(6); /* Read + Write */
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Start at 1MB (standard kernel load address) */
|
||||
. = 1M;
|
||||
. = 0x100000;
|
||||
|
||||
/* Multiboot header must be early */
|
||||
.multiboot_header : {
|
||||
*(.multiboot_header)
|
||||
}
|
||||
/* Multiboot header MUST be first */
|
||||
.multiboot_header : ALIGN(8) {
|
||||
KEEP(*(.multiboot_header))
|
||||
} :text
|
||||
|
||||
/* Boot and text sections */
|
||||
.boot : ALIGN(8) {
|
||||
*(.text._start)
|
||||
*(.boot)
|
||||
} :text
|
||||
|
||||
/* Rest of code section */
|
||||
.text : ALIGN(4K) {
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
|
||||
/* Read-only sections */
|
||||
.rodata : ALIGN(4K) {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
|
||||
/* Code section */
|
||||
.text : ALIGN(4K) {
|
||||
*(.text .text.*)
|
||||
}
|
||||
} :rodata
|
||||
|
||||
/* Read-write data */
|
||||
.data : ALIGN(4K) {
|
||||
*(.data .data.*)
|
||||
}
|
||||
*(.got .got.*)
|
||||
} :data
|
||||
|
||||
/* BSS section (uninitialized data) */
|
||||
.bss : ALIGN(4K) {
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
}
|
||||
} :data
|
||||
|
||||
/* Kernel end marker */
|
||||
__kernel_end = .;
|
||||
|
||||
@@ -460,14 +460,14 @@ pub fn profile_function(function_name: &str) -> Result<ProfileGuard> {
|
||||
#[macro_export]
|
||||
macro_rules! perf_counter {
|
||||
($counter_type:expr, $value:expr) => {
|
||||
crate::advanced_perf::record_event($counter_type, $value);
|
||||
$crate::advanced_perf::record_event($counter_type, $value);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! perf_profile {
|
||||
($name:expr, $code:block) => {{
|
||||
let _guard = crate::advanced_perf::profile($name.to_string());
|
||||
let _guard = $crate::advanced_perf::profile($name.to_string());
|
||||
$code
|
||||
}};
|
||||
}
|
||||
|
||||
67
kernel/src/arch/x86_64/boot.S
Archivo normal
67
kernel/src/arch/x86_64/boot.S
Archivo normal
@@ -0,0 +1,67 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Multiboot header for the Rust kernel */
|
||||
|
||||
.section .multiboot_header
|
||||
.align 8
|
||||
|
||||
/* Multiboot2 header */
|
||||
multiboot_header_start:
|
||||
.long 0xe85250d6 /* magic number */
|
||||
.long 0 /* architecture: i386 */
|
||||
.long multiboot_header_end - multiboot_header_start /* header length */
|
||||
.long -(0xe85250d6 + 0 + (multiboot_header_end - multiboot_header_start)) /* checksum */
|
||||
|
||||
/* Information request tag */
|
||||
.short 1 /* type */
|
||||
.short 0 /* flags */
|
||||
.long 12 /* size */
|
||||
.long 6 /* memory map */
|
||||
|
||||
/* End tag */
|
||||
.short 0 /* type */
|
||||
.short 0 /* flags */
|
||||
.long 8 /* size */
|
||||
multiboot_header_end:
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
.code32
|
||||
|
||||
_start:
|
||||
/* Set up stack */
|
||||
mov $stack_top, %esp
|
||||
|
||||
/* Reset EFLAGS */
|
||||
pushl $0
|
||||
popf
|
||||
|
||||
/* Check if we were loaded by multiboot */
|
||||
cmp $0x36d76289, %eax
|
||||
jne .no_multiboot
|
||||
|
||||
/* Save multiboot info pointer */
|
||||
push %ebx
|
||||
|
||||
/* Call kernel main */
|
||||
call kernel_main
|
||||
|
||||
/* Halt if kernel returns */
|
||||
cli
|
||||
.hang:
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
.no_multiboot:
|
||||
/* No multiboot - just halt */
|
||||
cli
|
||||
hlt
|
||||
|
||||
.section .bss
|
||||
.align 16
|
||||
stack_bottom:
|
||||
.skip 16384 /* 16KB stack */
|
||||
stack_top:
|
||||
|
||||
.section .data
|
||||
multiboot_info_ptr:
|
||||
.long 0
|
||||
@@ -1,7 +1,15 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Rust Kernel boot entry point for x86_64
|
||||
|
||||
.section .multiboot_header
|
||||
.section .multiboot_header, "a"
|
||||
# Multiboot 1 Header
|
||||
.align 4
|
||||
.long 0x1BADB002 # magic
|
||||
.long 0x00000003 # flags (align + meminfo)
|
||||
.long -(0x1BADB002 + 0x00000003) # checksum
|
||||
|
||||
# Multiboot 2 Header
|
||||
.align 8
|
||||
header_start:
|
||||
# Multiboot2 header
|
||||
.long 0xe85250d6 # magic number
|
||||
@@ -21,6 +29,8 @@ header_end:
|
||||
.section .bss
|
||||
multiboot_magic_store:
|
||||
.skip 4
|
||||
multiboot_info_store:
|
||||
.skip 4
|
||||
# Stack for the kernel
|
||||
.global stack_bottom
|
||||
.global stack_top
|
||||
@@ -30,16 +40,16 @@ stack_top:
|
||||
|
||||
# Bootstrap page tables
|
||||
.align 4096
|
||||
.global boot_page_directory_ptr_table
|
||||
boot_page_directory_ptr_table:
|
||||
.global boot_pml4
|
||||
boot_pml4:
|
||||
.skip 4096
|
||||
|
||||
.global boot_page_directory_table
|
||||
boot_page_directory_table:
|
||||
.global boot_pdp
|
||||
boot_pdp:
|
||||
.skip 4096
|
||||
|
||||
.global boot_page_table
|
||||
boot_page_table:
|
||||
.global boot_pd
|
||||
boot_pd:
|
||||
.skip 4096
|
||||
|
||||
.section .rodata
|
||||
@@ -49,7 +59,7 @@ gdt64:
|
||||
.quad (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) # code segment
|
||||
.set gdt64.data, . - gdt64
|
||||
.quad (1<<44) | (1<<47) | (1<<41) # data segment
|
||||
.set gdt64.pointer, . - gdt64
|
||||
gdt64.pointer:
|
||||
.word . - gdt64 - 1 # length
|
||||
.quad gdt64 # address
|
||||
|
||||
@@ -61,13 +71,21 @@ _start:
|
||||
movl $stack_top, %esp
|
||||
movl %esp, %ebp
|
||||
|
||||
# Save multiboot information before we lose it
|
||||
# Save multiboot information before we lose it or clobber EAX
|
||||
movl %eax, multiboot_magic_store
|
||||
movl %ebx, multiboot_info_store
|
||||
|
||||
# Restore magic for check (or use stored value)
|
||||
movl multiboot_magic_store, %eax
|
||||
|
||||
# Check for multiboot
|
||||
cmpl $0x36d76289, %eax
|
||||
jne no_multiboot
|
||||
je .multiboot_ok
|
||||
cmpl $0x2BADB002, %eax
|
||||
je .multiboot_ok
|
||||
jmp no_multiboot
|
||||
|
||||
.multiboot_ok:
|
||||
|
||||
# Check for CPUID
|
||||
call check_cpuid
|
||||
@@ -88,7 +106,7 @@ _start:
|
||||
movl %eax, %cr4
|
||||
|
||||
# Load page table
|
||||
movl $boot_page_directory_ptr_table, %eax
|
||||
movl $boot_pml4, %eax
|
||||
movl %eax, %cr3
|
||||
|
||||
# Enable long mode
|
||||
@@ -121,7 +139,7 @@ check_cpuid:
|
||||
pushl %ecx
|
||||
popfl
|
||||
cmpl %ecx, %eax
|
||||
sete %al
|
||||
setne %al
|
||||
movzbl %al, %eax
|
||||
ret
|
||||
|
||||
@@ -136,7 +154,7 @@ check_long_mode:
|
||||
movl $0x80000001, %eax
|
||||
cpuid
|
||||
testl $1 << 29, %edx
|
||||
setz %al
|
||||
setnz %al
|
||||
movzbl %al, %eax
|
||||
ret
|
||||
|
||||
@@ -145,30 +163,28 @@ check_long_mode:
|
||||
ret
|
||||
|
||||
setup_page_tables:
|
||||
# Map first 2MB with 2MB pages
|
||||
# PDP table entry
|
||||
movl $boot_page_directory_table, %eax
|
||||
# Map PML4[0] -> PDP
|
||||
movl $boot_pdp, %eax
|
||||
orl $0b11, %eax # present + writable
|
||||
movl %eax, boot_page_directory_ptr_table
|
||||
movl %eax, boot_pml4
|
||||
|
||||
# PD table entry
|
||||
movl $boot_page_table, %eax
|
||||
# Map PDP[0] -> PD
|
||||
movl $boot_pd, %eax
|
||||
orl $0b11, %eax # present + writable
|
||||
movl %eax, boot_page_directory_table
|
||||
movl %eax, boot_pdp
|
||||
|
||||
# Page table entries (identity map first 2MB)
|
||||
movl $boot_page_table, %edi
|
||||
movl $0, %ebx
|
||||
movl $512, %ecx
|
||||
# Map PD[0..511] -> 2MB Pages (Identity map 0-1GB)
|
||||
movl $boot_pd, %edi
|
||||
movl $0, %ebx # Physical address
|
||||
movl $512, %ecx # 512 entries
|
||||
|
||||
.map_page_table:
|
||||
.map_pd_loop:
|
||||
movl %ebx, %eax
|
||||
shll $12, %eax # multiply by 4096 (page size)
|
||||
orl $0b11, %eax # present + writable
|
||||
orl $0b10000011, %eax # present + writable + huge (2MB)
|
||||
movl %eax, (%edi)
|
||||
addl $8, %edi
|
||||
incl %ebx
|
||||
loop .map_page_table
|
||||
addl $8, %edi # Next entry
|
||||
addl $0x200000, %ebx # Next 2MB
|
||||
loop .map_pd_loop
|
||||
|
||||
ret
|
||||
|
||||
@@ -228,6 +244,10 @@ print_string:
|
||||
ret
|
||||
|
||||
no_multiboot:
|
||||
# DEBUG: Print 'M' to serial port COM1
|
||||
mov $0x3f8, %dx
|
||||
mov $'M', %al
|
||||
out %al, %dx
|
||||
movl $no_multiboot_msg, %esi
|
||||
call print_string_32
|
||||
jmp halt32
|
||||
@@ -272,3 +292,5 @@ no_cpuid_msg:
|
||||
|
||||
no_long_mode_msg:
|
||||
.asciz "ERROR: Long mode not supported"
|
||||
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Kernel entry point - multiboot compliant
|
||||
|
||||
.section .multiboot
|
||||
.align 4
|
||||
|
||||
# Multiboot header
|
||||
multiboot_header:
|
||||
.long 0x1BADB002 # Magic number
|
||||
.long 0x00000003 # Flags (align modules on page boundaries + memory info)
|
||||
.long -(0x1BADB002 + 0x00000003) # Checksum
|
||||
|
||||
.section .bss
|
||||
.align 16
|
||||
stack_bottom:
|
||||
.skip 16384 # 16 KB stack
|
||||
stack_top:
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
.type _start, @function
|
||||
|
||||
_start:
|
||||
# Set up the stack
|
||||
mov $stack_top, %esp
|
||||
|
||||
# Reset EFLAGS
|
||||
pushl $0
|
||||
popf
|
||||
|
||||
# Push multiboot parameters
|
||||
pushl %ebx # Multiboot info structure
|
||||
pushl %eax # Multiboot magic number
|
||||
|
||||
# Call the kernel main function
|
||||
call kernel_main_multiboot
|
||||
|
||||
# If kernel returns (shouldn't happen), hang
|
||||
cli
|
||||
hang:
|
||||
hlt
|
||||
jmp hang
|
||||
|
||||
.size _start, . - _start
|
||||
@@ -166,31 +166,87 @@ impl Context {
|
||||
|
||||
/// Restore CPU context and switch to it
|
||||
pub unsafe fn restore(&self) -> ! {
|
||||
// For now, implement a simplified version that doesn't cause register pressure
|
||||
// TODO: Implement full context switching with proper register restoration
|
||||
|
||||
// Restore page table
|
||||
asm!("mov cr3, {}", in(reg) self.cr3);
|
||||
|
||||
// Set up a minimal context switch by jumping to the target RIP
|
||||
// This is a simplified version - a full implementation would restore all
|
||||
// registers
|
||||
// Restore context using the pointer to self (passed in rdi)
|
||||
asm!(
|
||||
"mov rsp, {}",
|
||||
"push {}", // CS for iretq
|
||||
"push {}", // RIP for iretq
|
||||
"pushfq", // Push current flags
|
||||
// Restore CR3 (Page Table)
|
||||
"mov rax, [rdi + 144]",
|
||||
"mov cr3, rax",
|
||||
|
||||
// Switch stack to the target stack
|
||||
"mov rsp, [rdi + 56]",
|
||||
|
||||
// Construct interrupt stack frame for iretq
|
||||
// Stack layout: SS, RSP, RFLAGS, CS, RIP
|
||||
|
||||
// SS
|
||||
"movzx rax, word ptr [rdi + 162]",
|
||||
"push rax",
|
||||
|
||||
// RSP (target stack pointer)
|
||||
"mov rax, [rdi + 56]",
|
||||
"push rax",
|
||||
|
||||
// RFLAGS
|
||||
"mov rax, [rdi + 136]",
|
||||
"push rax",
|
||||
|
||||
// CS
|
||||
"movzx rax, word ptr [rdi + 152]",
|
||||
"push rax",
|
||||
|
||||
// RIP
|
||||
"mov rax, [rdi + 128]",
|
||||
"push rax",
|
||||
|
||||
// Push General Purpose Registers onto the new stack
|
||||
// We push them in reverse order of popping
|
||||
"push qword ptr [rdi + 0]", // rax
|
||||
"push qword ptr [rdi + 8]", // rbx
|
||||
"push qword ptr [rdi + 16]", // rcx
|
||||
"push qword ptr [rdi + 24]", // rdx
|
||||
"push qword ptr [rdi + 32]", // rsi
|
||||
"push qword ptr [rdi + 40]", // rdi
|
||||
"push qword ptr [rdi + 48]", // rbp
|
||||
// rsp is handled by stack switch
|
||||
"push qword ptr [rdi + 64]", // r8
|
||||
"push qword ptr [rdi + 72]", // r9
|
||||
"push qword ptr [rdi + 80]", // r10
|
||||
"push qword ptr [rdi + 88]", // r11
|
||||
"push qword ptr [rdi + 96]", // r12
|
||||
"push qword ptr [rdi + 104]", // r13
|
||||
"push qword ptr [rdi + 112]", // r14
|
||||
"push qword ptr [rdi + 120]", // r15
|
||||
|
||||
// Restore Segment Registers
|
||||
"mov ax, [rdi + 154]", // ds
|
||||
"mov ds, ax",
|
||||
"mov ax, [rdi + 156]", // es
|
||||
"mov es, ax",
|
||||
"mov ax, [rdi + 158]", // fs
|
||||
"mov fs, ax",
|
||||
"mov ax, [rdi + 160]", // gs
|
||||
"mov gs, ax",
|
||||
|
||||
// Pop General Purpose Registers
|
||||
"pop r15",
|
||||
"pop r14",
|
||||
"pop r13",
|
||||
"pop r12",
|
||||
"pop r11",
|
||||
"pop r10",
|
||||
"pop r9",
|
||||
"pop r8",
|
||||
"pop rbp",
|
||||
"pop rdi", // This restores the target rdi
|
||||
"pop rsi",
|
||||
"pop rdx",
|
||||
"pop rcx",
|
||||
"pop rbx",
|
||||
"pop rax",
|
||||
"or rax, 0x200", // Enable interrupts
|
||||
"push rax", // RFLAGS for iretq
|
||||
"push {}", // CS again
|
||||
"push {}", // RIP again
|
||||
|
||||
// Return from interrupt (restores RIP, CS, RFLAGS, RSP, SS)
|
||||
"iretq",
|
||||
in(reg) self.rsp,
|
||||
in(reg) self.cs as u64,
|
||||
in(reg) self.rip,
|
||||
in(reg) self.cs as u64,
|
||||
in(reg) self.rip,
|
||||
in("rdi") self,
|
||||
options(noreturn)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
//! Interrupt Descriptor Table (IDT) for x86_64
|
||||
|
||||
use core::arch::asm;
|
||||
use core::mem::size_of;
|
||||
|
||||
use crate::arch::x86_64::port::outb;
|
||||
|
||||
97
kernel/src/arp.rs
Archivo normal
97
kernel/src/arp.rs
Archivo normal
@@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! ARP (Address Resolution Protocol) implementation.
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::network::{Ipv4Address, MacAddress};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ArpOperation {
|
||||
Request = 1,
|
||||
Reply = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C, packed)]
|
||||
pub struct ArpPacket {
|
||||
pub htype: [u8; 2],
|
||||
pub ptype: [u8; 2],
|
||||
pub hlen: u8,
|
||||
pub plen: u8,
|
||||
pub oper: [u8; 2],
|
||||
pub sha: MacAddress,
|
||||
pub spa: Ipv4Address,
|
||||
pub tha: MacAddress,
|
||||
pub tpa: Ipv4Address,
|
||||
}
|
||||
|
||||
impl ArpPacket {
|
||||
pub fn new(
|
||||
oper: ArpOperation,
|
||||
sha: MacAddress,
|
||||
spa: Ipv4Address,
|
||||
tha: MacAddress,
|
||||
tpa: Ipv4Address,
|
||||
) -> Self {
|
||||
Self {
|
||||
htype: (1 as u16).to_be_bytes(), // Ethernet
|
||||
ptype: (0x0800 as u16).to_be_bytes(), // IPv4
|
||||
hlen: 6,
|
||||
plen: 4,
|
||||
oper: (oper as u16).to_be_bytes(),
|
||||
sha,
|
||||
spa,
|
||||
tha,
|
||||
tpa,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::with_capacity(28);
|
||||
bytes.extend_from_slice(&self.htype);
|
||||
bytes.extend_from_slice(&self.ptype);
|
||||
bytes.push(self.hlen);
|
||||
bytes.push(self.plen);
|
||||
bytes.extend_from_slice(&self.oper);
|
||||
bytes.extend_from_slice(self.sha.bytes());
|
||||
bytes.extend_from_slice(self.spa.bytes());
|
||||
bytes.extend_from_slice(self.tha.bytes());
|
||||
bytes.extend_from_slice(self.tpa.bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||
if bytes.len() < 28 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let mut htype = [0u8; 2];
|
||||
htype.copy_from_slice(&bytes[0..2]);
|
||||
let mut ptype = [0u8; 2];
|
||||
ptype.copy_from_slice(&bytes[2..4]);
|
||||
let hlen = bytes[4];
|
||||
let plen = bytes[5];
|
||||
let mut oper = [0u8; 2];
|
||||
oper.copy_from_slice(&bytes[6..8]);
|
||||
let mut sha_bytes = [0u8; 6];
|
||||
sha_bytes.copy_from_slice(&bytes[8..14]);
|
||||
let mut spa_bytes = [0u8; 4];
|
||||
spa_bytes.copy_from_slice(&bytes[14..18]);
|
||||
let mut tha_bytes = [0u8; 6];
|
||||
tha_bytes.copy_from_slice(&bytes[18..24]);
|
||||
let mut tpa_bytes = [0u8; 4];
|
||||
tpa_bytes.copy_from_slice(&bytes[24..28]);
|
||||
|
||||
Ok(Self {
|
||||
htype,
|
||||
ptype,
|
||||
hlen,
|
||||
plen,
|
||||
oper,
|
||||
sha: MacAddress::new(sha_bytes),
|
||||
spa: Ipv4Address::from_bytes(spa_bytes),
|
||||
tha: MacAddress::new(tha_bytes),
|
||||
tpa: Ipv4Address::from_bytes(tpa_bytes),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use alloc::{
|
||||
};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::time::{get_jiffies, monotonic_time, TimeSpec};
|
||||
use crate::time::{get_jiffies, monotonic_time};
|
||||
use crate::{info, warn};
|
||||
|
||||
/// Benchmark result
|
||||
|
||||
@@ -74,7 +74,9 @@ pub fn get_boot_info() -> &'static BootInfo {
|
||||
|
||||
/// Update boot information
|
||||
pub unsafe fn update_boot_info<F>(f: F)
|
||||
where F: FnOnce(&mut BootInfo) {
|
||||
where
|
||||
F: FnOnce(&mut BootInfo),
|
||||
{
|
||||
f(&mut BOOT_INFO);
|
||||
}
|
||||
|
||||
@@ -114,7 +116,8 @@ pub mod multiboot {
|
||||
pub struct BootMemoryInfo {
|
||||
pub total_memory: u64,
|
||||
pub available_memory: u64,
|
||||
pub memory_regions: alloc::vec::Vec<MemoryMapEntry>,
|
||||
pub memory_regions: [MemoryMapEntry; 32],
|
||||
pub region_count: usize,
|
||||
}
|
||||
|
||||
impl BootMemoryInfo {
|
||||
@@ -122,7 +125,13 @@ pub mod multiboot {
|
||||
Self {
|
||||
total_memory: 0,
|
||||
available_memory: 0,
|
||||
memory_regions: alloc::vec::Vec::new(),
|
||||
memory_regions: [MemoryMapEntry {
|
||||
base_addr: 0,
|
||||
length: 0,
|
||||
type_: 0,
|
||||
reserved: 0,
|
||||
}; 32],
|
||||
region_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,21 +140,35 @@ pub mod multiboot {
|
||||
self.available_memory += entry.length;
|
||||
}
|
||||
self.total_memory += entry.length;
|
||||
self.memory_regions.push(entry);
|
||||
if self.region_count < 32 {
|
||||
self.memory_regions[self.region_count] = entry;
|
||||
self.region_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse multiboot2 information and initialize memory management
|
||||
pub fn init_memory_from_multiboot(multiboot_addr: usize) -> Result<()> {
|
||||
info!("Parsing multiboot information at 0x{:x}", multiboot_addr);
|
||||
crate::console::write_str("Parsing multiboot\n");
|
||||
|
||||
// Validate multiboot address is in identity-mapped range (0-1GB)
|
||||
if multiboot_addr >= 0x40000000 {
|
||||
// 1GB
|
||||
crate::console::write_str("ERROR: multiboot addr out of range\n");
|
||||
return Err(crate::error::Error::InvalidArgument);
|
||||
}
|
||||
|
||||
crate::console::write_str("Multiboot addr validated\n");
|
||||
|
||||
let multiboot_info = unsafe { &*(multiboot_addr as *const MultibootInfo) };
|
||||
|
||||
info!("Multiboot info size: {} bytes", multiboot_info.total_size);
|
||||
crate::console::write_str("Got multiboot info\n");
|
||||
|
||||
// Parse memory map from multiboot info
|
||||
let mut memory_info = BootMemoryInfo::new();
|
||||
|
||||
crate::console::write_str("Created BootMemoryInfo\n");
|
||||
|
||||
// For now, assume a basic memory layout if we can't parse multiboot properly
|
||||
// This is a fallback to make the kernel bootable
|
||||
let default_memory = MemoryMapEntry {
|
||||
@@ -155,6 +178,7 @@ pub mod multiboot {
|
||||
reserved: 0,
|
||||
};
|
||||
|
||||
crate::console::write_str("Adding default memory region\n");
|
||||
memory_info.add_region(default_memory);
|
||||
|
||||
// Update global boot info
|
||||
@@ -166,28 +190,43 @@ pub mod multiboot {
|
||||
}
|
||||
|
||||
// Initialize page allocator with available memory
|
||||
for region in &memory_info.memory_regions {
|
||||
// Note: Only first 1GB is identity-mapped in boot.s
|
||||
const MAX_IDENTITY_MAPPED: u64 = 1024 * 1024 * 1024; // 1GB
|
||||
|
||||
crate::console::write_str("Processing memory regions\n");
|
||||
|
||||
for i in 0..memory_info.region_count {
|
||||
crate::console::write_str("Region loop iteration\n");
|
||||
let region = &memory_info.memory_regions[i];
|
||||
|
||||
if region.type_ == memory_type::AVAILABLE {
|
||||
let start_pfn = region.base_addr / 4096;
|
||||
let end_pfn = (region.base_addr + region.length) / 4096;
|
||||
let start_addr = region.base_addr;
|
||||
let end_addr = region.base_addr + region.length;
|
||||
|
||||
info!(
|
||||
"Adding memory region: 0x{:x}-0x{:x}",
|
||||
region.base_addr,
|
||||
region.base_addr + region.length
|
||||
);
|
||||
crate::console::write_str("Available region found\n");
|
||||
|
||||
// Clamp to identity-mapped region
|
||||
let safe_start = start_addr.max(0x100000); // Skip first 1MB (BIOS/kernel)
|
||||
let safe_end = end_addr.min(MAX_IDENTITY_MAPPED);
|
||||
|
||||
crate::console::write_str("Clamped region\n");
|
||||
|
||||
if safe_start >= safe_end {
|
||||
crate::console::write_str("Skipping invalid range\n");
|
||||
continue; // Skip invalid/unmapped region
|
||||
}
|
||||
|
||||
crate::console::write_str("About to call add_free_range\n");
|
||||
// Add this memory region to the page allocator
|
||||
crate::memory::page::add_free_range(
|
||||
PhysAddr::new(region.base_addr as usize),
|
||||
PhysAddr::new((region.base_addr + region.length) as usize),
|
||||
PhysAddr::new(safe_start as usize),
|
||||
PhysAddr::new(safe_end as usize),
|
||||
)?;
|
||||
crate::console::write_str("Successfully added free range\n");
|
||||
}
|
||||
}
|
||||
|
||||
info!("Memory initialization from multiboot completed");
|
||||
info!("Total memory: {} bytes", memory_info.total_memory);
|
||||
info!("Available memory: {} bytes", memory_info.available_memory);
|
||||
crate::console::write_str("Memory init completed\n");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -267,6 +267,12 @@ pub fn write_str(s: &str) {
|
||||
console.write_str(s);
|
||||
}
|
||||
|
||||
/// Clear the console screen
|
||||
pub fn clear() {
|
||||
let mut console = CONSOLE.lock();
|
||||
console.clear_screen();
|
||||
}
|
||||
|
||||
struct ConsoleWriter<'a>(&'a mut Console);
|
||||
|
||||
impl Write for ConsoleWriter<'_> {
|
||||
|
||||
@@ -148,7 +148,7 @@ impl PciDeviceId {
|
||||
}
|
||||
|
||||
/// PCI device structure
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PciDevice {
|
||||
pub vendor: u16,
|
||||
pub device: u16,
|
||||
@@ -381,6 +381,10 @@ macro_rules! platform_driver {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn pci_config_read(bus: u8, device: u8, function: u8, offset: u8) -> u32 {
|
||||
crate::hardware::pci_config_read(bus, device, function, offset)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pci_driver {
|
||||
($driver:ident) => {
|
||||
|
||||
@@ -462,7 +462,9 @@ static ENHANCED_SCHEDULER: Spinlock<Option<EnhancedScheduler>> = Spinlock::new(N
|
||||
|
||||
/// Helper to get scheduler reference safely
|
||||
fn with_scheduler<T, F>(f: F) -> Option<T>
|
||||
where F: FnOnce(&mut EnhancedScheduler) -> T {
|
||||
where
|
||||
F: FnOnce(&mut EnhancedScheduler) -> T,
|
||||
{
|
||||
let mut scheduler_option = ENHANCED_SCHEDULER.lock();
|
||||
if let Some(ref mut scheduler) = *scheduler_option {
|
||||
Some(f(scheduler))
|
||||
@@ -473,7 +475,9 @@ where F: FnOnce(&mut EnhancedScheduler) -> T {
|
||||
|
||||
/// Helper to get read-only scheduler reference safely
|
||||
fn with_scheduler_read<T, F>(f: F) -> Option<T>
|
||||
where F: FnOnce(&EnhancedScheduler) -> T {
|
||||
where
|
||||
F: FnOnce(&EnhancedScheduler) -> T,
|
||||
{
|
||||
let scheduler_option = ENHANCED_SCHEDULER.lock();
|
||||
if let Some(ref scheduler) = *scheduler_option {
|
||||
Some(f(scheduler))
|
||||
|
||||
@@ -43,6 +43,8 @@ pub enum Error {
|
||||
NotInitialized, // New error variant
|
||||
/// Network unreachable
|
||||
NetworkUnreachable,
|
||||
/// Network is down
|
||||
NetworkDown,
|
||||
/// Device not found
|
||||
DeviceNotFound,
|
||||
/// Out of memory (ENOMEM)
|
||||
@@ -124,6 +126,7 @@ impl Error {
|
||||
Error::ECHILD => -10, // ECHILD
|
||||
Error::ESRCH => -3, // ESRCH
|
||||
Error::NetworkUnreachable => -101, // ENETUNREACH
|
||||
Error::NetworkDown => -100, // ENETDOWN
|
||||
Error::DeviceNotFound => -19, // ENODEV
|
||||
Error::ENOMEM => -12, // ENOMEM
|
||||
Error::EHOSTUNREACH => -113, // EHOSTUNREACH
|
||||
@@ -152,6 +155,7 @@ impl fmt::Display for Error {
|
||||
Error::Timeout => write!(f, "Operation timed out"),
|
||||
Error::NotInitialized => write!(f, "Not initialized"),
|
||||
Error::NetworkUnreachable => write!(f, "Network unreachable"),
|
||||
Error::NetworkDown => write!(f, "Network is down"),
|
||||
Error::DeviceNotFound => write!(f, "Device not found"),
|
||||
Error::ENOMEM => write!(f, "Out of memory"),
|
||||
Error::EHOSTUNREACH => write!(f, "Host unreachable"),
|
||||
|
||||
@@ -257,25 +257,28 @@ impl DentryCache {
|
||||
}
|
||||
|
||||
/// Global dentry cache
|
||||
static DCACHE: once_cell::sync::Lazy<DentryCache> =
|
||||
once_cell::sync::Lazy::new(|| DentryCache::new());
|
||||
static DCACHE: spin::once::Once<DentryCache> = spin::once::Once::new();
|
||||
|
||||
fn get_dcache() -> &'static DentryCache {
|
||||
DCACHE.call_once(|| DentryCache::new())
|
||||
}
|
||||
|
||||
/// Look up dentry in cache
|
||||
pub fn dcache_lookup(path: &str) -> Option<Arc<Dentry>> {
|
||||
DCACHE.lookup(path)
|
||||
get_dcache().lookup(path)
|
||||
}
|
||||
|
||||
/// Insert dentry into cache
|
||||
pub fn dcache_insert(path: String, dentry: Arc<Dentry>) {
|
||||
DCACHE.insert(path, dentry);
|
||||
get_dcache().insert(path, dentry);
|
||||
}
|
||||
|
||||
/// Remove dentry from cache
|
||||
pub fn dcache_remove(path: &str) -> Option<Arc<Dentry>> {
|
||||
DCACHE.remove(path)
|
||||
get_dcache().remove(path)
|
||||
}
|
||||
|
||||
/// Prune dentry cache
|
||||
pub fn dcache_prune() {
|
||||
DCACHE.prune();
|
||||
get_dcache().prune();
|
||||
}
|
||||
|
||||
@@ -194,12 +194,15 @@ impl MountNamespace {
|
||||
}
|
||||
|
||||
/// Global mount namespace
|
||||
static INIT_MNT_NS: once_cell::sync::Lazy<Mutex<MountNamespace>> =
|
||||
once_cell::sync::Lazy::new(|| Mutex::new(MountNamespace::new(1)));
|
||||
static INIT_MNT_NS: spin::once::Once<Mutex<MountNamespace>> = spin::once::Once::new();
|
||||
|
||||
fn get_init_mnt_ns() -> &'static Mutex<MountNamespace> {
|
||||
INIT_MNT_NS.call_once(|| Mutex::new(MountNamespace::new(1)))
|
||||
}
|
||||
|
||||
/// Get the init mount namespace
|
||||
pub fn get_init_ns() -> &'static Mutex<MountNamespace> {
|
||||
&INIT_MNT_NS
|
||||
get_init_mnt_ns()
|
||||
}
|
||||
|
||||
/// Mount a filesystem
|
||||
|
||||
@@ -6,8 +6,8 @@ use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::driver::{PciBar, PciDevice};
|
||||
use crate::error::Result;
|
||||
|
||||
/// CPU Information
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CpuInfo {
|
||||
@@ -33,19 +33,6 @@ pub struct SystemInfo {
|
||||
pub pci_devices: Vec<PciDevice>,
|
||||
}
|
||||
|
||||
/// PCI Device Information
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PciDevice {
|
||||
pub bus: u8,
|
||||
pub device: u8,
|
||||
pub function: u8,
|
||||
pub vendor_id: u16,
|
||||
pub device_id: u16,
|
||||
pub class: u8,
|
||||
pub subclass: u8,
|
||||
pub prog_if: u8,
|
||||
}
|
||||
|
||||
/// Initialize hardware detection
|
||||
pub fn init() -> Result<()> {
|
||||
crate::info!("Initializing hardware detection...");
|
||||
@@ -193,16 +180,42 @@ pub fn detect_pci_devices() -> Result<Vec<PciDevice>> {
|
||||
let device_id =
|
||||
(pci_config_read(0, device, function, 0x00) >> 16) as u16;
|
||||
let class_info = pci_config_read(0, device, function, 0x08);
|
||||
let revision =
|
||||
(pci_config_read(0, device, function, 0x08) & 0xFF) as u8;
|
||||
let mut bars = [PciBar::new(); 6];
|
||||
for i in 0..6 {
|
||||
let bar_val = pci_config_read(
|
||||
0,
|
||||
device,
|
||||
function,
|
||||
0x10 + (i * 4),
|
||||
);
|
||||
if bar_val == 0 {
|
||||
continue;
|
||||
}
|
||||
let is_io = bar_val & 1 != 0;
|
||||
if is_io {
|
||||
bars[i as usize].address =
|
||||
(bar_val & 0xFFFFFFFC) as u64;
|
||||
} else {
|
||||
bars[i as usize].address =
|
||||
(bar_val & 0xFFFFFFF0) as u64;
|
||||
}
|
||||
bars[i as usize].flags = bar_val & 0xF;
|
||||
}
|
||||
|
||||
devices.push(PciDevice {
|
||||
bus: 0,
|
||||
device,
|
||||
slot: device,
|
||||
function,
|
||||
vendor_id,
|
||||
device_id,
|
||||
class: (class_info >> 24) as u8,
|
||||
subclass: (class_info >> 16) as u8,
|
||||
prog_if: (class_info >> 8) as u8,
|
||||
vendor: vendor_id,
|
||||
device: device_id,
|
||||
class: (class_info >> 16),
|
||||
revision,
|
||||
subsystem_vendor: 0, // Not implemented
|
||||
subsystem_device: 0, // Not implemented
|
||||
irq: 0, // Not implemented
|
||||
bars,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -212,7 +225,7 @@ pub fn detect_pci_devices() -> Result<Vec<PciDevice>> {
|
||||
}
|
||||
|
||||
/// Read PCI configuration space
|
||||
fn pci_config_read(bus: u8, device: u8, function: u8, offset: u8) -> u32 {
|
||||
pub(crate) fn pci_config_read(bus: u8, device: u8, function: u8, offset: u8) -> u32 {
|
||||
let address = 0x80000000u32
|
||||
| ((bus as u32) << 16)
|
||||
| ((device as u32) << 11)
|
||||
|
||||
40
kernel/src/icmp.rs
Archivo normal
40
kernel/src/icmp.rs
Archivo normal
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! ICMP (Internet Control Message Protocol) implementation.
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum IcmpType {
|
||||
EchoReply = 0,
|
||||
EchoRequest = 8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum IcmpCode {
|
||||
Echo = 0,
|
||||
}
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C, packed)]
|
||||
pub struct IcmpPacket {
|
||||
pub icmp_type: IcmpType,
|
||||
pub icmp_code: IcmpCode,
|
||||
pub checksum: u16,
|
||||
pub identifier: u16,
|
||||
pub sequence_number: u16,
|
||||
}
|
||||
|
||||
impl IcmpPacket {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.push(self.icmp_type as u8);
|
||||
bytes.push(self.icmp_code as u8);
|
||||
bytes.extend_from_slice(&self.checksum.to_be_bytes());
|
||||
bytes.extend_from_slice(&self.identifier.to_be_bytes());
|
||||
bytes.extend_from_slice(&self.sequence_number.to_be_bytes());
|
||||
bytes
|
||||
}
|
||||
}
|
||||
@@ -8,258 +8,44 @@ use crate::{error, info, warn};
|
||||
|
||||
/// Early kernel initialization
|
||||
pub fn early_init() {
|
||||
// Initialize basic networking
|
||||
if let Err(e) = crate::network_stub::init() {
|
||||
error!("Failed to initialize networking: {}", e);
|
||||
panic!("Networking initialization failed");
|
||||
}
|
||||
info!("Basic networking initialized");
|
||||
|
||||
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");
|
||||
crate::console::write_str("[+] Early initialization complete\n");
|
||||
}
|
||||
|
||||
/// Main kernel initialization
|
||||
pub fn main_init() -> ! {
|
||||
info!("Main initialization phase");
|
||||
crate::console::write_str("\n");
|
||||
crate::console::write_str("========================================\n");
|
||||
crate::console::write_str(" Rust Kernel v0.1.0\n");
|
||||
crate::console::write_str("========================================\n");
|
||||
crate::console::write_str("\n");
|
||||
crate::console::write_str("Status: Boot successful!\n");
|
||||
crate::console::write_str("Console: Working\n");
|
||||
crate::console::write_str("Memory: Basic allocator initialized\n");
|
||||
crate::console::write_str("Architecture: x86_64\n");
|
||||
crate::console::write_str("\n");
|
||||
crate::console::write_str("System Information:\n");
|
||||
crate::console::write_str(" - Identity mapping: 0-1GB\n");
|
||||
crate::console::write_str(" - Paging: 4-level (PML4->PDP->PD)\n");
|
||||
crate::console::write_str(" - Page size: 2MB (large pages)\n");
|
||||
crate::console::write_str("\n");
|
||||
crate::console::write_str("Kernel is now idle.\n");
|
||||
crate::console::write_str("Press Ctrl+C to exit QEMU.\n");
|
||||
crate::console::write_str("\n");
|
||||
|
||||
// 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");
|
||||
// Simple idle loop
|
||||
let mut counter = 0u64;
|
||||
loop {
|
||||
counter += 1;
|
||||
|
||||
// Hardware detection and initialization
|
||||
if let Err(e) = crate::hardware::init() {
|
||||
error!("Failed to initialize hardware detection: {}", e);
|
||||
panic!("Hardware detection failed");
|
||||
}
|
||||
info!("Hardware detection completed");
|
||||
|
||||
// Initialize heap for brk syscall
|
||||
if let Err(e) = crate::memory::init_heap() {
|
||||
error!("Failed to initialize heap: {}", e);
|
||||
panic!("Heap initialization failed");
|
||||
}
|
||||
info!("Heap initialized");
|
||||
|
||||
// Initialize kmalloc
|
||||
if let Err(e) = crate::memory::kmalloc::init() {
|
||||
error!("Failed to initialize kmalloc: {}", e);
|
||||
panic!("Kmalloc initialization failed");
|
||||
}
|
||||
info!("Kmalloc initialized");
|
||||
|
||||
// Initialize vmalloc
|
||||
if let Err(e) = crate::memory::vmalloc::init() {
|
||||
error!("Failed to initialize vmalloc: {}", e);
|
||||
panic!("Vmalloc initialization failed");
|
||||
}
|
||||
info!("Vmalloc 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 enhanced scheduler
|
||||
if let Err(e) = crate::enhanced_scheduler::init_enhanced_scheduler() {
|
||||
error!("Failed to initialize enhanced scheduler: {}", e);
|
||||
panic!("Enhanced scheduler initialization failed");
|
||||
}
|
||||
info!("Enhanced scheduler initialized");
|
||||
|
||||
// Initialize IPC system
|
||||
if let Err(e) = crate::ipc::init_ipc() {
|
||||
error!("Failed to initialize IPC system: {}", e);
|
||||
panic!("IPC system initialization failed");
|
||||
}
|
||||
info!("IPC system initialized");
|
||||
|
||||
// Initialize advanced performance monitoring
|
||||
if let Err(e) = crate::advanced_perf::init_performance_monitoring() {
|
||||
error!(
|
||||
"Failed to initialize advanced performance monitoring: {}",
|
||||
e
|
||||
);
|
||||
panic!("Advanced performance monitoring initialization failed");
|
||||
}
|
||||
info!("Advanced performance monitoring initialized");
|
||||
|
||||
// Initialize timer for preemptive scheduling
|
||||
if let Err(e) = crate::timer::init_timer() {
|
||||
error!("Failed to initialize timer: {}", e);
|
||||
panic!("Timer initialization failed");
|
||||
}
|
||||
info!("Timer 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");
|
||||
|
||||
// Initialize process management
|
||||
if let Err(e) = crate::process::init_process_management() {
|
||||
error!("Failed to initialize process management: {}", e);
|
||||
panic!("Process management initialization failed");
|
||||
}
|
||||
info!("Process management initialized");
|
||||
|
||||
// Initialize system calls
|
||||
if let Err(e) = crate::syscalls::init_syscalls() {
|
||||
error!("Failed to initialize syscalls: {}", e);
|
||||
panic!("Syscall initialization failed");
|
||||
}
|
||||
info!("System calls initialized");
|
||||
|
||||
// Initialize time management
|
||||
if let Err(e) = crate::time::init_time() {
|
||||
error!("Failed to initialize time management: {}", e);
|
||||
panic!("Time management initialization failed");
|
||||
}
|
||||
info!("Time management initialized");
|
||||
|
||||
// Display system information
|
||||
crate::test_init::display_system_info();
|
||||
|
||||
// Run basic functionality tests
|
||||
if let Err(e) = crate::test_init::run_basic_tests() {
|
||||
error!("Basic functionality tests failed: {}", e);
|
||||
panic!("Basic tests failed");
|
||||
// Print a heartbeat every ~1 million iterations
|
||||
if counter % 1_000_000 == 0 {
|
||||
crate::console::write_str(".");
|
||||
}
|
||||
|
||||
// Run initialization tests
|
||||
if let Err(e) = crate::test_init::run_init_tests() {
|
||||
error!("Initialization tests failed: {}", e);
|
||||
panic!("Initialization tests failed");
|
||||
unsafe {
|
||||
core::arch::asm!("hlt");
|
||||
}
|
||||
|
||||
// Initialize drivers
|
||||
if let Err(e) = crate::drivers_init::init_drivers() {
|
||||
error!("Failed to initialize drivers: {}", e);
|
||||
panic!("Driver initialization failed");
|
||||
}
|
||||
info!("Drivers initialized");
|
||||
|
||||
// Initialize kernel threads
|
||||
if let Err(e) = crate::kthread::init_kthreads() {
|
||||
error!("Failed to initialize kernel threads: {}", e);
|
||||
panic!("Kernel thread initialization failed");
|
||||
}
|
||||
info!("Kernel threads initialized");
|
||||
|
||||
// Initialize kernel shell
|
||||
if let Err(e) = crate::shell::init_shell() {
|
||||
error!("Failed to initialize kernel shell: {}", e);
|
||||
panic!("Shell initialization failed");
|
||||
}
|
||||
info!("Kernel shell initialized");
|
||||
|
||||
// Initialize basic networking
|
||||
if let Err(e) = crate::net_basic::init_networking() {
|
||||
error!("Failed to initialize networking: {}", e);
|
||||
panic!("Networking initialization failed");
|
||||
}
|
||||
info!("Basic networking initialized");
|
||||
|
||||
// Initialize module loading system
|
||||
if let Err(e) = crate::module_loader::init_modules() {
|
||||
error!("Failed to initialize module system: {}", e);
|
||||
panic!("Module system initialization failed");
|
||||
}
|
||||
info!("Module system initialized");
|
||||
|
||||
// Initialize in-memory file system
|
||||
if let Err(e) = crate::memfs::init_memfs() {
|
||||
error!("Failed to initialize file system: {}", e);
|
||||
panic!("File system initialization failed");
|
||||
}
|
||||
info!("In-memory file system initialized");
|
||||
|
||||
// Initialize test suite
|
||||
if let Err(e) = crate::test_suite::init() {
|
||||
error!("Failed to initialize test suite: {}", e);
|
||||
panic!("Test suite initialization failed");
|
||||
}
|
||||
info!("Test suite initialized");
|
||||
|
||||
// Initialize user mode support
|
||||
if let Err(e) = crate::usermode::init_usermode() {
|
||||
error!("Failed to initialize user mode: {}", e);
|
||||
panic!("User mode initialization failed");
|
||||
}
|
||||
info!("User mode support initialized");
|
||||
|
||||
// Initialize performance monitoring
|
||||
if let Err(e) = crate::perf::init_perf_monitor() {
|
||||
error!("Failed to initialize performance monitoring: {}", e);
|
||||
panic!("Performance monitoring initialization failed");
|
||||
}
|
||||
info!("Performance monitoring initialized");
|
||||
|
||||
// Initialize advanced logging system
|
||||
if let Err(e) = crate::logging::init_logging() {
|
||||
error!("Failed to initialize logging system: {}", e);
|
||||
panic!("Logging system initialization failed");
|
||||
}
|
||||
info!("Advanced logging system initialized");
|
||||
|
||||
// Initialize system information collection
|
||||
if let Err(e) = crate::sysinfo::init_sysinfo() {
|
||||
error!("Failed to initialize system information: {}", e);
|
||||
panic!("System information initialization failed");
|
||||
}
|
||||
info!("System information collection initialized");
|
||||
|
||||
// Initialize system diagnostics
|
||||
if let Err(e) = crate::diagnostics::init_diagnostics() {
|
||||
error!("Failed to initialize system diagnostics: {}", e);
|
||||
panic!("System diagnostics initialization failed");
|
||||
}
|
||||
info!("System diagnostics initialized");
|
||||
|
||||
// Initialize working task management
|
||||
if let Err(e) = crate::working_task::init_task_management() {
|
||||
error!("Failed to initialize task management: {}", e);
|
||||
panic!("Task management initialization failed");
|
||||
}
|
||||
info!("Task management initialized");
|
||||
|
||||
// Start kernel threads
|
||||
start_kernel_threads();
|
||||
|
||||
info!("Kernel initialization completed");
|
||||
info!("Starting main kernel loop");
|
||||
|
||||
// Start the main kernel loop
|
||||
main_kernel_loop();
|
||||
}
|
||||
|
||||
/// Start essential kernel threads
|
||||
|
||||
@@ -18,7 +18,13 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
// Include boot assembly
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// global_asm!(include_str!("arch/x86_64/boot.s"), options(att_syntax));
|
||||
|
||||
pub mod advanced_perf; // Advanced performance monitoring and profiling
|
||||
pub mod arch;
|
||||
pub mod arp;
|
||||
pub mod benchmark; // Performance benchmarking
|
||||
pub mod boot;
|
||||
pub mod console;
|
||||
@@ -32,6 +38,7 @@ pub mod enhanced_scheduler; // Enhanced preemptive scheduler
|
||||
pub mod error;
|
||||
pub mod fs;
|
||||
pub mod hardware; // Hardware detection and initialization
|
||||
pub mod icmp;
|
||||
pub mod init;
|
||||
pub mod interrupt;
|
||||
pub mod ipc; // Inter-process communication
|
||||
@@ -41,11 +48,7 @@ pub mod memfs; // In-memory file system
|
||||
pub mod memory;
|
||||
pub mod module;
|
||||
pub mod module_loader; // Dynamic module loading
|
||||
pub mod net_basic; // Basic networking support
|
||||
pub mod network;
|
||||
pub mod network_stub; // Basic network stub
|
||||
// pub mod network_advanced; // Advanced networking stack (removed for now)
|
||||
pub mod advanced_perf; // Advanced performance monitoring and profiling
|
||||
pub mod panic;
|
||||
pub mod perf; // Performance monitoring
|
||||
pub mod prelude;
|
||||
@@ -84,6 +87,7 @@ pub extern "C" fn kernel_main() -> ! {
|
||||
|
||||
// Now we can use allocations, continue with full initialization
|
||||
init::early_init();
|
||||
|
||||
init::main_init();
|
||||
|
||||
// Should not return from main_init
|
||||
@@ -94,7 +98,7 @@ pub extern "C" fn kernel_main() -> ! {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn kernel_main_multiboot(multiboot_magic: u32, multiboot_addr: u32) -> ! {
|
||||
// Verify multiboot magic number
|
||||
if multiboot_magic != 0x36d76289 {
|
||||
if multiboot_magic != 0x36d76289 && multiboot_magic != 0x2BADB002 {
|
||||
panic!("Invalid multiboot magic: 0x{:x}", multiboot_magic);
|
||||
}
|
||||
|
||||
@@ -113,24 +117,21 @@ fn early_kernel_init() {
|
||||
loop {}
|
||||
}
|
||||
|
||||
info!("Rust Kernel v{} starting...", VERSION);
|
||||
info!("Early kernel initialization");
|
||||
crate::console::write_str("\n");
|
||||
crate::console::write_str("Booting Rust Kernel...\n");
|
||||
}
|
||||
|
||||
/// Initialize memory management using multiboot information
|
||||
fn memory_init() -> Result<(), error::Error> {
|
||||
if let Some(multiboot_addr) = boot::get_boot_info().multiboot_addr {
|
||||
boot::multiboot::init_memory_from_multiboot(multiboot_addr)?;
|
||||
} else {
|
||||
// Fallback: initialize with default memory layout
|
||||
crate::console::write_str("[*] Initializing memory subsystem...\n");
|
||||
|
||||
// FIXME: Multiboot parsing causes crashes - use default memory layout for now
|
||||
memory::page::init()?;
|
||||
}
|
||||
|
||||
// Initialize heap allocator
|
||||
memory::kmalloc::init()?;
|
||||
memory::vmalloc::init()?;
|
||||
|
||||
info!("Memory management initialized");
|
||||
crate::console::write_str("[+] Memory subsystem ready\n");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
29
kernel/src/main.rs
Archivo normal
29
kernel/src/main.rs
Archivo normal
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Kernel main entry point
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate kernel;
|
||||
|
||||
use core::arch::global_asm;
|
||||
|
||||
// Include boot assembly
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
global_asm!(include_str!("arch/x86_64/boot.s"), options(att_syntax));
|
||||
|
||||
/// Entry point called by boot.s assembly code
|
||||
/// This is just a wrapper to ensure the kernel crate is linked
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> ! {
|
||||
// This function shouldn't be called directly if boot.s calls kernel_main_multiboot
|
||||
// But if it is called, we redirect to the kernel library
|
||||
loop {
|
||||
unsafe {
|
||||
core::arch::asm!("hlt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Panic handler is defined in the kernel library
|
||||
@@ -15,11 +15,10 @@ const KMALLOC_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096
|
||||
const MAX_KMALLOC_SIZE: usize = 4096;
|
||||
|
||||
/// Slab allocator for small kernel allocations
|
||||
/// Uses indices instead of raw pointers for thread safety
|
||||
/// Uses physical addresses directly - they're identity-mapped in the first 1GB
|
||||
struct SlabAllocator {
|
||||
size_classes: BTreeMap<usize, Vec<usize>>, // Store offsets instead of pointers
|
||||
allocated_blocks: BTreeMap<usize, usize>, // Maps offsets to size classes
|
||||
base_addr: usize, // Base address for calculations
|
||||
size_classes: BTreeMap<usize, Vec<usize>>, // Store physical addresses
|
||||
allocated_blocks: BTreeMap<usize, usize>, // Maps physical addresses to size classes
|
||||
}
|
||||
|
||||
impl SlabAllocator {
|
||||
@@ -27,14 +26,9 @@ impl SlabAllocator {
|
||||
Self {
|
||||
size_classes: BTreeMap::new(),
|
||||
allocated_blocks: BTreeMap::new(),
|
||||
base_addr: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&mut self, base_addr: usize) {
|
||||
self.base_addr = base_addr;
|
||||
}
|
||||
|
||||
fn allocate(&mut self, size: usize) -> Result<*mut u8> {
|
||||
// Find appropriate size class
|
||||
let size_class = KMALLOC_SIZES
|
||||
@@ -49,9 +43,9 @@ impl SlabAllocator {
|
||||
|
||||
// Try to get from free list
|
||||
if let Some(free_list) = self.size_classes.get_mut(&size_class) {
|
||||
if let Some(offset) = free_list.pop() {
|
||||
self.allocated_blocks.insert(offset, size_class);
|
||||
return Ok((self.base_addr + offset) as *mut u8);
|
||||
if let Some(addr) = free_list.pop() {
|
||||
self.allocated_blocks.insert(addr, size_class);
|
||||
return Ok(addr as *mut u8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,37 +57,58 @@ impl SlabAllocator {
|
||||
// Allocate a page using buddy allocator
|
||||
let pfn = alloc_pages(0, GfpFlags::KERNEL)?;
|
||||
let page_addr = pfn.to_phys_addr().as_usize();
|
||||
let offset = page_addr - self.base_addr;
|
||||
|
||||
// Split page into blocks of size_class
|
||||
let blocks_per_page = 4096 / size_class;
|
||||
let free_list = self.size_classes.entry(size_class).or_insert_with(Vec::new);
|
||||
|
||||
for i in 1..blocks_per_page {
|
||||
let block_offset = offset + (i * size_class);
|
||||
free_list.push(block_offset);
|
||||
let block_addr = page_addr + (i * size_class);
|
||||
free_list.push(block_addr);
|
||||
}
|
||||
|
||||
// Return the first block
|
||||
self.allocated_blocks.insert(offset, size_class);
|
||||
self.allocated_blocks.insert(page_addr, size_class);
|
||||
Ok(page_addr as *mut u8)
|
||||
}
|
||||
|
||||
fn deallocate(&mut self, ptr: *mut u8) -> Result<()> {
|
||||
let offset = (ptr as usize).saturating_sub(self.base_addr);
|
||||
if let Some(size_class) = self.allocated_blocks.remove(&offset) {
|
||||
let addr = ptr as usize;
|
||||
if let Some(size_class) = self.allocated_blocks.remove(&addr) {
|
||||
let free_list =
|
||||
self.size_classes.entry(size_class).or_insert_with(Vec::new);
|
||||
free_list.push(offset);
|
||||
free_list.push(addr);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> (usize, usize, usize) {
|
||||
let mut allocated_count = 0;
|
||||
let mut allocated_bytes = 0;
|
||||
for (_, size_class) in &self.allocated_blocks {
|
||||
allocated_count += 1;
|
||||
allocated_bytes += size_class;
|
||||
}
|
||||
|
||||
let mut free_count = 0;
|
||||
for (_, free_list) in &self.size_classes {
|
||||
free_count += free_list.len();
|
||||
}
|
||||
|
||||
(allocated_count, allocated_bytes, free_count)
|
||||
}
|
||||
}
|
||||
|
||||
static SLAB_ALLOCATOR: Spinlock<SlabAllocator> = Spinlock::new(SlabAllocator::new());
|
||||
|
||||
/// Get kmalloc statistics
|
||||
pub fn get_stats() -> (usize, usize, usize) {
|
||||
let allocator = SLAB_ALLOCATOR.lock();
|
||||
allocator.stats()
|
||||
}
|
||||
|
||||
/// Allocate kernel memory
|
||||
pub fn kmalloc(size: usize) -> Result<*mut u8> {
|
||||
// Increment performance counter
|
||||
@@ -170,8 +185,6 @@ pub fn krealloc(ptr: *mut u8, old_size: usize, new_size: usize) -> Result<*mut u
|
||||
|
||||
/// Initialize the slab allocator
|
||||
pub fn init() -> Result<()> {
|
||||
let mut allocator = SLAB_ALLOCATOR.lock();
|
||||
// Use a reasonable base address for offset calculations
|
||||
allocator.init(0x_4000_0000_0000);
|
||||
// No initialization needed - we use physical addresses directly
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
//! Page frame allocator
|
||||
|
||||
use alloc::collections::BTreeSet;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -98,34 +97,71 @@ pub static PAGE_ALLOCATOR: Spinlock<PageAllocator> = Spinlock::new(PageAllocator
|
||||
|
||||
/// Page allocator implementation
|
||||
pub struct PageAllocator {
|
||||
free_pages: BTreeSet<Pfn>,
|
||||
free_list_head: Option<PhysAddr>,
|
||||
total_pages: usize,
|
||||
allocated_pages: usize,
|
||||
free_count: usize,
|
||||
}
|
||||
|
||||
impl PageAllocator {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
free_pages: BTreeSet::new(),
|
||||
free_list_head: None,
|
||||
total_pages: 0,
|
||||
allocated_pages: 0,
|
||||
free_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a range of pages to the free list
|
||||
pub fn add_free_range(&mut self, start: Pfn, count: usize) {
|
||||
for i in 0..count {
|
||||
self.free_pages.insert(Pfn(start.0 + i));
|
||||
// Safety: Only add pages that are within the identity-mapped region (0-1GB)
|
||||
// Boot assembly maps 0-1GB with 2MB pages
|
||||
const MAX_IDENTITY_MAPPED_PFN: usize = (1024 * 1024 * 1024) / 4096; // 1GB / 4KB
|
||||
|
||||
let safe_count = if start.0 >= MAX_IDENTITY_MAPPED_PFN {
|
||||
// Start is beyond identity mapping, skip entirely
|
||||
return;
|
||||
} else if start.0 + count > MAX_IDENTITY_MAPPED_PFN {
|
||||
// Trim to stay within identity mapping
|
||||
MAX_IDENTITY_MAPPED_PFN - start.0
|
||||
} else {
|
||||
count
|
||||
};
|
||||
|
||||
for i in 0..safe_count {
|
||||
let pfn = Pfn(start.0 + i);
|
||||
let phys_addr = PhysAddr(pfn.0 * 4096);
|
||||
|
||||
// Store current head in the new page
|
||||
// We can write to phys_addr because it's identity mapped
|
||||
unsafe {
|
||||
let ptr = phys_addr.0 as *mut u64;
|
||||
*ptr = self.free_list_head.map(|a| a.0 as u64).unwrap_or(0);
|
||||
}
|
||||
self.total_pages += count;
|
||||
|
||||
// Update head
|
||||
self.free_list_head = Some(phys_addr);
|
||||
}
|
||||
self.total_pages += safe_count;
|
||||
self.free_count += safe_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);
|
||||
if let Some(head_addr) = self.free_list_head {
|
||||
// Read next ptr from head
|
||||
let next_addr_u64 = unsafe { *(head_addr.0 as *const u64) };
|
||||
|
||||
self.free_list_head = if next_addr_u64 == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(PhysAddr(next_addr_u64 as usize))
|
||||
};
|
||||
|
||||
self.allocated_pages += 1;
|
||||
Ok(pfn)
|
||||
self.free_count -= 1;
|
||||
Ok(Pfn(head_addr.0 / 4096))
|
||||
} else {
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
@@ -133,32 +169,25 @@ impl PageAllocator {
|
||||
|
||||
/// Free a single page
|
||||
fn free_page(&mut self, pfn: Pfn) {
|
||||
if self.free_pages.insert(pfn) {
|
||||
self.allocated_pages -= 1;
|
||||
let phys_addr = PhysAddr(pfn.0 * 4096);
|
||||
unsafe {
|
||||
let ptr = phys_addr.0 as *mut u64;
|
||||
*ptr = self.free_list_head.map(|a| a.0 as u64).unwrap_or(0);
|
||||
}
|
||||
self.free_list_head = Some(phys_addr);
|
||||
self.allocated_pages -= 1;
|
||||
self.free_count += 1;
|
||||
}
|
||||
|
||||
/// Get statistics
|
||||
fn stats(&self) -> (usize, usize, usize) {
|
||||
(
|
||||
self.total_pages,
|
||||
self.allocated_pages,
|
||||
self.free_pages.len(),
|
||||
)
|
||||
(self.total_pages, self.allocated_pages, self.free_count)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
||||
// Page allocator stub - no actual pages initialized yet
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -128,10 +128,24 @@ impl VmallocAllocator {
|
||||
self.next_addr = addr + size;
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
pub fn stats(&self) -> (usize, usize) {
|
||||
let mut allocated_bytes = 0;
|
||||
for (_, area) in &self.areas {
|
||||
allocated_bytes += area.size;
|
||||
}
|
||||
(self.areas.len(), allocated_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
static VMALLOC_ALLOCATOR: Spinlock<VmallocAllocator> = Spinlock::new(VmallocAllocator::new());
|
||||
|
||||
/// Get vmalloc statistics
|
||||
pub fn get_stats() -> (usize, usize) {
|
||||
let allocator = VMALLOC_ALLOCATOR.lock();
|
||||
allocator.stats()
|
||||
}
|
||||
|
||||
/// Allocate virtual memory
|
||||
pub fn vmalloc(size: usize) -> Result<VirtAddr> {
|
||||
let mut allocator = VMALLOC_ALLOCATOR.lock();
|
||||
@@ -157,6 +171,45 @@ pub fn vzalloc(size: usize) -> Result<VirtAddr> {
|
||||
}
|
||||
|
||||
/// Map physical memory into virtual space
|
||||
pub fn vmap_phys(phys_addr: PhysAddr, size: usize) -> Result<VirtAddr> {
|
||||
let start_addr;
|
||||
let aligned_size;
|
||||
{
|
||||
let mut allocator = VMALLOC_ALLOCATOR.lock();
|
||||
if allocator.page_table.is_none() {
|
||||
return Err(Error::NotInitialized);
|
||||
}
|
||||
aligned_size = (size + 4095) & !4095;
|
||||
start_addr = allocator.find_free_area(aligned_size)?;
|
||||
}
|
||||
|
||||
let mut allocator = VMALLOC_ALLOCATOR.lock();
|
||||
let page_table = allocator.page_table.as_mut().unwrap();
|
||||
|
||||
// Map virtual to physical pages
|
||||
let pages_needed = aligned_size / 4096;
|
||||
for i in 0..pages_needed {
|
||||
let virt_addr = VirtAddr::new(start_addr + i * 4096);
|
||||
let phys_addr = PhysAddr::new(phys_addr.as_usize() + i * 4096);
|
||||
page_table.map_page(
|
||||
virt_addr,
|
||||
phys_addr,
|
||||
PageTableFlags::kernel_page() | PageTableFlags::NO_EXECUTE,
|
||||
)?;
|
||||
}
|
||||
|
||||
let end_addr = start_addr + aligned_size;
|
||||
let area = VmallocArea {
|
||||
start: VirtAddr::new(start_addr),
|
||||
end: VirtAddr::new(end_addr),
|
||||
size: aligned_size,
|
||||
pages: alloc::vec![], // We don't own these pages
|
||||
};
|
||||
|
||||
allocator.areas.insert(start_addr, area);
|
||||
Ok(VirtAddr::new(start_addr))
|
||||
}
|
||||
|
||||
pub fn vmap(pages: &[PhysAddr], count: usize) -> Result<VirtAddr> {
|
||||
let size = count * 4096;
|
||||
let mut allocator = VMALLOC_ALLOCATOR.lock();
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Basic networking support - loopback interface
|
||||
|
||||
use alloc::{collections::VecDeque, vec::Vec};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::sync::Spinlock;
|
||||
use crate::{info, warn};
|
||||
|
||||
/// Network packet
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetPacket {
|
||||
pub data: Vec<u8>,
|
||||
pub length: usize,
|
||||
pub protocol: u16,
|
||||
}
|
||||
|
||||
impl NetPacket {
|
||||
pub fn new(data: Vec<u8>, protocol: u16) -> Self {
|
||||
let length = data.len();
|
||||
Self {
|
||||
data,
|
||||
length,
|
||||
protocol,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Network interface
|
||||
#[derive(Debug)]
|
||||
pub struct NetInterface {
|
||||
pub name: &'static str,
|
||||
pub mtu: usize,
|
||||
pub tx_queue: VecDeque<NetPacket>,
|
||||
pub rx_queue: VecDeque<NetPacket>,
|
||||
pub stats: NetStats,
|
||||
}
|
||||
|
||||
/// Network statistics
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NetStats {
|
||||
pub tx_packets: u64,
|
||||
pub rx_packets: u64,
|
||||
pub tx_bytes: u64,
|
||||
pub rx_bytes: u64,
|
||||
pub tx_errors: u64,
|
||||
pub rx_errors: u64,
|
||||
}
|
||||
|
||||
impl NetInterface {
|
||||
pub fn new(name: &'static str, mtu: usize) -> Self {
|
||||
Self {
|
||||
name,
|
||||
mtu,
|
||||
tx_queue: VecDeque::new(),
|
||||
rx_queue: VecDeque::new(),
|
||||
stats: NetStats::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a packet
|
||||
pub fn send_packet(&mut self, packet: NetPacket) -> Result<()> {
|
||||
if packet.length > self.mtu {
|
||||
self.stats.tx_errors += 1;
|
||||
return Err(crate::error::Error::InvalidArgument);
|
||||
}
|
||||
|
||||
self.tx_queue.push_back(packet.clone());
|
||||
self.stats.tx_packets += 1;
|
||||
self.stats.tx_bytes += packet.length as u64;
|
||||
|
||||
// For loopback, immediately receive the packet
|
||||
if self.name == "lo" {
|
||||
let rx_length = packet.length;
|
||||
self.rx_queue.push_back(packet);
|
||||
self.stats.rx_packets += 1;
|
||||
self.stats.rx_bytes += rx_length as u64;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive a packet
|
||||
pub fn receive_packet(&mut self) -> Option<NetPacket> {
|
||||
self.rx_queue.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
/// Network subsystem
|
||||
static NETWORK: Spinlock<NetworkSubsystem> = Spinlock::new(NetworkSubsystem::new());
|
||||
|
||||
struct NetworkSubsystem {
|
||||
interfaces: Vec<NetInterface>,
|
||||
}
|
||||
|
||||
impl NetworkSubsystem {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
interfaces: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_interface(&mut self, interface: NetInterface) {
|
||||
info!("Adding network interface: {}", interface.name);
|
||||
self.interfaces.push(interface);
|
||||
}
|
||||
|
||||
fn get_interface_mut(&mut self, name: &str) -> Option<&mut NetInterface> {
|
||||
self.interfaces.iter_mut().find(|iface| iface.name == name)
|
||||
}
|
||||
|
||||
fn get_interface(&self, name: &str) -> Option<&NetInterface> {
|
||||
self.interfaces.iter().find(|iface| iface.name == name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize basic networking
|
||||
pub fn init_networking() -> Result<()> {
|
||||
info!("Initializing network subsystem");
|
||||
|
||||
let mut network = NETWORK.lock();
|
||||
|
||||
// Create loopback interface
|
||||
let loopback = NetInterface::new("lo", 65536);
|
||||
network.add_interface(loopback);
|
||||
|
||||
info!("Network subsystem initialized");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a packet on an interface
|
||||
pub fn net_send(interface_name: &str, data: Vec<u8>, protocol: u16) -> Result<()> {
|
||||
let packet = NetPacket::new(data, protocol);
|
||||
let mut network = NETWORK.lock();
|
||||
|
||||
if let Some(interface) = network.get_interface_mut(interface_name) {
|
||||
interface.send_packet(packet)?;
|
||||
info!("Sent packet on interface {}", interface_name);
|
||||
Ok(())
|
||||
} else {
|
||||
warn!("Network interface not found: {}", interface_name);
|
||||
Err(crate::error::Error::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a packet from an interface
|
||||
pub fn net_receive(interface_name: &str) -> Option<NetPacket> {
|
||||
let mut network = NETWORK.lock();
|
||||
|
||||
if let Some(interface) = network.get_interface_mut(interface_name) {
|
||||
interface.receive_packet()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get network statistics
|
||||
pub fn get_net_stats(interface_name: &str) -> Option<NetStats> {
|
||||
let network = NETWORK.lock();
|
||||
|
||||
if let Some(interface) = network.get_interface(interface_name) {
|
||||
Some(NetStats {
|
||||
tx_packets: interface.stats.tx_packets,
|
||||
rx_packets: interface.stats.rx_packets,
|
||||
tx_bytes: interface.stats.tx_bytes,
|
||||
rx_bytes: interface.stats.rx_bytes,
|
||||
tx_errors: interface.stats.tx_errors,
|
||||
rx_errors: interface.stats.rx_errors,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Test networking functionality
|
||||
pub fn test_networking() -> Result<()> {
|
||||
info!("Testing network functionality");
|
||||
|
||||
// Test loopback
|
||||
let test_data = b"Hello, network!".to_vec();
|
||||
net_send("lo", test_data.clone(), 0x0800)?; // IP protocol
|
||||
|
||||
if let Some(packet) = net_receive("lo") {
|
||||
if packet.data == test_data {
|
||||
info!("Loopback test passed");
|
||||
} else {
|
||||
warn!("Loopback test failed - data mismatch");
|
||||
return Err(crate::error::Error::Generic);
|
||||
}
|
||||
} else {
|
||||
warn!("Loopback test failed - no packet received");
|
||||
return Err(crate::error::Error::Generic);
|
||||
}
|
||||
|
||||
// Display statistics
|
||||
if let Some(stats) = get_net_stats("lo") {
|
||||
info!(
|
||||
"Loopback stats: TX: {} packets/{} bytes, RX: {} packets/{} bytes",
|
||||
stats.tx_packets, stats.tx_bytes, stats.rx_packets, stats.rx_bytes
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2,7 +2,13 @@
|
||||
|
||||
//! Network stack implementation
|
||||
|
||||
use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec};
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeMap,
|
||||
collections::VecDeque,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::fmt;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
@@ -219,6 +225,7 @@ impl NetworkBuffer {
|
||||
/// Network interface
|
||||
pub trait NetworkInterface: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
fn ip_address(&self) -> Option<Ipv4Address>;
|
||||
fn mac_address(&self) -> MacAddress;
|
||||
fn mtu(&self) -> u16;
|
||||
fn is_up(&self) -> bool;
|
||||
@@ -241,12 +248,82 @@ pub struct InterfaceStats {
|
||||
pub dropped: u64,
|
||||
}
|
||||
|
||||
/// A loopback network interface.
|
||||
#[derive(Debug)]
|
||||
pub struct LoopbackInterface {
|
||||
rx_queue: VecDeque<NetworkBuffer>,
|
||||
up: bool,
|
||||
}
|
||||
|
||||
impl LoopbackInterface {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
rx_queue: VecDeque::new(),
|
||||
up: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkInterface for LoopbackInterface {
|
||||
fn name(&self) -> &str {
|
||||
"lo"
|
||||
}
|
||||
|
||||
fn ip_address(&self) -> Option<Ipv4Address> {
|
||||
Some(Ipv4Address::localhost())
|
||||
}
|
||||
|
||||
fn mac_address(&self) -> MacAddress {
|
||||
MacAddress::zero()
|
||||
}
|
||||
|
||||
fn mtu(&self) -> u16 {
|
||||
65535
|
||||
}
|
||||
|
||||
fn is_up(&self) -> bool {
|
||||
self.up
|
||||
}
|
||||
|
||||
fn send_packet(&mut self, buffer: &NetworkBuffer) -> Result<()> {
|
||||
if !self.up {
|
||||
return Err(Error::NetworkDown);
|
||||
}
|
||||
self.rx_queue.push_back(buffer.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn receive_packet(&mut self) -> Result<Option<NetworkBuffer>> {
|
||||
if !self.up {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(self.rx_queue.pop_front())
|
||||
}
|
||||
|
||||
fn set_up(&mut self, up: bool) -> Result<()> {
|
||||
self.up = up;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_mac_address(&mut self, _mac: MacAddress) -> Result<()> {
|
||||
// The loopback interface doesn't have a real MAC address.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Network stack
|
||||
struct PendingArpRequest {
|
||||
packet: NetworkBuffer,
|
||||
ip: Ipv4Address,
|
||||
timestamp: u64,
|
||||
}
|
||||
|
||||
pub struct NetworkStack {
|
||||
interfaces: BTreeMap<String, Box<dyn NetworkInterface>>,
|
||||
interface_stats: BTreeMap<String, InterfaceStats>,
|
||||
routing_table: Vec<RouteEntry>,
|
||||
arp_table: BTreeMap<Ipv4Address, MacAddress>,
|
||||
pending_arp_requests: Vec<PendingArpRequest>,
|
||||
}
|
||||
|
||||
/// Routing table entry
|
||||
@@ -266,6 +343,7 @@ impl NetworkStack {
|
||||
interface_stats: BTreeMap::new(),
|
||||
routing_table: Vec::new(),
|
||||
arp_table: BTreeMap::new(),
|
||||
pending_arp_requests: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,6 +410,11 @@ impl NetworkStack {
|
||||
data: &[u8],
|
||||
protocol: ProtocolType,
|
||||
) -> Result<()> {
|
||||
// Clean up timed out ARP requests
|
||||
let now = crate::time::get_time_ns();
|
||||
self.pending_arp_requests
|
||||
.retain(|req| now - req.timestamp < 10_000_000_000); // 10 seconds
|
||||
|
||||
// Find route (borrow self immutably)
|
||||
let route = {
|
||||
let route = self.find_route(dest).ok_or(Error::NetworkUnreachable)?;
|
||||
@@ -340,9 +423,46 @@ impl NetworkStack {
|
||||
|
||||
// Look up MAC address first (borrow self immutably)
|
||||
let dest_mac = if let Some(gateway) = route.gateway {
|
||||
self.lookup_arp(gateway).ok_or(Error::NetworkUnreachable)?
|
||||
self.lookup_arp(gateway)
|
||||
} else {
|
||||
self.lookup_arp(dest).ok_or(Error::NetworkUnreachable)?
|
||||
self.lookup_arp(dest)
|
||||
};
|
||||
|
||||
let dest_mac = if let Some(mac) = dest_mac {
|
||||
mac
|
||||
} else {
|
||||
// ARP lookup failed, send an ARP request and queue the packet
|
||||
let interface = self
|
||||
.get_interface(&route.interface)
|
||||
.ok_or(Error::DeviceNotFound)?;
|
||||
let arp_request = crate::arp::ArpPacket::new(
|
||||
crate::arp::ArpOperation::Request,
|
||||
interface.mac_address(),
|
||||
interface.ip_address().unwrap_or(Ipv4Address::any()),
|
||||
MacAddress::zero(),
|
||||
dest,
|
||||
);
|
||||
let mut buffer = NetworkBuffer::new(28);
|
||||
buffer.set_protocol(ProtocolType::ARP);
|
||||
buffer.set_mac_addresses(interface.mac_address(), MacAddress::broadcast());
|
||||
buffer.extend_from_slice(&arp_request.to_bytes())?;
|
||||
let interface_mut = self
|
||||
.get_interface_mut(&route.interface)
|
||||
.ok_or(Error::DeviceNotFound)?;
|
||||
interface_mut.send_packet(&buffer)?;
|
||||
|
||||
// Queue the original packet
|
||||
let mut packet_to_queue = NetworkBuffer::new(data.len());
|
||||
packet_to_queue.extend_from_slice(data)?;
|
||||
packet_to_queue.set_protocol(protocol);
|
||||
packet_to_queue.set_ip_addresses(Ipv4Address::any(), dest); // TODO: Set source IP
|
||||
self.pending_arp_requests.push(PendingArpRequest {
|
||||
packet: packet_to_queue,
|
||||
ip: dest,
|
||||
timestamp: crate::time::get_time_ns(),
|
||||
});
|
||||
|
||||
return Ok(()); // We'll have to wait for the reply
|
||||
};
|
||||
|
||||
// Get interface MAC address
|
||||
@@ -376,25 +496,126 @@ impl NetworkStack {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn receive_packets(&mut self) -> Result<Vec<NetworkBuffer>> {
|
||||
let mut packets = Vec::new();
|
||||
pub fn receive_and_handle_packets(&mut self) -> Result<Vec<NetworkBuffer>> {
|
||||
let mut received_packets = Vec::new();
|
||||
let mut unhandled_packets = Vec::new();
|
||||
|
||||
// First, receive all packets from all interfaces
|
||||
for (name, interface) in &mut self.interfaces {
|
||||
while let Some(packet) = interface.receive_packet()? {
|
||||
if let Some(stats) = self.interface_stats.get_mut(name) {
|
||||
stats.packets_received += 1;
|
||||
stats.bytes_received += packet.len() as u64;
|
||||
}
|
||||
packets.push(packet);
|
||||
received_packets.push((name.clone(), packet));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(packets)
|
||||
// Now, process the received packets
|
||||
for (interface_name, packet) in received_packets {
|
||||
if packet.protocol == ProtocolType::ARP {
|
||||
if let Ok(arp_packet) =
|
||||
crate::arp::ArpPacket::from_bytes(packet.data())
|
||||
{
|
||||
self.handle_arp_packet(&arp_packet, &interface_name)?;
|
||||
}
|
||||
} else if packet.protocol == ProtocolType::ICMP {
|
||||
if let Some(source_ip) = packet.source_ip {
|
||||
self.handle_icmp_packet(source_ip, packet.data())?;
|
||||
}
|
||||
} else {
|
||||
unhandled_packets.push(packet);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(unhandled_packets)
|
||||
}
|
||||
|
||||
pub fn receive_packets(&mut self) -> Result<Vec<NetworkBuffer>> {
|
||||
self.receive_and_handle_packets()
|
||||
}
|
||||
|
||||
pub fn get_interface_stats(&self, name: &str) -> Option<&InterfaceStats> {
|
||||
self.interface_stats.get(name)
|
||||
}
|
||||
|
||||
fn handle_arp_packet(
|
||||
&mut self,
|
||||
packet: &crate::arp::ArpPacket,
|
||||
interface_name: &str,
|
||||
) -> Result<()> {
|
||||
// Add the sender to the ARP table
|
||||
self.add_arp_entry(packet.spa, packet.sha);
|
||||
|
||||
// If it's a request for us, send a reply
|
||||
if u16::from_be_bytes(packet.oper) == crate::arp::ArpOperation::Request as u16 {
|
||||
if let Some(interface) = self.get_interface(interface_name) {
|
||||
if let Some(ip_addr) = interface.ip_address() {
|
||||
if ip_addr == packet.tpa {
|
||||
let reply = crate::arp::ArpPacket::new(
|
||||
crate::arp::ArpOperation::Reply,
|
||||
interface.mac_address(),
|
||||
ip_addr,
|
||||
packet.sha,
|
||||
packet.spa,
|
||||
);
|
||||
let mut buffer = NetworkBuffer::new(28);
|
||||
buffer.set_protocol(ProtocolType::ARP);
|
||||
buffer.set_mac_addresses(
|
||||
interface.mac_address(),
|
||||
packet.sha,
|
||||
);
|
||||
buffer.extend_from_slice(&reply.to_bytes())?;
|
||||
if let Some(interface) =
|
||||
self.get_interface_mut(interface_name)
|
||||
{
|
||||
interface.send_packet(&buffer)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for pending packets
|
||||
let mut packets_to_send = Vec::new();
|
||||
let mut still_pending = Vec::new();
|
||||
for pending in self.pending_arp_requests.drain(..) {
|
||||
if pending.ip == packet.spa {
|
||||
packets_to_send.push(pending);
|
||||
} else {
|
||||
still_pending.push(pending);
|
||||
}
|
||||
}
|
||||
self.pending_arp_requests = still_pending;
|
||||
|
||||
for pending in packets_to_send {
|
||||
self.send_packet(
|
||||
pending.ip,
|
||||
pending.packet.data(),
|
||||
pending.packet.protocol,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_icmp_packet(&mut self, source_ip: Ipv4Address, packet: &[u8]) -> Result<()> {
|
||||
if packet.len() < 8 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let icmp_type = packet[0];
|
||||
if icmp_type == crate::icmp::IcmpType::EchoRequest as u8 {
|
||||
let mut reply = packet.to_vec();
|
||||
reply[0] = crate::icmp::IcmpType::EchoReply as u8;
|
||||
// Recalculate checksum
|
||||
let checksum = utils::calculate_checksum(&reply);
|
||||
reply[2] = (checksum >> 8) as u8;
|
||||
reply[3] = (checksum & 0xFF) as u8;
|
||||
|
||||
self.send_packet(source_ip, &reply, ProtocolType::ICMP)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Global network stack
|
||||
@@ -403,7 +624,25 @@ pub static NETWORK_STACK: Spinlock<Option<NetworkStack>> = Spinlock::new(None);
|
||||
/// Initialize network stack
|
||||
pub fn init() -> Result<()> {
|
||||
let mut stack = NETWORK_STACK.lock();
|
||||
*stack = Some(NetworkStack::new());
|
||||
let mut network_stack = NetworkStack::new();
|
||||
|
||||
// Add loopback interface
|
||||
let loopback = LoopbackInterface::new();
|
||||
network_stack.add_interface("lo".to_string(), Box::new(loopback));
|
||||
|
||||
// Add route for loopback
|
||||
network_stack.add_route(RouteEntry {
|
||||
destination: Ipv4Address::new(127, 0, 0, 0),
|
||||
netmask: Ipv4Address::new(255, 0, 0, 0),
|
||||
gateway: None,
|
||||
interface: "lo".to_string(),
|
||||
metric: 0,
|
||||
});
|
||||
|
||||
// Add ARP entry for loopback
|
||||
network_stack.add_arp_entry(Ipv4Address::localhost(), MacAddress::zero());
|
||||
|
||||
*stack = Some(network_stack);
|
||||
crate::info!("Network stack initialized");
|
||||
Ok(())
|
||||
}
|
||||
@@ -419,6 +658,30 @@ pub fn add_network_interface(name: String, interface: Box<dyn NetworkInterface>)
|
||||
}
|
||||
}
|
||||
|
||||
pub mod utils {
|
||||
/// Calculate checksum
|
||||
pub fn calculate_checksum(data: &[u8]) -> u16 {
|
||||
let mut sum = 0u32;
|
||||
|
||||
// Sum all 16-bit words
|
||||
for chunk in data.chunks(2) {
|
||||
if chunk.len() == 2 {
|
||||
sum += ((chunk[0] as u32) << 8) + (chunk[1] as u32);
|
||||
} else {
|
||||
sum += (chunk[0] as u32) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Add carry
|
||||
while sum >> 16 != 0 {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
}
|
||||
|
||||
// One's complement
|
||||
!sum as u16
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a packet
|
||||
pub fn send_packet(dest: Ipv4Address, data: &[u8], protocol: ProtocolType) -> Result<()> {
|
||||
let mut stack_opt = NETWORK_STACK.lock();
|
||||
|
||||
@@ -1,373 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Advanced networking stack implementation
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use alloc::vec::Vec;
|
||||
use alloc::string::String;
|
||||
use alloc::collections::BTreeMap;
|
||||
use spin::Mutex;
|
||||
use core::sync::atomic::{AtomicU64, AtomicU32, Ordering};
|
||||
|
||||
/// Network interface statistics
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct NetStats {
|
||||
pub rx_packets: u64,
|
||||
pub tx_packets: u64,
|
||||
pub rx_bytes: u64,
|
||||
pub tx_bytes: u64,
|
||||
pub rx_errors: u64,
|
||||
pub tx_errors: u64,
|
||||
pub rx_dropped: u64,
|
||||
pub tx_dropped: u64,
|
||||
}
|
||||
|
||||
/// Network interface configuration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetConfig {
|
||||
pub name: String,
|
||||
pub mac_address: [u8; 6],
|
||||
pub ip_address: [u8; 4],
|
||||
pub netmask: [u8; 4],
|
||||
pub gateway: [u8; 4],
|
||||
pub mtu: u16,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
/// Network packet
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetPacket {
|
||||
pub data: Vec<u8>,
|
||||
pub length: usize,
|
||||
pub interface: String,
|
||||
pub timestamp: u64,
|
||||
}
|
||||
|
||||
/// Network interface
|
||||
pub struct NetworkInterface {
|
||||
pub config: NetConfig,
|
||||
pub stats: Mutex<NetStats>,
|
||||
pub rx_queue: Mutex<Vec<NetPacket>>,
|
||||
pub tx_queue: Mutex<Vec<NetPacket>>,
|
||||
}
|
||||
|
||||
impl NetworkInterface {
|
||||
pub fn new(config: NetConfig) -> Self {
|
||||
Self {
|
||||
config,
|
||||
stats: Mutex::new(NetStats::default()),
|
||||
rx_queue: Mutex::new(Vec::new()),
|
||||
tx_queue: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a packet
|
||||
pub fn send_packet(&self, data: &[u8]) -> Result<()> {
|
||||
let packet = NetPacket {
|
||||
data: data.to_vec(),
|
||||
length: data.len(),
|
||||
interface: self.config.name.clone(),
|
||||
timestamp: crate::time::get_time_ns(),
|
||||
};
|
||||
|
||||
let mut tx_queue = self.tx_queue.lock();
|
||||
tx_queue.push(packet);
|
||||
|
||||
let mut stats = self.stats.lock();
|
||||
stats.tx_packets += 1;
|
||||
stats.tx_bytes += data.len() as u64;
|
||||
|
||||
crate::info!("Packet sent on interface {}: {} bytes", self.config.name, data.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Receive a packet
|
||||
pub fn receive_packet(&self) -> Option<NetPacket> {
|
||||
let mut rx_queue = self.rx_queue.lock();
|
||||
if let Some(packet) = rx_queue.pop() {
|
||||
let mut stats = self.stats.lock();
|
||||
stats.rx_packets += 1;
|
||||
stats.rx_bytes += packet.length as u64;
|
||||
Some(packet)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interface statistics
|
||||
pub fn get_stats(&self) -> NetStats {
|
||||
self.stats.lock().clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Network stack
|
||||
pub struct NetworkStack {
|
||||
interfaces: Mutex<BTreeMap<String, NetworkInterface>>,
|
||||
routing_table: Mutex<Vec<Route>>,
|
||||
arp_table: Mutex<BTreeMap<[u8; 4], [u8; 6]>>,
|
||||
}
|
||||
|
||||
/// Routing table entry
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Route {
|
||||
pub destination: [u8; 4],
|
||||
pub netmask: [u8; 4],
|
||||
pub gateway: [u8; 4],
|
||||
pub interface: String,
|
||||
pub metric: u32,
|
||||
}
|
||||
|
||||
impl NetworkStack {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
interfaces: Mutex::new(BTreeMap::new()),
|
||||
routing_table: Mutex::new(Vec::new()),
|
||||
arp_table: Mutex::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add network interface
|
||||
pub fn add_interface(&self, interface: NetworkInterface) -> Result<()> {
|
||||
let name = interface.config.name.clone();
|
||||
let mut interfaces = self.interfaces.lock();
|
||||
interfaces.insert(name.clone(), interface);
|
||||
|
||||
crate::info!("Network interface {} added", name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove network interface
|
||||
pub fn remove_interface(&self, name: &str) -> Result<()> {
|
||||
let mut interfaces = self.interfaces.lock();
|
||||
if interfaces.remove(name).is_some() {
|
||||
crate::info!("Network interface {} removed", name);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ENODEV)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get interface by name
|
||||
pub fn get_interface(&self, name: &str) -> Option<NetworkInterface> {
|
||||
let interfaces = self.interfaces.lock();
|
||||
interfaces.get(name).cloned()
|
||||
}
|
||||
|
||||
/// List all interfaces
|
||||
pub fn list_interfaces(&self) -> Vec<String> {
|
||||
let interfaces = self.interfaces.lock();
|
||||
interfaces.keys().cloned().collect()
|
||||
}
|
||||
|
||||
/// Add route
|
||||
pub fn add_route(&self, route: Route) -> Result<()> {
|
||||
let mut routing_table = self.routing_table.lock();
|
||||
routing_table.push(route);
|
||||
|
||||
crate::info!("Route added to routing table");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Find route for destination
|
||||
pub fn find_route(&self, destination: [u8; 4]) -> Option<Route> {
|
||||
let routing_table = self.routing_table.lock();
|
||||
|
||||
// Simple routing - find exact match first, then default route
|
||||
for route in routing_table.iter() {
|
||||
if Self::ip_matches(&destination, &route.destination, &route.netmask) {
|
||||
return Some(route.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Look for default route (0.0.0.0/0)
|
||||
for route in routing_table.iter() {
|
||||
if route.destination == [0, 0, 0, 0] && route.netmask == [0, 0, 0, 0] {
|
||||
return Some(route.clone());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Check if IP matches network
|
||||
fn ip_matches(ip: &[u8; 4], network: &[u8; 4], netmask: &[u8; 4]) -> bool {
|
||||
for i in 0..4 {
|
||||
if (ip[i] & netmask[i]) != (network[i] & netmask[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Add ARP entry
|
||||
pub fn add_arp_entry(&self, ip: [u8; 4], mac: [u8; 6]) -> Result<()> {
|
||||
let mut arp_table = self.arp_table.lock();
|
||||
arp_table.insert(ip, mac);
|
||||
|
||||
crate::info!("ARP entry added: {:?} -> {:?}", ip, mac);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lookup MAC address for IP
|
||||
pub fn arp_lookup(&self, ip: [u8; 4]) -> Option<[u8; 6]> {
|
||||
let arp_table = self.arp_table.lock();
|
||||
arp_table.get(&ip).copied()
|
||||
}
|
||||
|
||||
/// Send packet to destination
|
||||
pub fn send_to(&self, destination: [u8; 4], data: &[u8]) -> Result<()> {
|
||||
// Find route
|
||||
let route = self.find_route(destination)
|
||||
.ok_or(Error::EHOSTUNREACH)?;
|
||||
|
||||
// Get interface
|
||||
let interfaces = self.interfaces.lock();
|
||||
let interface = interfaces.get(&route.interface)
|
||||
.ok_or(Error::ENODEV)?;
|
||||
|
||||
// For now, just send on the interface
|
||||
interface.send_packet(data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get network statistics
|
||||
pub fn get_network_stats(&self) -> Vec<(String, NetStats)> {
|
||||
let interfaces = self.interfaces.lock();
|
||||
interfaces.iter()
|
||||
.map(|(name, iface)| (name.clone(), iface.get_stats()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Global network stack instance
|
||||
static NETWORK_STACK: Mutex<Option<NetworkStack>> = Mutex::new(None);
|
||||
|
||||
/// Initialize networking stack
|
||||
pub fn init() -> Result<()> {
|
||||
let stack = NetworkStack::new();
|
||||
|
||||
// Create loopback interface
|
||||
let loopback_config = NetConfig {
|
||||
name: "lo".to_string(),
|
||||
mac_address: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
ip_address: [127, 0, 0, 1],
|
||||
netmask: [255, 0, 0, 0],
|
||||
gateway: [0, 0, 0, 0],
|
||||
mtu: 65536,
|
||||
flags: 0x1, // IFF_UP
|
||||
};
|
||||
|
||||
let loopback = NetworkInterface::new(loopback_config);
|
||||
stack.add_interface(loopback)?;
|
||||
|
||||
// Add loopback route
|
||||
let loopback_route = Route {
|
||||
destination: [127, 0, 0, 0],
|
||||
netmask: [255, 0, 0, 0],
|
||||
gateway: [0, 0, 0, 0],
|
||||
interface: "lo".to_string(),
|
||||
metric: 0,
|
||||
};
|
||||
stack.add_route(loopback_route)?;
|
||||
|
||||
*NETWORK_STACK.lock() = Some(stack);
|
||||
|
||||
crate::info!("Advanced networking stack initialized");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get global network stack
|
||||
pub fn get_network_stack() -> Result<&'static Mutex<Option<NetworkStack>>> {
|
||||
Ok(&NETWORK_STACK)
|
||||
}
|
||||
|
||||
/// Network utility functions
|
||||
pub mod utils {
|
||||
use super::*;
|
||||
|
||||
/// Format IP address as string
|
||||
pub fn ip_to_string(ip: [u8; 4]) -> String {
|
||||
format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
|
||||
}
|
||||
|
||||
/// Parse IP address from string
|
||||
pub fn string_to_ip(s: &str) -> Result<[u8; 4]> {
|
||||
let parts: Vec<&str> = s.split('.').collect();
|
||||
if parts.len() != 4 {
|
||||
return Err(Error::EINVAL);
|
||||
}
|
||||
|
||||
let mut ip = [0u8; 4];
|
||||
for (i, part) in parts.iter().enumerate() {
|
||||
ip[i] = part.parse().map_err(|_| Error::EINVAL)?;
|
||||
}
|
||||
|
||||
Ok(ip)
|
||||
}
|
||||
|
||||
/// Format MAC address as string
|
||||
pub fn mac_to_string(mac: [u8; 6]) -> String {
|
||||
format!("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])
|
||||
}
|
||||
|
||||
/// Calculate checksum
|
||||
pub fn calculate_checksum(data: &[u8]) -> u16 {
|
||||
let mut sum = 0u32;
|
||||
|
||||
// Sum all 16-bit words
|
||||
for chunk in data.chunks(2) {
|
||||
if chunk.len() == 2 {
|
||||
sum += ((chunk[0] as u32) << 8) + (chunk[1] as u32);
|
||||
} else {
|
||||
sum += (chunk[0] as u32) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Add carry
|
||||
while sum >> 16 != 0 {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
}
|
||||
|
||||
// One's complement
|
||||
!sum as u16
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple packet creation utilities
|
||||
pub mod packet {
|
||||
use super::*;
|
||||
|
||||
/// Create a simple test packet
|
||||
pub fn create_test_packet(size: usize) -> Vec<u8> {
|
||||
let mut data = Vec::with_capacity(size);
|
||||
for i in 0..size {
|
||||
data.push((i % 256) as u8);
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
/// Create ICMP ping packet
|
||||
pub fn create_ping_packet(id: u16, seq: u16, data: &[u8]) -> Vec<u8> {
|
||||
let mut packet = Vec::new();
|
||||
|
||||
// ICMP header
|
||||
packet.push(8); // Type: Echo Request
|
||||
packet.push(0); // Code: 0
|
||||
packet.push(0); // Checksum (will be calculated)
|
||||
packet.push(0);
|
||||
packet.extend_from_slice(&id.to_be_bytes());
|
||||
packet.extend_from_slice(&seq.to_be_bytes());
|
||||
|
||||
// Data
|
||||
packet.extend_from_slice(data);
|
||||
|
||||
// Calculate checksum
|
||||
let checksum = utils::calculate_checksum(&packet);
|
||||
packet[2] = (checksum >> 8) as u8;
|
||||
packet[3] = (checksum & 0xFF) as u8;
|
||||
|
||||
packet
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Network stub for basic functionality
|
||||
|
||||
use alloc::string::{String, ToString};
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
/// Initialize basic networking
|
||||
pub fn init() -> Result<()> {
|
||||
crate::info!("Network stub initialized");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get network status
|
||||
pub fn get_network_status() -> String {
|
||||
"Network: Basic stub - No interfaces configured".to_string()
|
||||
}
|
||||
@@ -251,6 +251,29 @@ impl ProcessTable {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_two_threads_mut(
|
||||
&mut self,
|
||||
tid1: Tid,
|
||||
tid2: Tid,
|
||||
) -> (Option<&mut Thread>, Option<&mut Thread>) {
|
||||
if tid1 == tid2 {
|
||||
let t = self.find_thread_mut(tid1);
|
||||
return (t, None);
|
||||
}
|
||||
|
||||
// This is a bit inefficient but safe
|
||||
// We can't easily return two mutable references to the same structure
|
||||
// But since they are in different processes or different threads, they are distinct memory locations.
|
||||
// We can use unsafe to cheat the borrow checker, knowing that tid1 != tid2.
|
||||
|
||||
let ptr = self as *mut ProcessTable;
|
||||
unsafe {
|
||||
let t1 = (*ptr).find_thread_mut(tid1);
|
||||
let t2 = (*ptr).find_thread_mut(tid2);
|
||||
(t1, t2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a new PID
|
||||
@@ -279,6 +302,20 @@ pub fn create_process(name: String, uid: Uid, gid: Gid) -> Result<Pid> {
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
/// Add a thread to the kernel process (PID 0)
|
||||
pub fn add_kernel_thread(tid: Tid, context: Context, stack_pointer: VirtAddr) -> Result<()> {
|
||||
let mut table = PROCESS_TABLE.lock();
|
||||
if let Some(process) = table.get_process_mut(Pid(0)) {
|
||||
let mut thread = Thread::new(tid, Pid(0), 0);
|
||||
thread.context = context;
|
||||
thread.stack_pointer = stack_pointer;
|
||||
process.add_thread(thread);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get current process PID
|
||||
pub fn current_process_pid() -> Option<Pid> {
|
||||
let table = PROCESS_TABLE.lock();
|
||||
|
||||
@@ -458,32 +458,39 @@ impl Scheduler {
|
||||
if let Some(current_tid) = self.current {
|
||||
if current_tid != tid {
|
||||
// Look up current and next threads
|
||||
let process_table = PROCESS_TABLE.lock();
|
||||
if let (Some(current_thread), Some(next_thread)) = (
|
||||
process_table.find_thread(current_tid),
|
||||
process_table.find_thread(tid),
|
||||
) {
|
||||
// We need to use a scope to ensure the lock is dropped before switching
|
||||
let (current_ctx_ptr, next_ctx_ptr) = {
|
||||
let mut process_table = PROCESS_TABLE.lock();
|
||||
|
||||
let (current_thread, next_thread) = process_table
|
||||
.find_two_threads_mut(current_tid, tid);
|
||||
|
||||
let current_ptr = if let Some(t) = current_thread {
|
||||
&mut t.context as *mut Context
|
||||
} else {
|
||||
return; // Current thread not found?
|
||||
};
|
||||
|
||||
let next_ptr = if let Some(t) = next_thread {
|
||||
&t.context as *const Context
|
||||
} else {
|
||||
return; // Next thread not found
|
||||
};
|
||||
|
||||
(current_ptr, next_ptr)
|
||||
};
|
||||
|
||||
// Update scheduler state
|
||||
self.current = Some(tid);
|
||||
self.nr_switches += 1;
|
||||
|
||||
// Drop the lock before context switch to avoid deadlock
|
||||
drop(process_table);
|
||||
|
||||
// TODO: Implement actual context switch
|
||||
// This would involve:
|
||||
// 1. Saving current thread's context
|
||||
// 2. Loading next thread's context
|
||||
// 3. Switching page tables if different processes
|
||||
// 4. Updating stack pointer and instruction pointer
|
||||
|
||||
crate::info!(
|
||||
"Context switch from TID {} to TID {}",
|
||||
current_tid.0,
|
||||
tid.0
|
||||
);
|
||||
return;
|
||||
// Perform the context switch
|
||||
// SAFETY: We have valid pointers to the contexts and we've dropped the lock
|
||||
unsafe {
|
||||
switch_context(&mut *current_ctx_ptr, &*next_ctx_ptr);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,3 +661,10 @@ pub fn scheduler_tick() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a manual context switch to a specific task
|
||||
/// This is used by the enhanced scheduler to execute its scheduling decisions
|
||||
pub fn context_switch_to(tid: Tid) {
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
scheduler.switch_to(tid);
|
||||
}
|
||||
|
||||
@@ -231,8 +231,27 @@ impl KernelShell {
|
||||
info!(" Total pages: {}", total);
|
||||
info!(" Allocated pages: {}", allocated);
|
||||
info!(" Free pages: {}", free);
|
||||
info!(
|
||||
" Memory usage: {} / {} KB",
|
||||
(allocated * 4096) / 1024,
|
||||
(total * 4096) / 1024
|
||||
);
|
||||
|
||||
// TODO: Add more memory statistics (kmalloc, vmalloc, etc.)
|
||||
let (kmalloc_alloc_count, kmalloc_alloc_bytes, kmalloc_free_count) =
|
||||
crate::memory::kmalloc::get_stats();
|
||||
info!("\nKmalloc (slab) statistics:");
|
||||
info!(
|
||||
" Allocated: {} blocks ({} bytes)",
|
||||
kmalloc_alloc_count, kmalloc_alloc_bytes
|
||||
);
|
||||
info!(" Free: {} blocks", kmalloc_free_count);
|
||||
|
||||
let (vmalloc_areas, vmalloc_bytes) = crate::memory::vmalloc::get_stats();
|
||||
info!("\nVmalloc statistics:");
|
||||
info!(
|
||||
" Allocated: {} areas ({} bytes)",
|
||||
vmalloc_areas, vmalloc_bytes
|
||||
);
|
||||
}
|
||||
|
||||
/// Process command
|
||||
@@ -258,49 +277,92 @@ impl KernelShell {
|
||||
|
||||
/// Clear command
|
||||
fn cmd_clear(&self) {
|
||||
// TODO: Clear console screen
|
||||
info!("Clear screen not implemented yet");
|
||||
crate::console::clear();
|
||||
}
|
||||
|
||||
/// Network command
|
||||
fn cmd_network(&self, args: &[&str]) {
|
||||
if args.is_empty() {
|
||||
info!("Network commands: stats, test");
|
||||
info!("Network commands: stats, ping <ip>");
|
||||
return;
|
||||
}
|
||||
|
||||
match args[0] {
|
||||
"stats" => {
|
||||
info!("Network interface statistics:");
|
||||
if let Some(stats) = crate::net_basic::get_net_stats("lo") {
|
||||
info!(" lo (loopback):");
|
||||
let mut stack = crate::network::NETWORK_STACK.lock();
|
||||
if let Some(ref mut stack) = *stack {
|
||||
for iface_name in stack.list_interfaces() {
|
||||
if let Some(stats) =
|
||||
stack.get_interface_stats(&iface_name)
|
||||
{
|
||||
info!(" {}:", iface_name);
|
||||
info!(
|
||||
" TX: {} packets, {} bytes",
|
||||
stats.tx_packets, stats.tx_bytes
|
||||
stats.packets_sent,
|
||||
stats.bytes_sent
|
||||
);
|
||||
info!(
|
||||
" RX: {} packets, {} bytes",
|
||||
stats.rx_packets, stats.rx_bytes
|
||||
stats.packets_received,
|
||||
stats.bytes_received
|
||||
);
|
||||
info!(
|
||||
" Errors: TX {}, RX {}",
|
||||
stats.tx_errors, stats.rx_errors
|
||||
" Errors: {}, Dropped: {}",
|
||||
stats.errors, stats.dropped
|
||||
);
|
||||
} else {
|
||||
warn!("Failed to get loopback statistics");
|
||||
}
|
||||
}
|
||||
"test" => {
|
||||
info!("Running network tests...");
|
||||
if let Err(e) = crate::net_basic::test_networking() {
|
||||
error!("Network tests failed: {}", e);
|
||||
}
|
||||
}
|
||||
"ping" => {
|
||||
if args.len() < 2 {
|
||||
info!("Usage: net ping <ip>");
|
||||
return;
|
||||
}
|
||||
let ip_str = args[1];
|
||||
let parts: Vec<&str> = ip_str.split('.').collect();
|
||||
if parts.len() != 4 {
|
||||
info!("Invalid IP address format");
|
||||
return;
|
||||
}
|
||||
let mut bytes = [0u8; 4];
|
||||
for i in 0..4 {
|
||||
if let Ok(byte) = parts[i].parse() {
|
||||
bytes[i] = byte;
|
||||
} else {
|
||||
info!("Network tests passed!");
|
||||
info!("Invalid IP address format");
|
||||
return;
|
||||
}
|
||||
}
|
||||
let dest_ip = crate::network::Ipv4Address::from_bytes(bytes);
|
||||
|
||||
let mut icmp_packet = crate::icmp::IcmpPacket {
|
||||
icmp_type: crate::icmp::IcmpType::EchoRequest,
|
||||
icmp_code: crate::icmp::IcmpCode::Echo,
|
||||
checksum: 0,
|
||||
identifier: 0,
|
||||
sequence_number: 0,
|
||||
};
|
||||
|
||||
let mut data = icmp_packet.to_bytes();
|
||||
let checksum = crate::network::utils::calculate_checksum(&data);
|
||||
icmp_packet.checksum = checksum;
|
||||
data = icmp_packet.to_bytes();
|
||||
|
||||
if let Err(e) = crate::network::send_packet(
|
||||
dest_ip,
|
||||
&data,
|
||||
crate::network::ProtocolType::ICMP,
|
||||
) {
|
||||
error!("Failed to send ping: {}", e);
|
||||
} else {
|
||||
info!("Ping sent to {}", dest_ip);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
info!(
|
||||
"Unknown network command: {}. Available: stats, test",
|
||||
"Unknown network command: {}. Available: stats, ping",
|
||||
args[0]
|
||||
);
|
||||
}
|
||||
|
||||
41
kernel/src/simple_boot.s
Archivo normal
41
kernel/src/simple_boot.s
Archivo normal
@@ -0,0 +1,41 @@
|
||||
# Simple multiboot2 header and boot code
|
||||
.section .multiboot_header, "a", @progbits
|
||||
.align 8
|
||||
|
||||
multiboot2_header_start:
|
||||
.long 0xe85250d6 # multiboot2 magic
|
||||
.long 0 # architecture (i386)
|
||||
.long multiboot2_header_end - multiboot2_header_start # header length
|
||||
.long -(0xe85250d6 + 0 + (multiboot2_header_end - multiboot2_header_start)) # checksum
|
||||
|
||||
# End tag
|
||||
.word 0 # type
|
||||
.word 0 # flags
|
||||
.long 8 # size
|
||||
multiboot2_header_end:
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
.code32
|
||||
|
||||
_start:
|
||||
# Disable interrupts
|
||||
cli
|
||||
|
||||
# Set up a simple stack
|
||||
mov $stack_top, %esp
|
||||
mov $stack_top, %ebp
|
||||
|
||||
# Call main Rust function
|
||||
call main
|
||||
|
||||
# If main returns, halt
|
||||
halt:
|
||||
hlt
|
||||
jmp halt
|
||||
|
||||
.section .bss
|
||||
.align 16
|
||||
stack_bottom:
|
||||
.skip 16384 # 16KB stack
|
||||
stack_top:
|
||||
@@ -69,7 +69,7 @@ impl CpuInfo {
|
||||
unsafe {
|
||||
asm!("mov {ebx_save}, rbx",
|
||||
"cpuid",
|
||||
"mov {ebx_out}, ebx",
|
||||
"mov {ebx_out:e}, ebx",
|
||||
"mov rbx, {ebx_save}",
|
||||
ebx_save = out(reg) _,
|
||||
ebx_out = out(reg) ebx,
|
||||
@@ -144,7 +144,7 @@ impl CpuInfo {
|
||||
|
||||
asm!("mov {ebx_save}, rbx",
|
||||
"cpuid",
|
||||
"mov {ebx_out}, ebx",
|
||||
"mov {ebx_out:e}, ebx",
|
||||
"mov rbx, {ebx_save}",
|
||||
ebx_save = out(reg) _,
|
||||
ebx_out = out(reg) vendor_ebx,
|
||||
|
||||
@@ -403,8 +403,37 @@ impl TimerWheel {
|
||||
}
|
||||
|
||||
pub fn add_timer(&mut self, timer: HrTimer) {
|
||||
// TODO: Add timer to appropriate level based on expiry time
|
||||
let level = 0; // Simplified
|
||||
let now_ns = get_time_ns();
|
||||
let expires_ns = timer.expires.to_ns();
|
||||
|
||||
// If already expired or expires very soon, put in level 0
|
||||
if expires_ns <= now_ns {
|
||||
self.levels[0].push(timer);
|
||||
return;
|
||||
}
|
||||
|
||||
let delta_ns = expires_ns - now_ns;
|
||||
let delta_jiffies = delta_ns / NSEC_PER_JIFFY;
|
||||
|
||||
// Determine level based on delta
|
||||
// Level 0: Immediate to ~256ms
|
||||
// Level 1: ~256ms to ~16s
|
||||
// Level 2: ~16s to ~17m
|
||||
// Level 3: ~17m to ~18h
|
||||
// Level 4+: Far future
|
||||
let level = if delta_jiffies < 256 {
|
||||
0
|
||||
} else if delta_jiffies < 256 * 64 {
|
||||
1
|
||||
} else if delta_jiffies < 256 * 64 * 64 {
|
||||
2
|
||||
} else if delta_jiffies < 256 * 64 * 64 * 64 {
|
||||
3
|
||||
} else {
|
||||
4
|
||||
};
|
||||
|
||||
let level = core::cmp::min(level, 7);
|
||||
self.levels[level].push(timer);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,9 +93,10 @@ impl TimerState {
|
||||
let mut stats = self.stats.lock();
|
||||
stats.context_switches += 1;
|
||||
|
||||
// TODO: Actual context switching would happen here
|
||||
// This would involve saving current CPU state and
|
||||
// restoring the state of the next task
|
||||
// Perform actual context switch
|
||||
// This will save current CPU state and restore the state of the next task
|
||||
crate::scheduler::context_switch_to(next_tid);
|
||||
|
||||
crate::info!(
|
||||
"Context switch: {:?} -> {:?}",
|
||||
current_tid,
|
||||
|
||||
@@ -50,8 +50,8 @@ impl Task {
|
||||
function: TaskFunction,
|
||||
stack_size: usize,
|
||||
) -> Result<Self> {
|
||||
static NEXT_TID: AtomicU32 = AtomicU32::new(1);
|
||||
let tid = Tid(NEXT_TID.fetch_add(1, Ordering::Relaxed));
|
||||
// Use process subsystem to allocate TID to ensure uniqueness
|
||||
let tid = crate::process::allocate_tid();
|
||||
|
||||
// Allocate stack
|
||||
let stack_ptr = kmalloc::kmalloc(stack_size)?;
|
||||
@@ -156,10 +156,18 @@ impl TaskManager {
|
||||
function: TaskFunction,
|
||||
stack_size: usize,
|
||||
) -> Result<Tid> {
|
||||
let task = Task::new_kernel_task(name, function, stack_size)?;
|
||||
let task = Task::new_kernel_task(name.clone(), function, stack_size)?;
|
||||
let tid = task.tid;
|
||||
|
||||
self.tasks.lock().push(task);
|
||||
// Add to local task list
|
||||
self.tasks.lock().push(task.clone());
|
||||
|
||||
// Add to PROCESS_TABLE so the low-level scheduler can find it
|
||||
crate::process::add_kernel_thread(
|
||||
tid,
|
||||
task.context,
|
||||
crate::memory::VirtAddr::new(task.context.rsp as usize),
|
||||
)?;
|
||||
|
||||
// Add to enhanced scheduler
|
||||
crate::enhanced_scheduler::add_task(
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Rust Kernel
|
||||
//! Rust Kernel Library
|
||||
//!
|
||||
//! A modern kernel implementation in Rust, inspired by the Linux kernel
|
||||
//! and utilizing the Rust for Linux infrastructure.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate kernel;
|
||||
|
||||
// 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]
|
||||
|
||||
19
src/main.rs
Archivo normal
19
src/main.rs
Archivo normal
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
//! Rust Kernel Binary
|
||||
//!
|
||||
//! Main binary entry point for the Rust kernel
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate kernel;
|
||||
|
||||
use core::arch::global_asm;
|
||||
|
||||
// Include boot assembly
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
global_asm!(
|
||||
include_str!("../kernel/src/arch/x86_64/boot.s"),
|
||||
options(att_syntax)
|
||||
);
|
||||
Referencia en una nueva incidencia
Block a user