From eb8069b4942e39786711552b0c72bdefd1e99f2c Mon Sep 17 00:00:00 2001 From: ale Date: Sun, 15 Jun 2025 23:53:38 +0200 Subject: [PATCH] build ok 2 Signed-off-by: ale --- drivers/src/keyboard.rs | 306 ++++++++++++++++++++++++++++++ drivers/src/lib.rs | 2 + drivers/src/serial.rs | 336 +++++++++++++++++++++++++++++++++ kernel/src/device.rs | 2 + kernel/src/driver.rs | 1 + kernel/src/error.rs | 20 ++ kernel/src/fs/dentry.rs | 4 +- kernel/src/fs/inode.rs | 3 +- kernel/src/fs/mod.rs | 5 +- kernel/src/fs/mount.rs | 8 +- kernel/src/fs/operations.rs | 4 +- kernel/src/fs/super_block.rs | 4 +- kernel/src/init.rs | 28 ++- kernel/src/interrupt.rs | 89 ++++----- kernel/src/lib.rs | 2 +- kernel/src/memory/allocator.rs | 195 ++++++++++++++++++- kernel/src/memory/mod.rs | 1 - kernel/src/process.rs | 85 ++++++++- kernel/src/scheduler.rs | 3 +- kernel/src/sync.rs | 4 +- kernel/src/syscalls.rs | 291 ++++++++++++++++++++++++++++ kernel/src/time.rs | 22 ++- 22 files changed, 1325 insertions(+), 90 deletions(-) create mode 100644 drivers/src/keyboard.rs create mode 100644 drivers/src/serial.rs create mode 100644 kernel/src/syscalls.rs diff --git a/drivers/src/keyboard.rs b/drivers/src/keyboard.rs new file mode 100644 index 0000000..2fb2353 --- /dev/null +++ b/drivers/src/keyboard.rs @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! PS/2 Keyboard driver + +use kernel::error::{Error, Result}; +use kernel::interrupt::{register_interrupt_handler, IrqHandler}; +use kernel::device::{Device, DeviceType, CharDevice, FileOperations}; +use kernel::sync::{Spinlock, Arc}; +use kernel::arch::x86_64::port::{inb, outb}; +use alloc::{string::String, vec::Vec, collections::VecDeque}; + +/// PS/2 keyboard controller ports +const KEYBOARD_DATA_PORT: u16 = 0x60; +const KEYBOARD_STATUS_PORT: u16 = 0x64; +const KEYBOARD_COMMAND_PORT: u16 = 0x64; + +/// Keyboard scan codes to ASCII mapping (US layout, simplified) +const SCANCODE_TO_ASCII: [u8; 128] = [ + 0, 27, b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'-', b'=', 8, // backspace + b'\t', b'q', b'w', b'e', b'r', b't', b'y', b'u', b'i', b'o', b'p', b'[', b']', b'\n', // enter + 0, // ctrl + b'a', b's', b'd', b'f', b'g', b'h', b'j', b'k', b'l', b';', b'\'', b'`', + 0, // left shift + b'\\', b'z', b'x', b'c', b'v', b'b', b'n', b'm', b',', b'.', b'/', + 0, // right shift + b'*', 0, // alt + b' ', // space + 0, // caps lock + // Function keys F1-F10 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // num lock + 0, // scroll lock + // Numeric keypad + b'7', b'8', b'9', b'-', b'4', b'5', b'6', b'+', b'1', b'2', b'3', b'0', b'.', + 0, 0, 0, // F11, F12 + // Fill the rest with zeros + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +/// Keyboard state +#[derive(Debug)] +struct KeyboardState { + /// Input buffer for key presses + buffer: VecDeque, + /// Modifier key states + shift_pressed: bool, + ctrl_pressed: bool, + alt_pressed: bool, + caps_lock: bool, +} + +impl KeyboardState { + fn new() -> Self { + Self { + buffer: VecDeque::new(), + shift_pressed: false, + ctrl_pressed: false, + alt_pressed: false, + caps_lock: false, + } + } + + fn push_key(&mut self, key: u8) { + if self.buffer.len() < 256 { // Prevent buffer overflow + self.buffer.push_back(key); + } + } + + fn pop_key(&mut self) -> Option { + self.buffer.pop_front() + } + + fn is_empty(&self) -> bool { + self.buffer.is_empty() + } +} + +/// Global keyboard state +static KEYBOARD_STATE: Spinlock = Spinlock::new(KeyboardState::new()); + +/// Keyboard interrupt handler +#[derive(Debug)] +pub struct KeyboardIrqHandler; + +impl IrqHandler for KeyboardIrqHandler { + fn handle_irq(&self, _irq: u32) -> Result<()> { + // Read scan code from keyboard data port + let scancode = unsafe { inb(KEYBOARD_DATA_PORT) }; + + // Process the scan code + process_scancode(scancode); + + Ok(()) + } +} + +/// Process a keyboard scan code +fn process_scancode(scancode: u8) { + let mut keyboard = KEYBOARD_STATE.lock(); + + // Check if this is a key release (high bit set) + if scancode & 0x80 != 0 { + // Key release + let key_code = scancode & 0x7F; + match key_code { + 0x2A | 0x36 => keyboard.shift_pressed = false, // Shift keys + 0x1D => keyboard.ctrl_pressed = false, // Ctrl key + 0x38 => keyboard.alt_pressed = false, // Alt key + _ => {} // Other key releases + } + return; + } + + // Key press + match scancode { + 0x2A | 0x36 => { // Shift keys + keyboard.shift_pressed = true; + } + 0x1D => { // Ctrl key + keyboard.ctrl_pressed = true; + } + 0x38 => { // Alt key + keyboard.alt_pressed = true; + } + 0x3A => { // Caps Lock + keyboard.caps_lock = !keyboard.caps_lock; + } + _ => { + // Convert scan code to ASCII + if let Some(ascii) = scancode_to_ascii(scancode, &keyboard) { + keyboard.push_key(ascii); + + // Echo to console for now + if ascii.is_ascii_graphic() || ascii == b' ' || ascii == b'\n' { + kernel::console::print_char(ascii as char); + } + } + } + } +} + +/// Convert scan code to ASCII character +fn scancode_to_ascii(scancode: u8, keyboard: &KeyboardState) -> Option { + if scancode as usize >= SCANCODE_TO_ASCII.len() { + return None; + } + + let mut ascii = SCANCODE_TO_ASCII[scancode as usize]; + if ascii == 0 { + return None; + } + + // Apply modifiers + if keyboard.shift_pressed || keyboard.caps_lock { + if ascii >= b'a' && ascii <= b'z' { + ascii = ascii - b'a' + b'A'; + } else { + // Handle shifted symbols + ascii = match ascii { + b'1' => b'!', + b'2' => b'@', + b'3' => b'#', + b'4' => b'$', + b'5' => b'%', + b'6' => b'^', + b'7' => b'&', + b'8' => b'*', + b'9' => b'(', + b'0' => b')', + b'-' => b'_', + b'=' => b'+', + b'[' => b'{', + b']' => b'}', + b'\\' => b'|', + b';' => b':', + b'\'' => b'"', + b',' => b'<', + b'.' => b'>', + b'/' => b'?', + b'`' => b'~', + _ => ascii, + }; + } + } + + // Handle Ctrl combinations + if keyboard.ctrl_pressed && ascii >= b'a' && ascii <= b'z' { + ascii = ascii - b'a' + 1; // Ctrl+A = 1, Ctrl+B = 2, etc. + } else if keyboard.ctrl_pressed && ascii >= b'A' && ascii <= b'Z' { + ascii = ascii - b'A' + 1; + } + + Some(ascii) +} + +/// Keyboard character device file operations +#[derive(Debug)] +pub struct KeyboardFileOps; + +impl FileOperations for KeyboardFileOps { + fn open(&self, _inode: &kernel::device::Inode, _file: &mut kernel::device::File) -> Result<()> { + Ok(()) + } + + fn release(&self, _inode: &kernel::device::Inode, _file: &mut kernel::device::File) -> Result<()> { + Ok(()) + } + + fn read(&self, _file: &mut kernel::device::File, buf: &mut [u8], _offset: u64) -> Result { + let mut keyboard = KEYBOARD_STATE.lock(); + let mut bytes_read = 0; + + // Read available characters from buffer + while bytes_read < buf.len() && !keyboard.is_empty() { + if let Some(key) = keyboard.pop_key() { + buf[bytes_read] = key; + bytes_read += 1; + + // Stop at newline for line-buffered reading + if key == b'\n' { + break; + } + } + } + + Ok(bytes_read) + } + + fn write(&self, _file: &mut kernel::device::File, _buf: &[u8], _offset: u64) -> Result { + // Can't write to keyboard + Err(Error::EPERM) + } + + fn ioctl(&self, _file: &mut kernel::device::File, _cmd: u32, _arg: usize) -> Result { + // TODO: Implement keyboard-specific ioctl commands + Err(Error::ENOTTY) + } + + fn mmap(&self, _file: &mut kernel::device::File, _vma: &mut kernel::memory::VmaArea) -> Result<()> { + // Can't mmap keyboard + Err(Error::ENODEV) + } +} + +/// Initialize the keyboard driver +pub fn init() -> Result<()> { + // Create keyboard device + let keyboard_device = Device::new( + "keyboard".to_string(), + DeviceType::Input, + 10, // Input major number + 0, // Minor number 0 + ); + + // Register character device + let char_dev = CharDevice::new(10, 0, 1, "keyboard".to_string()); + + // Register interrupt handler for keyboard (IRQ 1) + let handler = Arc::new(KeyboardIrqHandler); + register_interrupt_handler(33, handler as Arc)?; // IRQ 1 = INT 33 + + // TODO: Register device in device filesystem + + Ok(()) +} + +/// Read a line from keyboard (blocking) +pub fn read_line() -> String { + let mut line = String::new(); + + loop { + let mut keyboard = KEYBOARD_STATE.lock(); + while let Some(key) = keyboard.pop_key() { + if key == b'\n' { + return line; + } else if key == 8 { // Backspace + if !line.is_empty() { + line.pop(); + // Move cursor back and clear character + kernel::console::print_str("\x08 \x08"); + } + } else if key.is_ascii_graphic() || key == b' ' { + line.push(key as char); + } + } + drop(keyboard); + + // Yield CPU while waiting for input + kernel::scheduler::yield_now(); + } +} + +/// Check if there are pending key presses +pub fn has_pending_input() -> bool { + let keyboard = KEYBOARD_STATE.lock(); + !keyboard.is_empty() +} + +/// Get next character without blocking +pub fn try_read_char() -> Option { + let mut keyboard = KEYBOARD_STATE.lock(); + keyboard.pop_key().map(|k| k as char) +} diff --git a/drivers/src/lib.rs b/drivers/src/lib.rs index dfacbe3..cf4b72d 100644 --- a/drivers/src/lib.rs +++ b/drivers/src/lib.rs @@ -12,6 +12,8 @@ pub mod dummy; pub mod mem; pub mod platform_example; pub mod ramdisk; +pub mod keyboard; // New keyboard driver +pub mod serial; // New serial driver pub use dummy::*; pub use mem::*; diff --git a/drivers/src/serial.rs b/drivers/src/serial.rs new file mode 100644 index 0000000..4b5436c --- /dev/null +++ b/drivers/src/serial.rs @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Serial console driver (16550 UART) + +use kernel::error::{Error, Result}; +use kernel::interrupt::{register_interrupt_handler, IrqHandler}; +use kernel::device::{Device, DeviceType, CharDevice, FileOperations}; +use kernel::sync::{Spinlock, Arc}; +use kernel::arch::x86_64::port::{inb, outb}; +use alloc::{string::String, vec::Vec, collections::VecDeque}; + +/// Standard COM port addresses +const COM1_BASE: u16 = 0x3F8; +const COM2_BASE: u16 = 0x2F8; +const COM3_BASE: u16 = 0x3E8; +const COM4_BASE: u16 = 0x2E8; + +/// UART register offsets +const UART_DATA: u16 = 0; // Data register (R/W) +const UART_IER: u16 = 1; // Interrupt Enable Register +const UART_IIR: u16 = 2; // Interrupt Identification Register (R) +const UART_FCR: u16 = 2; // FIFO Control Register (W) +const UART_LCR: u16 = 3; // Line Control Register +const UART_MCR: u16 = 4; // Modem Control Register +const UART_LSR: u16 = 5; // Line Status Register +const UART_MSR: u16 = 6; // Modem Status Register +const UART_SCR: u16 = 7; // Scratch Register + +/// Line Status Register bits +const LSR_DATA_READY: u8 = 0x01; // Data available +const LSR_OVERRUN_ERROR: u8 = 0x02; // Overrun error +const LSR_PARITY_ERROR: u8 = 0x04; // Parity error +const LSR_FRAMING_ERROR: u8 = 0x08; // Framing error +const LSR_BREAK_INTERRUPT: u8 = 0x10; // Break interrupt +const LSR_THR_EMPTY: u8 = 0x20; // Transmitter holding register empty +const LSR_THR_EMPTY_IDLE: u8 = 0x40; // Transmitter empty and idle +const LSR_FIFO_ERROR: u8 = 0x80; // FIFO error + +/// Serial port structure +#[derive(Debug)] +pub struct SerialPort { + /// Base I/O port address + base: u16, + /// Port name + name: String, + /// Receive buffer + rx_buffer: VecDeque, + /// Transmit buffer + tx_buffer: VecDeque, + /// Port configuration + baudrate: u32, + data_bits: u8, + stop_bits: u8, + parity: Parity, +} + +/// Parity settings +#[derive(Debug, Clone, Copy)] +pub enum Parity { + None, + Odd, + Even, + Mark, + Space, +} + +impl SerialPort { + /// Create a new serial port + pub fn new(base: u16, name: String) -> Self { + Self { + base, + name, + rx_buffer: VecDeque::new(), + tx_buffer: VecDeque::new(), + baudrate: 115200, + data_bits: 8, + stop_bits: 1, + parity: Parity::None, + } + } + + /// Initialize the serial port + pub fn init(&mut self) -> Result<()> { + // Disable interrupts + unsafe { outb(self.base + UART_IER, 0x00); } + + // Set baud rate to 115200 (divisor = 1) + unsafe { + outb(self.base + UART_LCR, 0x80); // Enable DLAB + outb(self.base + UART_DATA, 0x01); // Divisor low byte + outb(self.base + UART_IER, 0x00); // Divisor high byte + } + + // Configure line: 8 data bits, 1 stop bit, no parity + unsafe { outb(self.base + UART_LCR, 0x03); } + + // Enable FIFO, clear them, with 14-byte threshold + unsafe { outb(self.base + UART_FCR, 0xC7); } + + // Enable IRQs, set RTS/DSR, set AUX2 (used for interrupts) + unsafe { outb(self.base + UART_MCR, 0x0B); } + + // Test serial chip (send 0xAE and check if serial returns same byte) + unsafe { + outb(self.base + UART_MCR, 0x1E); // Enable loopback mode + outb(self.base + UART_DATA, 0xAE); // Send test byte + + if inb(self.base + UART_DATA) != 0xAE { + return Err(Error::EIO); + } + + // Disable loopback mode + outb(self.base + UART_MCR, 0x0F); + } + + // Enable interrupts + unsafe { outb(self.base + UART_IER, 0x01); } // Enable receive interrupt + + Ok(()) + } + + /// Check if data is available for reading + pub fn is_receive_ready(&self) -> bool { + unsafe { (inb(self.base + UART_LSR) & LSR_DATA_READY) != 0 } + } + + /// Check if transmitter is ready for data + pub fn is_transmit_ready(&self) -> bool { + unsafe { (inb(self.base + UART_LSR) & LSR_THR_EMPTY) != 0 } + } + + /// Read a byte from the serial port (non-blocking) + pub fn read_byte(&mut self) -> Option { + if !self.rx_buffer.is_empty() { + return self.rx_buffer.pop_front(); + } + + if self.is_receive_ready() { + let byte = unsafe { inb(self.base + UART_DATA) }; + Some(byte) + } else { + None + } + } + + /// Write a byte to the serial port + pub fn write_byte(&mut self, byte: u8) -> Result<()> { + // Wait until transmitter is ready + while !self.is_transmit_ready() { + // Could yield here in a real implementation + } + + unsafe { outb(self.base + UART_DATA, byte); } + Ok(()) + } + + /// Write a string to the serial port + pub fn write_str(&mut self, s: &str) -> Result<()> { + for byte in s.bytes() { + self.write_byte(byte)?; + } + Ok(()) + } + + /// Handle receive interrupt + pub fn handle_receive_interrupt(&mut self) { + while self.is_receive_ready() { + let byte = unsafe { inb(self.base + UART_DATA) }; + if self.rx_buffer.len() < 1024 { // Prevent buffer overflow + self.rx_buffer.push_back(byte); + } + } + } +} + +/// Global serial ports +static COM1: Spinlock> = Spinlock::new(None); + +/// Serial interrupt handler +#[derive(Debug)] +pub struct SerialIrqHandler { + port_base: u16, +} + +impl SerialIrqHandler { + pub fn new(port_base: u16) -> Self { + Self { port_base } + } +} + +impl IrqHandler for SerialIrqHandler { + fn handle_irq(&self, _irq: u32) -> Result<()> { + // Handle COM1 interrupt + if self.port_base == COM1_BASE { + if let Some(ref mut port) = *COM1.lock() { + port.handle_receive_interrupt(); + } + } + + Ok(()) + } +} + +/// Serial console file operations +#[derive(Debug)] +pub struct SerialConsoleOps; + +impl FileOperations for SerialConsoleOps { + fn open(&self, _inode: &kernel::device::Inode, _file: &mut kernel::device::File) -> Result<()> { + Ok(()) + } + + fn release(&self, _inode: &kernel::device::Inode, _file: &mut kernel::device::File) -> Result<()> { + Ok(()) + } + + fn read(&self, _file: &mut kernel::device::File, buf: &mut [u8], _offset: u64) -> Result { + let mut port = COM1.lock(); + if let Some(ref mut serial) = *port { + let mut bytes_read = 0; + + while bytes_read < buf.len() { + if let Some(byte) = serial.read_byte() { + buf[bytes_read] = byte; + bytes_read += 1; + + // Stop at newline + if byte == b'\n' { + break; + } + } else { + break; + } + } + + Ok(bytes_read) + } else { + Err(Error::ENODEV) + } + } + + fn write(&self, _file: &mut kernel::device::File, buf: &[u8], _offset: u64) -> Result { + let mut port = COM1.lock(); + if let Some(ref mut serial) = *port { + for &byte in buf { + serial.write_byte(byte)?; + } + Ok(buf.len()) + } else { + Err(Error::ENODEV) + } + } + + fn ioctl(&self, _file: &mut kernel::device::File, _cmd: u32, _arg: usize) -> Result { + // TODO: Implement serial-specific ioctl commands (baudrate, etc.) + Err(Error::ENOTTY) + } + + fn mmap(&self, _file: &mut kernel::device::File, _vma: &mut kernel::memory::VmaArea) -> Result<()> { + Err(Error::ENODEV) + } +} + +/// Initialize serial console +pub fn init() -> Result<()> { + // Initialize COM1 + let mut com1 = SerialPort::new(COM1_BASE, "ttyS0".to_string()); + com1.init()?; + + *COM1.lock() = Some(com1); + + // Register interrupt handler for COM1 (IRQ 4) + let handler = Arc::new(SerialIrqHandler::new(COM1_BASE)); + register_interrupt_handler(36, handler as Arc)?; // IRQ 4 = INT 36 + + // Create character device + let char_dev = CharDevice::new(4, 64, 1, "ttyS0".to_string()); + + Ok(()) +} + +/// Write to serial console (for kernel debugging) +pub fn serial_print(s: &str) { + let mut port = COM1.lock(); + if let Some(ref mut serial) = *port { + let _ = serial.write_str(s); + } +} + +/// Serial console macros +#[macro_export] +macro_rules! serial_print { + ($($arg:tt)*) => { + $crate::drivers::serial::serial_print(&alloc::format!($($arg)*)); + }; +} + +#[macro_export] +macro_rules! serial_println { + () => ($crate::serial_print!("\n")); + ($($arg:tt)*) => ($crate::serial_print!("{}\n", alloc::format!($($arg)*))); +} + +/// Read a line from serial console +pub fn read_line() -> Result { + let mut line = String::new(); + + loop { + let mut port = COM1.lock(); + if let Some(ref mut serial) = *port { + if let Some(byte) = serial.read_byte() { + if byte == b'\r' || byte == b'\n' { + // Echo newline + let _ = serial.write_byte(b'\n'); + break; + } else if byte == 8 || byte == 127 { // Backspace or DEL + if !line.is_empty() { + line.pop(); + // Echo backspace sequence + let _ = serial.write_str("\x08 \x08"); + } + } else if byte.is_ascii_graphic() || byte == b' ' { + line.push(byte as char); + // Echo character + let _ = serial.write_byte(byte); + } + } + } + drop(port); + + // Yield CPU while waiting + kernel::scheduler::yield_now(); + } + + Ok(line) +} diff --git a/kernel/src/device.rs b/kernel/src/device.rs index 9345e1a..2141ec3 100644 --- a/kernel/src/device.rs +++ b/kernel/src/device.rs @@ -254,10 +254,12 @@ impl DeviceSubsystem { self.devices.remove(name).ok_or(Error::NotFound) } + #[allow(dead_code)] fn find_device(&self, name: &str) -> Option<&Device> { self.devices.get(name) } + #[allow(dead_code)] fn find_device_mut(&mut self, name: &str) -> Option<&mut Device> { self.devices.get_mut(name) } diff --git a/kernel/src/driver.rs b/kernel/src/driver.rs index f09b61a..ef155b3 100644 --- a/kernel/src/driver.rs +++ b/kernel/src/driver.rs @@ -264,6 +264,7 @@ impl DriverSubsystem { } } + #[allow(dead_code)] fn find_driver(&self, name: &str) -> Option<&dyn Driver> { self.drivers.get(name).map(|d| d.as_ref()) } diff --git a/kernel/src/error.rs b/kernel/src/error.rs index 023a130..b8c049c 100644 --- a/kernel/src/error.rs +++ b/kernel/src/error.rs @@ -29,6 +29,12 @@ pub enum Error { Device, /// Generic error Generic, + /// Invalid operation + InvalidOperation, + /// Timeout + Timeout, + /// Not initialized + NotInitialized, // New error variant // Linux-compatible errno values /// Operation not permitted (EPERM) @@ -59,6 +65,10 @@ pub enum Error { EEXIST, /// Directory not empty (ENOTEMPTY) ENOTEMPTY, + /// No child process (ECHILD) + ECHILD, + /// No such process (ESRCH) + ESRCH, } impl Error { @@ -76,6 +86,9 @@ impl Error { Error::WouldBlock => -11, // EAGAIN Error::Device => -19, // ENODEV Error::Generic => -1, // EPERM + Error::InvalidOperation => -1, // EPERM + Error::Timeout => -110, // ETIMEDOUT + Error::NotInitialized => -6, // ENXIO // Linux errno mappings Error::EPERM => -1, // EPERM @@ -92,6 +105,8 @@ impl Error { Error::EISDIR => -21, // EISDIR Error::EEXIST => -17, // EEXIST Error::ENOTEMPTY => -39, // ENOTEMPTY + Error::ECHILD => -10, // ECHILD + Error::ESRCH => -3, // ESRCH } } } @@ -110,6 +125,9 @@ impl fmt::Display for Error { Error::WouldBlock => write!(f, "Resource temporarily unavailable"), Error::Device => write!(f, "Device error"), Error::Generic => write!(f, "Generic error"), + Error::InvalidOperation => write!(f, "Invalid operation"), + Error::Timeout => write!(f, "Operation timed out"), + Error::NotInitialized => write!(f, "Not initialized"), // Linux errno variants Error::EPERM => write!(f, "Operation not permitted"), @@ -126,6 +144,8 @@ impl fmt::Display for Error { Error::EISDIR => write!(f, "Is a directory"), Error::EEXIST => write!(f, "File exists"), Error::ENOTEMPTY => write!(f, "Directory not empty"), + Error::ECHILD => write!(f, "No child processes"), + Error::ESRCH => write!(f, "No such process"), } } } diff --git a/kernel/src/fs/dentry.rs b/kernel/src/fs/dentry.rs index cd2e570..49ee8f2 100644 --- a/kernel/src/fs/dentry.rs +++ b/kernel/src/fs/dentry.rs @@ -2,8 +2,8 @@ //! Directory entry (dentry) abstraction - Linux compatible -use crate::error::{Error, Result}; -use crate::sync::{Arc, Mutex, RwLock}; +use crate::error::Result; +use crate::sync::{Arc, Mutex}; use alloc::{string::String, vec::Vec, format}; // Add format macro use core::sync::atomic::{AtomicU32, Ordering}; diff --git a/kernel/src/fs/inode.rs b/kernel/src/fs/inode.rs index 1d13608..0d9fe92 100644 --- a/kernel/src/fs/inode.rs +++ b/kernel/src/fs/inode.rs @@ -3,8 +3,7 @@ //! Inode abstraction - Linux compatible use crate::error::{Error, Result}; -use crate::types::*; -use crate::sync::{Arc, Mutex, RwLock}; +use crate::sync::{Arc, Mutex}; use crate::device::DeviceNumber; use crate::time::{TimeSpec, get_current_time}; use alloc::string::String; diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index 15ab557..4b184f7 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -18,14 +18,11 @@ pub mod devfs; pub mod mode; // Add mode module use crate::error::{Error, Result}; -use crate::types::*; -use crate::sync::{Arc, Mutex, RwLock}; +use crate::sync::{Arc, Mutex}; use crate::memory::{UserPtr, UserSlicePtr}; -use crate::device::DeviceNumber; use alloc::vec::Vec; use alloc::string::String; use alloc::collections::BTreeMap; -use core::fmt; pub use file::*; pub use inode::*; diff --git a/kernel/src/fs/mount.rs b/kernel/src/fs/mount.rs index 684e737..5dc7f3e 100644 --- a/kernel/src/fs/mount.rs +++ b/kernel/src/fs/mount.rs @@ -3,7 +3,7 @@ //! VFS mount abstraction - Linux compatible use crate::error::{Error, Result}; -use crate::sync::{Arc, Mutex, RwLock}; +use crate::sync::{Arc, Mutex}; use alloc::{string::String, vec::Vec, format}; // Add format macro use core::sync::atomic::{AtomicU32, Ordering}; @@ -215,7 +215,7 @@ pub fn do_mount( let mount = Arc::new(VfsMount::new(sb, dir_name, flags)?); let ns = get_init_ns(); - let mut ns = ns.lock(); + let ns = ns.lock(); ns.add_mount(mount); crate::console::print_info(&format!("Mounted {} on {} (type {})\n", dev_name, dir_name, type_name)); @@ -225,7 +225,7 @@ pub fn do_mount( /// Unmount a filesystem pub fn do_umount(dir_name: &str, flags: u32) -> Result<()> { let ns = get_init_ns(); - let mut ns = ns.lock(); + let ns = ns.lock(); if let Some(mount) = ns.remove_mount(dir_name) { mount.mntput(); @@ -281,7 +281,7 @@ pub fn do_remount(dir_name: &str, flags: u32, data: Option<&str>) -> Result<()> /// Bind mount - create a bind mount pub fn do_bind_mount(old_path: &str, new_path: &str, flags: u32) -> Result<()> { let ns = get_init_ns(); - let mut ns = ns.lock(); + let ns = ns.lock(); if let Some(old_mount) = ns.find_mount(old_path) { let new_mount = Arc::new(VfsMount::new(old_mount.mnt_sb.clone(), new_path, flags | super::super_block::MS_BIND)?); diff --git a/kernel/src/fs/operations.rs b/kernel/src/fs/operations.rs index d564a7b..ab30e6c 100644 --- a/kernel/src/fs/operations.rs +++ b/kernel/src/fs/operations.rs @@ -4,9 +4,7 @@ use crate::error::{Error, Result}; use crate::sync::Arc; -use crate::memory::{UserPtr, UserSlicePtr}; -use alloc::string::String; -use alloc::vec::Vec; +use crate::memory::UserSlicePtr; /// Address space operations trait - similar to Linux address_space_operations pub trait AddressSpaceOperations: Send + Sync { diff --git a/kernel/src/fs/super_block.rs b/kernel/src/fs/super_block.rs index 27c81d0..2e3af3f 100644 --- a/kernel/src/fs/super_block.rs +++ b/kernel/src/fs/super_block.rs @@ -2,8 +2,8 @@ //! Superblock abstraction - Linux compatible -use crate::error::{Error, Result}; -use crate::sync::{Arc, Mutex, RwLock}; +use crate::error::Result; +use crate::sync::{Arc, Mutex}; use crate::device::DeviceNumber; use alloc::string::String; use alloc::vec::Vec; diff --git a/kernel/src/init.rs b/kernel/src/init.rs index ce108ca..2b70b36 100644 --- a/kernel/src/init.rs +++ b/kernel/src/init.rs @@ -2,7 +2,6 @@ //! Kernel initialization -use crate::error::Result; use crate::{info, error}; /// Early kernel initialization @@ -58,6 +57,33 @@ pub fn main_init() -> ! { } 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"); + + // TODO: Initialize drivers + // init_drivers(); + + // TODO: Start kernel threads + // start_kernel_threads(); + info!("Kernel initialization completed"); info!("Starting idle loop"); diff --git a/kernel/src/interrupt.rs b/kernel/src/interrupt.rs index 230bbe9..40ff0e8 100644 --- a/kernel/src/interrupt.rs +++ b/kernel/src/interrupt.rs @@ -4,9 +4,9 @@ use crate::error::{Error, Result}; use crate::sync::Spinlock; -use crate::types::Irq; -use alloc::{vec::Vec, collections::BTreeMap, boxed::Box}; // Add Box import +use alloc::{collections::BTreeMap, boxed::Box}; // Add Box import use core::fmt; +use core::arch::asm; /// IRQ flags - compatible with Linux kernel pub mod irq_flags { @@ -155,6 +155,7 @@ impl InterruptSubsystem { self.descriptors.get_mut(&irq) } + #[allow(dead_code)] fn get_descriptor(&self, irq: u32) -> Option<&IrqDescriptor> { self.descriptors.get(&irq) } @@ -284,8 +285,8 @@ pub fn free_irq(irq: u32, dev_id: *mut u8) -> Result<()> { if let Some(desc) = subsystem.get_descriptor_mut(irq) { // Remove action with matching dev_id - let mut prev: Option<&mut Box> = None; - let mut current = &mut desc.action; + let prev: Option<&mut Box> = None; + let current = &mut desc.action; let mut found = false; // Handle first element specially @@ -365,59 +366,39 @@ pub fn disable_irq(irq: u32) -> Result<()> { } } -/// Handle an interrupt - called from low-level interrupt handlers -pub fn handle_interrupt(irq: u32) { - let mut subsystem = INTERRUPT_SUBSYSTEM.lock(); +/// Register an interrupt handler at a specific vector +pub fn register_interrupt_handler(vector: u32, handler: usize) -> Result<()> { + // TODO: Implement IDT (Interrupt Descriptor Table) setup + // For now, this is a placeholder that would: + // 1. Set up IDT entry for the given vector + // 2. Install the handler function + // 3. Configure interrupt gate type and privilege level - if let Some(desc) = subsystem.get_descriptor_mut(irq) { - desc.irq_count += 1; - - if !desc.is_enabled() { - // Interrupt is disabled, shouldn't happen - crate::warn!("Received disabled interrupt {}", irq); - return; - } - - // Call all handlers in the action chain - let mut current = desc.action.as_ref(); - let mut handled = false; - - while let Some(action) = current { - let result = (action.handler)(irq, action.dev_id.as_ptr()); - match result { - IrqReturn::Handled => { - handled = true; - } - IrqReturn::WakeThread => { - handled = true; - // TODO: Wake threaded interrupt handler - } - IrqReturn::None => { - // Handler didn't handle this interrupt - } - } - current = action.next.as_ref(); - } - - if !handled { - desc.irqs_unhandled += 1; - if desc.irqs_unhandled > 100 { - crate::warn!("Too many unhandled interrupts on IRQ {}", irq); - } - } - } else { - crate::warn!("Spurious interrupt {}", irq); + crate::info!("Registering interrupt handler at vector 0x{:x} -> 0x{:x}", vector, handler); + + // In a real implementation, this would configure the IDT + // For x86_64, this involves setting up interrupt gates in the IDT + Ok(()) +} + +/// System call interrupt handler +#[no_mangle] +pub extern "C" fn syscall_handler() { + // TODO: Get syscall arguments from registers + // TODO: Call syscall dispatcher + // TODO: Return result in register + + // For now, just a placeholder + unsafe { + asm!("iretq", options(noreturn)); } } -/// Get interrupt statistics -pub fn get_irq_stats() -> Vec<(u32, &'static str, u64, u64)> { - let subsystem = INTERRUPT_SUBSYSTEM.lock(); - let mut stats = Vec::new(); +/// Install syscall interrupt handler +pub fn install_syscall_handler() -> Result<()> { + // Install at interrupt vector 0x80 (traditional Linux syscall vector) + register_interrupt_handler(0x80, syscall_handler as usize)?; - for (irq, desc) in &subsystem.descriptors { - stats.push((*irq, desc.name, desc.irq_count, desc.irqs_unhandled)); - } - - stats + // TODO: Also set up SYSCALL/SYSRET for x86_64 + Ok(()) } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 790835e..53b418f 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -36,11 +36,11 @@ pub mod process; pub mod scheduler; pub mod sync; pub mod syscall; +pub mod syscalls; // New syscall infrastructure pub mod task; pub mod time; pub mod types; -use core::panic::PanicInfo; /// Kernel version information pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/kernel/src/memory/allocator.rs b/kernel/src/memory/allocator.rs index 8dcff6b..9ad9a5b 100644 --- a/kernel/src/memory/allocator.rs +++ b/kernel/src/memory/allocator.rs @@ -1,19 +1,153 @@ // SPDX-License-Identifier: GPL-2.0 -//! Heap allocator implementation +//! Memory allocator implementation - Enhanced with buddy allocator use crate::memory::ALLOCATOR; -use crate::types::{VirtAddr, PAGE_SIZE}; -use crate::error::Result; +use crate::types::{VirtAddr, PhysAddr, PAGE_SIZE}; +use crate::error::{Error, Result}; +use crate::sync::Spinlock; +use alloc::vec::Vec; + +/// Maximum order for buddy allocator (2^MAX_ORDER pages) +const MAX_ORDER: usize = 11; + +/// Page frame number +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct PageFrameNumber(pub usize); + +impl PageFrameNumber { + pub fn to_phys_addr(self) -> PhysAddr { + PhysAddr::new(self.0 * PAGE_SIZE) + } + + pub fn from_phys_addr(addr: PhysAddr) -> Self { + Self(addr.as_usize() / PAGE_SIZE) + } +} + +/// Page allocation flags +#[derive(Debug, Clone, Copy)] +pub struct GfpFlags(pub u32); + +impl GfpFlags { + pub const KERNEL: Self = Self(0x01); + pub const USER: Self = Self(0x02); + pub const ATOMIC: Self = Self(0x04); + pub const ZERO: Self = Self(0x08); + pub const DMA: Self = Self(0x10); + pub const HIGHMEM: Self = Self(0x20); +} + +/// Free page block in buddy allocator +#[derive(Debug)] +struct FreeBlock { + pfn: PageFrameNumber, + #[allow(dead_code)] + order: usize, +} + +/// Simple buddy allocator for page allocation +pub struct BuddyAllocator { + /// Free lists for each order + free_lists: [Vec; MAX_ORDER], + /// Total number of pages + total_pages: usize, + /// Base physical address + #[allow(dead_code)] + base_addr: PhysAddr, +} + +impl BuddyAllocator { + pub fn new(base_addr: PhysAddr, total_pages: usize) -> Self { + const EMPTY_VEC: Vec = Vec::new(); + + Self { + free_lists: [EMPTY_VEC; MAX_ORDER], + total_pages, + base_addr, + } + } + + /// Add a free memory region to the allocator + pub fn add_free_region(&mut self, start_pfn: PageFrameNumber, num_pages: usize) { + // Simple implementation: add as single-page blocks + for i in 0..num_pages { + self.free_lists[0].push(FreeBlock { + pfn: PageFrameNumber(start_pfn.0 + i), + order: 0, + }); + } + } + + /// Allocate pages of given order + pub fn alloc_pages(&mut self, order: usize) -> Result { + if order >= MAX_ORDER { + return Err(Error::InvalidArgument); + } + + // Try to find a free block of the requested order + if let Some(block) = self.free_lists[order].pop() { + return Ok(block.pfn); + } + + // For simplicity, just allocate from order 0 if we need higher orders + if order > 0 { + // Try to get multiple single pages (simplified approach) + let pages_needed = 1 << order; + if self.free_lists[0].len() >= pages_needed { + let first_pfn = self.free_lists[0].pop().unwrap().pfn; + // Remove additional pages + for _ in 1..pages_needed { + if self.free_lists[0].is_empty() { + // Put back the first page if we can't get enough + self.free_lists[0].push(FreeBlock { pfn: first_pfn, order: 0 }); + return Err(Error::OutOfMemory); + } + self.free_lists[0].pop(); + } + return Ok(first_pfn); + } + } + + Err(Error::OutOfMemory) + } + + /// Free pages + pub fn free_pages(&mut self, pfn: PageFrameNumber, order: usize) { + if order >= MAX_ORDER { + return; + } + + // Simple implementation: add back to the appropriate order + let pages_to_free = 1 << order; + for i in 0..pages_to_free { + self.free_lists[0].push(FreeBlock { + pfn: PageFrameNumber(pfn.0 + i), + order: 0, + }); + } + } + + /// Get free page count + pub fn free_pages_count(&self) -> usize { + let mut count = 0; + for order in 0..MAX_ORDER { + count += self.free_lists[order].len() * (1 << order); + } + count + } +} + +/// Global buddy allocator for page allocation +static PAGE_ALLOCATOR: Spinlock> = Spinlock::new(None); /// Heap start address (will be set during initialization) static mut HEAP_START: usize = 0; static mut HEAP_SIZE: usize = 0; -/// Initialize the heap allocator +/// Initialize the allocators pub fn init() -> Result<()> { - // TODO: Get heap region from memory map - // For now, use a fixed region + // Initialize heap allocator let heap_start = 0x_4444_4444_0000; let heap_size = 100 * 1024; // 100 KB @@ -23,6 +157,16 @@ pub fn init() -> Result<()> { ALLOCATOR.lock().init(heap_start as *mut u8, heap_size); } + // Initialize page allocator + // For demo purposes, assume we have 1024 pages starting at 1MB + let page_base = PhysAddr::new(0x100000); // 1MB + let total_pages = 1024; + + let mut buddy = BuddyAllocator::new(page_base, total_pages); + buddy.add_free_region(PageFrameNumber(page_base.as_usize() / PAGE_SIZE), total_pages); + + *PAGE_ALLOCATOR.lock() = Some(buddy); + Ok(()) } @@ -30,3 +174,42 @@ pub fn init() -> Result<()> { pub fn heap_stats() -> (usize, usize) { unsafe { (HEAP_START, HEAP_SIZE) } } + +/// Linux-compatible page allocation functions +pub fn alloc_pages(order: usize, flags: GfpFlags) -> Result { + let mut allocator = PAGE_ALLOCATOR.lock(); + if let Some(ref mut alloc) = *allocator { + alloc.alloc_pages(order) + } else { + Err(Error::NotInitialized) + } +} + +pub fn free_pages(pfn: PageFrameNumber, order: usize) { + let mut allocator = PAGE_ALLOCATOR.lock(); + if let Some(ref mut alloc) = *allocator { + alloc.free_pages(pfn, order); + } +} + +/// Allocate a single page +pub fn get_free_page(flags: GfpFlags) -> Result { + let pfn = alloc_pages(0, flags)?; + Ok(VirtAddr::new(pfn.to_phys_addr().as_usize())) +} + +/// Free a single page +pub fn free_page(addr: VirtAddr) { + let pfn = PageFrameNumber::from_phys_addr(PhysAddr::new(addr.as_usize())); + free_pages(pfn, 0); +} + +/// Get page allocator statistics +pub fn page_alloc_stats() -> Option<(usize, usize)> { + let allocator = PAGE_ALLOCATOR.lock(); + if let Some(ref alloc) = *allocator { + Some((alloc.total_pages, alloc.free_pages_count())) + } else { + None + } +} diff --git a/kernel/src/memory/mod.rs b/kernel/src/memory/mod.rs index 2e6eb39..b9396ff 100644 --- a/kernel/src/memory/mod.rs +++ b/kernel/src/memory/mod.rs @@ -12,7 +12,6 @@ pub use page::Page; pub use crate::types::{PhysAddr, VirtAddr, Pfn}; // Re-export from types use crate::error::{Error, Result}; -use core::alloc::{GlobalAlloc, Layout}; use linked_list_allocator::LockedHeap; /// GFP (Get Free Pages) flags - compatible with Linux kernel diff --git a/kernel/src/process.rs b/kernel/src/process.rs index c94693e..ddcb09e 100644 --- a/kernel/src/process.rs +++ b/kernel/src/process.rs @@ -6,7 +6,7 @@ use crate::types::{Pid, Tid, Uid, Gid}; use crate::error::{Error, Result}; use crate::sync::Spinlock; use crate::memory::VirtAddr; -use alloc::{string::String, vec::Vec, collections::BTreeMap}; +use alloc::{string::{String, ToString}, vec::Vec, collections::BTreeMap}; use core::sync::atomic::{AtomicU32, Ordering}; /// Process state - compatible with Linux kernel @@ -71,6 +71,67 @@ impl Process { pub fn is_running(&self) -> bool { self.state == ProcessState::Running } + + /// Fork the current process (create a copy) + pub fn fork(&self) -> Result { + let new_pid = allocate_pid(); + let mut child = self.clone(); + child.pid = new_pid; + child.parent = Some(self.pid); + child.state = ProcessState::Running; + + // TODO: Copy memory space (copy-on-write) + // TODO: Copy file descriptor table + // TODO: Set up new page tables + + Ok(child) + } + + /// Execute a new program in this process + pub fn exec(&mut self, program_path: &str, args: Vec) -> Result<()> { + // TODO: Load program from filesystem + // TODO: Set up new memory layout + // TODO: Initialize stack with arguments + // TODO: Set entry point + + self.name = program_path.to_string(); + Ok(()) + } + + /// Terminate the process with given exit code + pub fn exit(&mut self, exit_code: i32) { + self.state = ProcessState::Zombie; + self.exit_code = exit_code; + + // TODO: Free memory + // TODO: Close file descriptors + // TODO: Notify parent + // TODO: Reparent children to init + } + + /// Send a signal to the process + pub fn send_signal(&mut self, signal: i32) -> Result<()> { + match signal { + 9 => { // SIGKILL + self.state = ProcessState::Dead; + } + 15 => { // SIGTERM + self.signal_pending = true; + // TODO: Add to signal queue + } + _ => { + // TODO: Handle other signals + } + } + Ok(()) + } + + /// Wait for child processes + pub fn wait(&self) -> Result<(Pid, i32)> { + // TODO: Block until child exits + // TODO: Return child PID and exit status + Err(Error::ECHILD) + } } /// Thread structure - Linux-compatible @@ -185,6 +246,7 @@ impl ProcessTable { self.processes.get_mut(&pid) } + #[allow(dead_code)] fn remove_process(&mut self, pid: Pid) -> Option { let process = self.processes.remove(&pid); if self.current_process == Some(pid) { @@ -224,14 +286,24 @@ pub fn create_process(name: String, uid: Uid, gid: Gid) -> Result { Ok(pid) } -/// Get current process -pub fn current_process() -> Option { +/// Get current process PID +pub fn current_process_pid() -> Option { let table = PROCESS_TABLE.lock(); table.current_process } +/// Get current process object +pub fn current_process() -> Option { + let table = PROCESS_TABLE.lock(); + if let Some(pid) = table.current_process { + table.get_process(pid).cloned() + } else { + None + } +} + /// Get process by PID -pub fn get_process(pid: Pid) -> Option { +pub fn find_process(pid: Pid) -> Option { let table = PROCESS_TABLE.lock(); table.get_process(pid).cloned() } @@ -254,6 +326,11 @@ pub fn list_processes() -> Vec { table.list_processes() } +/// Initialize process management +pub fn init_process_management() -> Result<()> { + init() +} + /// Initialize the process subsystem pub fn init() -> Result<()> { // Create kernel process (PID 0) diff --git a/kernel/src/scheduler.rs b/kernel/src/scheduler.rs index 1b54844..93d0e7a 100644 --- a/kernel/src/scheduler.rs +++ b/kernel/src/scheduler.rs @@ -3,8 +3,7 @@ //! Task scheduler compatible with Linux kernel CFS (Completely Fair Scheduler) use crate::error::{Error, Result}; -use crate::process::{Process, Thread, ProcessState, ThreadContext}; -use crate::types::{Pid, Tid, Nanoseconds}; +use crate::types::Tid; use crate::sync::Spinlock; use crate::time; use alloc::{collections::{BTreeMap, VecDeque}, vec::Vec}; diff --git a/kernel/src/sync.rs b/kernel/src/sync.rs index 38efa03..a08d0d2 100644 --- a/kernel/src/sync.rs +++ b/kernel/src/sync.rs @@ -28,7 +28,7 @@ impl Spinlock { } } - pub fn lock(&self) -> SpinlockGuard { + pub fn lock(&self) -> SpinlockGuard<'_, T> { while self.locked.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() { // Busy wait while self.locked.load(Ordering::Relaxed) { @@ -39,7 +39,7 @@ impl Spinlock { SpinlockGuard { lock: self } } - pub fn try_lock(&self) -> Option> { + pub fn try_lock(&self) -> Option> { if self.locked.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok() { Some(SpinlockGuard { lock: self }) } else { diff --git a/kernel/src/syscalls.rs b/kernel/src/syscalls.rs new file mode 100644 index 0000000..5d8f901 --- /dev/null +++ b/kernel/src/syscalls.rs @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! System call interface - Linux compatible + +use crate::error::{Error, Result}; +use crate::types::Pid; +use crate::process::{current_process, find_process, allocate_pid}; + +/// System call numbers (Linux compatible subset) +#[derive(Debug, Clone, Copy)] +#[repr(u64)] +pub enum SyscallNumber { + Read = 0, + Write = 1, + Open = 2, + Close = 3, + Stat = 4, + Fstat = 5, + Lseek = 8, + Mmap = 9, + Munmap = 11, + Brk = 12, + Ioctl = 16, + Access = 21, + Pipe = 22, + Select = 23, + Socket = 41, + Connect = 42, + Accept = 43, + Fork = 57, + Execve = 59, + Exit = 60, + Wait4 = 61, + Kill = 62, + Getpid = 39, + Getppid = 110, + Getuid = 102, + Setuid = 105, + Getgid = 104, + Setgid = 106, + Gettid = 186, + Clone = 56, + Futex = 202, +} + +/// System call arguments structure +#[derive(Debug)] +pub struct SyscallArgs { + pub syscall_num: u64, + pub arg0: u64, + pub arg1: u64, + pub arg2: u64, + pub arg3: u64, + pub arg4: u64, + pub arg5: u64, +} + +/// System call dispatcher +pub fn handle_syscall(args: SyscallArgs) -> u64 { + let result = match args.syscall_num { + // Process management + 57 => sys_fork(), // fork + 59 => sys_execve(args.arg0, args.arg1, args.arg2), // execve + 60 => sys_exit(args.arg0 as i32), // exit + 61 => sys_wait4(args.arg0, args.arg1, args.arg2, args.arg3), // wait4 + 62 => sys_kill(args.arg0 as i32, args.arg1 as i32), // kill + + // Process info + 39 => Ok(sys_getpid() as u64), // getpid + 110 => Ok(sys_getppid() as u64), // getppid + 102 => Ok(sys_getuid() as u64), // getuid + 104 => Ok(sys_getgid() as u64), // getgid + 186 => Ok(sys_gettid() as u64), // gettid + + // File operations + 0 => sys_read(args.arg0 as i32, args.arg1, args.arg2), // read + 1 => sys_write(args.arg0 as i32, args.arg1, args.arg2), // write + 2 => sys_open(args.arg0, args.arg1 as i32, args.arg2 as u32), // open + 3 => sys_close(args.arg0 as i32), // close + + // Memory management + 9 => sys_mmap(args.arg0, args.arg1, args.arg2 as i32, args.arg3 as i32, args.arg4 as i32, args.arg5 as i64), // mmap + 11 => sys_munmap(args.arg0, args.arg1), // munmap + 12 => sys_brk(args.arg0), // brk + + // Unimplemented syscalls + _ => Err(Error::ENOSYS), + }; + + match result { + Ok(value) => value, + Err(error) => (-error.to_errno()) as u64, + } +} + +/// Process management syscalls +pub fn sys_fork() -> Result { + // TODO: Implement fork + // 1. Allocate new PID + // 2. Copy current process + // 3. Set up copy-on-write memory + // 4. Add to scheduler + // 5. Return child PID to parent, 0 to child + + let new_pid = allocate_pid(); + // For now, just return the new PID + Ok(new_pid.0 as u64) +} + +pub fn sys_execve(filename: u64, argv: u64, envp: u64) -> Result { + // TODO: Implement execve + // 1. Load program from filesystem + // 2. Set up new memory layout + // 3. Parse arguments and environment + // 4. Set up initial stack + // 5. Jump to entry point + + Err(Error::ENOSYS) +} + +pub fn sys_exit(exit_code: i32) -> Result { + // TODO: Implement exit + // 1. Set process state to zombie + // 2. Free resources + // 3. Notify parent + // 4. Schedule next process + + // This syscall doesn't return + panic!("Process exit with code {}", exit_code); +} + +pub fn sys_wait4(pid: u64, status: u64, options: u64, rusage: u64) -> Result { + // TODO: Implement wait4 + // 1. Find child process + // 2. Block until child exits + // 3. Return child PID and status + + Err(Error::ECHILD) +} + +pub fn sys_kill(pid: i32, signal: i32) -> Result { + // TODO: Implement kill + // 1. Find target process + // 2. Send signal + // 3. Wake up process if needed + + if let Some(mut process) = find_process(Pid(pid as u32)) { + process.send_signal(signal)?; + Ok(0) + } else { + Err(Error::ESRCH) + } +} + +/// Process info syscalls +pub fn sys_getpid() -> u32 { + current_process().map(|p| p.pid.0).unwrap_or(0) +} + +pub fn sys_getppid() -> u32 { + current_process() + .and_then(|p| p.parent) + .map(|p| p.0) + .unwrap_or(0) +} + +pub fn sys_getuid() -> u32 { + current_process().map(|p| p.uid.0).unwrap_or(0) +} + +pub fn sys_getgid() -> u32 { + current_process().map(|p| p.gid.0).unwrap_or(0) +} + +pub fn sys_gettid() -> u32 { + // For now, return PID (single-threaded processes) + sys_getpid() +} + +/// File operation syscalls +pub fn sys_read(fd: i32, buf: u64, count: u64) -> Result { + // TODO: Implement read + // 1. Get file from file descriptor table + // 2. Read from file + // 3. Copy to user buffer + + Err(Error::ENOSYS) +} + +pub fn sys_write(fd: i32, buf: u64, count: u64) -> Result { + // TODO: Implement write + // 1. Get file from file descriptor table + // 2. Copy from user buffer + // 3. Write to file + + if fd == 1 || fd == 2 { // stdout or stderr + // For now, just return the count as if we wrote to console + Ok(count) + } else { + Err(Error::EBADF) + } +} + +pub fn sys_open(filename: u64, flags: i32, mode: u32) -> Result { + // TODO: Implement open + // 1. Copy filename from user space + // 2. Open file in VFS + // 3. Allocate file descriptor + // 4. Add to process file table + + Err(Error::ENOSYS) +} + +pub fn sys_close(fd: i32) -> Result { + // TODO: Implement close + // 1. Get file from file descriptor table + // 2. Remove from table + // 3. Close file + + Err(Error::ENOSYS) +} + +/// Memory management syscalls +pub fn sys_mmap(addr: u64, length: u64, prot: i32, flags: i32, fd: i32, offset: i64) -> Result { + // TODO: Implement mmap + // 1. Validate parameters + // 2. Find free virtual memory region + // 3. Create VMA + // 4. Set up page tables + // 5. Return mapped address + + Err(Error::ENOSYS) +} + +pub fn sys_munmap(addr: u64, length: u64) -> Result { + // TODO: Implement munmap + // 1. Find VMA containing address + // 2. Unmap pages + // 3. Free physical memory + // 4. Remove VMA + + Err(Error::ENOSYS) +} + +pub fn sys_brk(addr: u64) -> Result { + // TODO: Implement brk + // 1. Get current heap end + // 2. Validate new address + // 3. Expand or shrink heap + // 4. Return new heap end + + Err(Error::ENOSYS) +} + +/// Architecture-specific syscall entry point +#[cfg(target_arch = "x86_64")] +pub mod arch { + use super::*; + + /// x86_64 syscall entry point (called from assembly) + #[no_mangle] + pub extern "C" fn syscall_entry( + syscall_num: u64, + arg0: u64, + arg1: u64, + arg2: u64, + arg3: u64, + arg4: u64, + arg5: u64, + ) -> u64 { + let args = SyscallArgs { + syscall_num, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + }; + + handle_syscall(args) + } +} + +/// Initialize syscall handling +pub fn init_syscalls() -> Result<()> { + // TODO: Set up syscall entry point in IDT/MSR + // For x86_64, this would involve setting up the SYSCALL instruction + + Ok(()) +} diff --git a/kernel/src/time.rs b/kernel/src/time.rs index 9600ef5..e92de16 100644 --- a/kernel/src/time.rs +++ b/kernel/src/time.rs @@ -3,7 +3,7 @@ //! Time management compatible with Linux kernel use crate::error::Result; -use crate::types::{Nanoseconds, Microseconds, Milliseconds, Seconds, Jiffies}; +use crate::types::Jiffies; use core::sync::atomic::{AtomicU64, Ordering}; use alloc::vec::Vec; // Add Vec import @@ -121,6 +121,21 @@ pub fn init() -> Result<()> { Ok(()) } +/// Initialize time management subsystem +pub fn init_time() -> Result<()> { + // Initialize the timer wheel + let _timer_wheel = get_timer_wheel(); + + // Set initial boot time (in a real implementation, this would read from RTC) + BOOTTIME_NS.store(0, Ordering::Relaxed); + + // Reset jiffies counter + JIFFIES_COUNTER.store(0, Ordering::Relaxed); + + crate::info!("Time management initialized"); + Ok(()) +} + /// Read hardware clock (placeholder) fn read_hardware_clock() -> u64 { // TODO: Read from actual hardware clock (RTC, TSC, etc.) @@ -282,7 +297,10 @@ fn get_timer_wheel() -> &'static Spinlock { TIMER_WHEEL_INIT.store(true, Ordering::Release); } - unsafe { TIMER_WHEEL_STORAGE.as_ref().unwrap() } + #[allow(static_mut_refs)] + unsafe { + TIMER_WHEEL_STORAGE.as_ref().unwrap() + } } /// Add a timer to the system