build ok 2
Signed-off-by: ale <ale@manalejandro.com>
This commit is contained in:
parent
bbfefe2546
commit
eb8069b494
306
drivers/src/keyboard.rs
Normal file
306
drivers/src/keyboard.rs
Normal file
@ -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<u8>,
|
||||||
|
/// 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<u8> {
|
||||||
|
self.buffer.pop_front()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.buffer.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Global keyboard state
|
||||||
|
static KEYBOARD_STATE: Spinlock<KeyboardState> = 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<u8> {
|
||||||
|
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<usize> {
|
||||||
|
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<usize> {
|
||||||
|
// Can't write to keyboard
|
||||||
|
Err(Error::EPERM)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ioctl(&self, _file: &mut kernel::device::File, _cmd: u32, _arg: usize) -> Result<usize> {
|
||||||
|
// 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<dyn IrqHandler>)?; // 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<char> {
|
||||||
|
let mut keyboard = KEYBOARD_STATE.lock();
|
||||||
|
keyboard.pop_key().map(|k| k as char)
|
||||||
|
}
|
@ -12,6 +12,8 @@ pub mod dummy;
|
|||||||
pub mod mem;
|
pub mod mem;
|
||||||
pub mod platform_example;
|
pub mod platform_example;
|
||||||
pub mod ramdisk;
|
pub mod ramdisk;
|
||||||
|
pub mod keyboard; // New keyboard driver
|
||||||
|
pub mod serial; // New serial driver
|
||||||
|
|
||||||
pub use dummy::*;
|
pub use dummy::*;
|
||||||
pub use mem::*;
|
pub use mem::*;
|
||||||
|
336
drivers/src/serial.rs
Normal file
336
drivers/src/serial.rs
Normal file
@ -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<u8>,
|
||||||
|
/// Transmit buffer
|
||||||
|
tx_buffer: VecDeque<u8>,
|
||||||
|
/// 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<u8> {
|
||||||
|
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<Option<SerialPort>> = 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<usize> {
|
||||||
|
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<usize> {
|
||||||
|
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<usize> {
|
||||||
|
// 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<dyn IrqHandler>)?; // 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<String> {
|
||||||
|
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)
|
||||||
|
}
|
@ -254,10 +254,12 @@ impl DeviceSubsystem {
|
|||||||
self.devices.remove(name).ok_or(Error::NotFound)
|
self.devices.remove(name).ok_or(Error::NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn find_device(&self, name: &str) -> Option<&Device> {
|
fn find_device(&self, name: &str) -> Option<&Device> {
|
||||||
self.devices.get(name)
|
self.devices.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn find_device_mut(&mut self, name: &str) -> Option<&mut Device> {
|
fn find_device_mut(&mut self, name: &str) -> Option<&mut Device> {
|
||||||
self.devices.get_mut(name)
|
self.devices.get_mut(name)
|
||||||
}
|
}
|
||||||
|
@ -264,6 +264,7 @@ impl DriverSubsystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn find_driver(&self, name: &str) -> Option<&dyn Driver> {
|
fn find_driver(&self, name: &str) -> Option<&dyn Driver> {
|
||||||
self.drivers.get(name).map(|d| d.as_ref())
|
self.drivers.get(name).map(|d| d.as_ref())
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,12 @@ pub enum Error {
|
|||||||
Device,
|
Device,
|
||||||
/// Generic error
|
/// Generic error
|
||||||
Generic,
|
Generic,
|
||||||
|
/// Invalid operation
|
||||||
|
InvalidOperation,
|
||||||
|
/// Timeout
|
||||||
|
Timeout,
|
||||||
|
/// Not initialized
|
||||||
|
NotInitialized, // New error variant
|
||||||
|
|
||||||
// Linux-compatible errno values
|
// Linux-compatible errno values
|
||||||
/// Operation not permitted (EPERM)
|
/// Operation not permitted (EPERM)
|
||||||
@ -59,6 +65,10 @@ pub enum Error {
|
|||||||
EEXIST,
|
EEXIST,
|
||||||
/// Directory not empty (ENOTEMPTY)
|
/// Directory not empty (ENOTEMPTY)
|
||||||
ENOTEMPTY,
|
ENOTEMPTY,
|
||||||
|
/// No child process (ECHILD)
|
||||||
|
ECHILD,
|
||||||
|
/// No such process (ESRCH)
|
||||||
|
ESRCH,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
@ -76,6 +86,9 @@ impl Error {
|
|||||||
Error::WouldBlock => -11, // EAGAIN
|
Error::WouldBlock => -11, // EAGAIN
|
||||||
Error::Device => -19, // ENODEV
|
Error::Device => -19, // ENODEV
|
||||||
Error::Generic => -1, // EPERM
|
Error::Generic => -1, // EPERM
|
||||||
|
Error::InvalidOperation => -1, // EPERM
|
||||||
|
Error::Timeout => -110, // ETIMEDOUT
|
||||||
|
Error::NotInitialized => -6, // ENXIO
|
||||||
|
|
||||||
// Linux errno mappings
|
// Linux errno mappings
|
||||||
Error::EPERM => -1, // EPERM
|
Error::EPERM => -1, // EPERM
|
||||||
@ -92,6 +105,8 @@ impl Error {
|
|||||||
Error::EISDIR => -21, // EISDIR
|
Error::EISDIR => -21, // EISDIR
|
||||||
Error::EEXIST => -17, // EEXIST
|
Error::EEXIST => -17, // EEXIST
|
||||||
Error::ENOTEMPTY => -39, // ENOTEMPTY
|
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::WouldBlock => write!(f, "Resource temporarily unavailable"),
|
||||||
Error::Device => write!(f, "Device error"),
|
Error::Device => write!(f, "Device error"),
|
||||||
Error::Generic => write!(f, "Generic 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
|
// Linux errno variants
|
||||||
Error::EPERM => write!(f, "Operation not permitted"),
|
Error::EPERM => write!(f, "Operation not permitted"),
|
||||||
@ -126,6 +144,8 @@ impl fmt::Display for Error {
|
|||||||
Error::EISDIR => write!(f, "Is a directory"),
|
Error::EISDIR => write!(f, "Is a directory"),
|
||||||
Error::EEXIST => write!(f, "File exists"),
|
Error::EEXIST => write!(f, "File exists"),
|
||||||
Error::ENOTEMPTY => write!(f, "Directory not empty"),
|
Error::ENOTEMPTY => write!(f, "Directory not empty"),
|
||||||
|
Error::ECHILD => write!(f, "No child processes"),
|
||||||
|
Error::ESRCH => write!(f, "No such process"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
//! Directory entry (dentry) abstraction - Linux compatible
|
//! Directory entry (dentry) abstraction - Linux compatible
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::Result;
|
||||||
use crate::sync::{Arc, Mutex, RwLock};
|
use crate::sync::{Arc, Mutex};
|
||||||
use alloc::{string::String, vec::Vec, format}; // Add format macro
|
use alloc::{string::String, vec::Vec, format}; // Add format macro
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
//! Inode abstraction - Linux compatible
|
//! Inode abstraction - Linux compatible
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::types::*;
|
use crate::sync::{Arc, Mutex};
|
||||||
use crate::sync::{Arc, Mutex, RwLock};
|
|
||||||
use crate::device::DeviceNumber;
|
use crate::device::DeviceNumber;
|
||||||
use crate::time::{TimeSpec, get_current_time};
|
use crate::time::{TimeSpec, get_current_time};
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
@ -18,14 +18,11 @@ pub mod devfs;
|
|||||||
pub mod mode; // Add mode module
|
pub mod mode; // Add mode module
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::types::*;
|
use crate::sync::{Arc, Mutex};
|
||||||
use crate::sync::{Arc, Mutex, RwLock};
|
|
||||||
use crate::memory::{UserPtr, UserSlicePtr};
|
use crate::memory::{UserPtr, UserSlicePtr};
|
||||||
use crate::device::DeviceNumber;
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
pub use file::*;
|
pub use file::*;
|
||||||
pub use inode::*;
|
pub use inode::*;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
//! VFS mount abstraction - Linux compatible
|
//! VFS mount abstraction - Linux compatible
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
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 alloc::{string::String, vec::Vec, format}; // Add format macro
|
||||||
use core::sync::atomic::{AtomicU32, Ordering};
|
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 mount = Arc::new(VfsMount::new(sb, dir_name, flags)?);
|
||||||
|
|
||||||
let ns = get_init_ns();
|
let ns = get_init_ns();
|
||||||
let mut ns = ns.lock();
|
let ns = ns.lock();
|
||||||
ns.add_mount(mount);
|
ns.add_mount(mount);
|
||||||
|
|
||||||
crate::console::print_info(&format!("Mounted {} on {} (type {})\n", dev_name, dir_name, type_name));
|
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
|
/// Unmount a filesystem
|
||||||
pub fn do_umount(dir_name: &str, flags: u32) -> Result<()> {
|
pub fn do_umount(dir_name: &str, flags: u32) -> Result<()> {
|
||||||
let ns = get_init_ns();
|
let ns = get_init_ns();
|
||||||
let mut ns = ns.lock();
|
let ns = ns.lock();
|
||||||
|
|
||||||
if let Some(mount) = ns.remove_mount(dir_name) {
|
if let Some(mount) = ns.remove_mount(dir_name) {
|
||||||
mount.mntput();
|
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
|
/// Bind mount - create a bind mount
|
||||||
pub fn do_bind_mount(old_path: &str, new_path: &str, flags: u32) -> Result<()> {
|
pub fn do_bind_mount(old_path: &str, new_path: &str, flags: u32) -> Result<()> {
|
||||||
let ns = get_init_ns();
|
let ns = get_init_ns();
|
||||||
let mut ns = ns.lock();
|
let ns = ns.lock();
|
||||||
|
|
||||||
if let Some(old_mount) = ns.find_mount(old_path) {
|
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)?);
|
let new_mount = Arc::new(VfsMount::new(old_mount.mnt_sb.clone(), new_path, flags | super::super_block::MS_BIND)?);
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::sync::Arc;
|
use crate::sync::Arc;
|
||||||
use crate::memory::{UserPtr, UserSlicePtr};
|
use crate::memory::UserSlicePtr;
|
||||||
use alloc::string::String;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
/// Address space operations trait - similar to Linux address_space_operations
|
/// Address space operations trait - similar to Linux address_space_operations
|
||||||
pub trait AddressSpaceOperations: Send + Sync {
|
pub trait AddressSpaceOperations: Send + Sync {
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
//! Superblock abstraction - Linux compatible
|
//! Superblock abstraction - Linux compatible
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::Result;
|
||||||
use crate::sync::{Arc, Mutex, RwLock};
|
use crate::sync::{Arc, Mutex};
|
||||||
use crate::device::DeviceNumber;
|
use crate::device::DeviceNumber;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
//! Kernel initialization
|
//! Kernel initialization
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
use crate::{info, error};
|
use crate::{info, error};
|
||||||
|
|
||||||
/// Early kernel initialization
|
/// Early kernel initialization
|
||||||
@ -58,6 +57,33 @@ pub fn main_init() -> ! {
|
|||||||
}
|
}
|
||||||
info!("VFS initialized");
|
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!("Kernel initialization completed");
|
||||||
info!("Starting idle loop");
|
info!("Starting idle loop");
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::sync::Spinlock;
|
use crate::sync::Spinlock;
|
||||||
use crate::types::Irq;
|
use alloc::{collections::BTreeMap, boxed::Box}; // Add Box import
|
||||||
use alloc::{vec::Vec, collections::BTreeMap, boxed::Box}; // Add Box import
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
/// IRQ flags - compatible with Linux kernel
|
/// IRQ flags - compatible with Linux kernel
|
||||||
pub mod irq_flags {
|
pub mod irq_flags {
|
||||||
@ -155,6 +155,7 @@ impl InterruptSubsystem {
|
|||||||
self.descriptors.get_mut(&irq)
|
self.descriptors.get_mut(&irq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn get_descriptor(&self, irq: u32) -> Option<&IrqDescriptor> {
|
fn get_descriptor(&self, irq: u32) -> Option<&IrqDescriptor> {
|
||||||
self.descriptors.get(&irq)
|
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) {
|
if let Some(desc) = subsystem.get_descriptor_mut(irq) {
|
||||||
// Remove action with matching dev_id
|
// Remove action with matching dev_id
|
||||||
let mut prev: Option<&mut Box<IrqAction>> = None;
|
let prev: Option<&mut Box<IrqAction>> = None;
|
||||||
let mut current = &mut desc.action;
|
let current = &mut desc.action;
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
|
||||||
// Handle first element specially
|
// Handle first element specially
|
||||||
@ -365,59 +366,39 @@ pub fn disable_irq(irq: u32) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle an interrupt - called from low-level interrupt handlers
|
/// Register an interrupt handler at a specific vector
|
||||||
pub fn handle_interrupt(irq: u32) {
|
pub fn register_interrupt_handler(vector: u32, handler: usize) -> Result<()> {
|
||||||
let mut subsystem = INTERRUPT_SUBSYSTEM.lock();
|
// 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) {
|
crate::info!("Registering interrupt handler at vector 0x{:x} -> 0x{:x}", vector, handler);
|
||||||
desc.irq_count += 1;
|
|
||||||
|
// In a real implementation, this would configure the IDT
|
||||||
if !desc.is_enabled() {
|
// For x86_64, this involves setting up interrupt gates in the IDT
|
||||||
// Interrupt is disabled, shouldn't happen
|
Ok(())
|
||||||
crate::warn!("Received disabled interrupt {}", irq);
|
}
|
||||||
return;
|
|
||||||
}
|
/// System call interrupt handler
|
||||||
|
#[no_mangle]
|
||||||
// Call all handlers in the action chain
|
pub extern "C" fn syscall_handler() {
|
||||||
let mut current = desc.action.as_ref();
|
// TODO: Get syscall arguments from registers
|
||||||
let mut handled = false;
|
// TODO: Call syscall dispatcher
|
||||||
|
// TODO: Return result in register
|
||||||
while let Some(action) = current {
|
|
||||||
let result = (action.handler)(irq, action.dev_id.as_ptr());
|
// For now, just a placeholder
|
||||||
match result {
|
unsafe {
|
||||||
IrqReturn::Handled => {
|
asm!("iretq", options(noreturn));
|
||||||
handled = true;
|
|
||||||
}
|
|
||||||
IrqReturn::WakeThread => {
|
|
||||||
handled = true;
|
|
||||||
// TODO: Wake threaded interrupt handler
|
|
||||||
}
|
|
||||||
IrqReturn::None => {
|
|
||||||
// Handler didn't handle this interrupt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current = action.next.as_ref();
|
|
||||||
}
|
|
||||||
|
|
||||||
if !handled {
|
|
||||||
desc.irqs_unhandled += 1;
|
|
||||||
if desc.irqs_unhandled > 100 {
|
|
||||||
crate::warn!("Too many unhandled interrupts on IRQ {}", irq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
crate::warn!("Spurious interrupt {}", irq);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get interrupt statistics
|
/// Install syscall interrupt handler
|
||||||
pub fn get_irq_stats() -> Vec<(u32, &'static str, u64, u64)> {
|
pub fn install_syscall_handler() -> Result<()> {
|
||||||
let subsystem = INTERRUPT_SUBSYSTEM.lock();
|
// Install at interrupt vector 0x80 (traditional Linux syscall vector)
|
||||||
let mut stats = Vec::new();
|
register_interrupt_handler(0x80, syscall_handler as usize)?;
|
||||||
|
|
||||||
for (irq, desc) in &subsystem.descriptors {
|
// TODO: Also set up SYSCALL/SYSRET for x86_64
|
||||||
stats.push((*irq, desc.name, desc.irq_count, desc.irqs_unhandled));
|
Ok(())
|
||||||
}
|
|
||||||
|
|
||||||
stats
|
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,11 @@ pub mod process;
|
|||||||
pub mod scheduler;
|
pub mod scheduler;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod syscall;
|
pub mod syscall;
|
||||||
|
pub mod syscalls; // New syscall infrastructure
|
||||||
pub mod task;
|
pub mod task;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
|
||||||
|
|
||||||
/// Kernel version information
|
/// Kernel version information
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
@ -1,19 +1,153 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
//! Heap allocator implementation
|
//! Memory allocator implementation - Enhanced with buddy allocator
|
||||||
|
|
||||||
use crate::memory::ALLOCATOR;
|
use crate::memory::ALLOCATOR;
|
||||||
use crate::types::{VirtAddr, PAGE_SIZE};
|
use crate::types::{VirtAddr, PhysAddr, PAGE_SIZE};
|
||||||
use crate::error::Result;
|
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<FreeBlock>; 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<FreeBlock> = 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<PageFrameNumber> {
|
||||||
|
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<Option<BuddyAllocator>> = Spinlock::new(None);
|
||||||
|
|
||||||
/// Heap start address (will be set during initialization)
|
/// Heap start address (will be set during initialization)
|
||||||
static mut HEAP_START: usize = 0;
|
static mut HEAP_START: usize = 0;
|
||||||
static mut HEAP_SIZE: usize = 0;
|
static mut HEAP_SIZE: usize = 0;
|
||||||
|
|
||||||
/// Initialize the heap allocator
|
/// Initialize the allocators
|
||||||
pub fn init() -> Result<()> {
|
pub fn init() -> Result<()> {
|
||||||
// TODO: Get heap region from memory map
|
// Initialize heap allocator
|
||||||
// For now, use a fixed region
|
|
||||||
let heap_start = 0x_4444_4444_0000;
|
let heap_start = 0x_4444_4444_0000;
|
||||||
let heap_size = 100 * 1024; // 100 KB
|
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);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,3 +174,42 @@ pub fn init() -> Result<()> {
|
|||||||
pub fn heap_stats() -> (usize, usize) {
|
pub fn heap_stats() -> (usize, usize) {
|
||||||
unsafe { (HEAP_START, HEAP_SIZE) }
|
unsafe { (HEAP_START, HEAP_SIZE) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Linux-compatible page allocation functions
|
||||||
|
pub fn alloc_pages(order: usize, flags: GfpFlags) -> Result<PageFrameNumber> {
|
||||||
|
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<VirtAddr> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,7 +12,6 @@ pub use page::Page;
|
|||||||
pub use crate::types::{PhysAddr, VirtAddr, Pfn}; // Re-export from types
|
pub use crate::types::{PhysAddr, VirtAddr, Pfn}; // Re-export from types
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use core::alloc::{GlobalAlloc, Layout};
|
|
||||||
use linked_list_allocator::LockedHeap;
|
use linked_list_allocator::LockedHeap;
|
||||||
|
|
||||||
/// GFP (Get Free Pages) flags - compatible with Linux kernel
|
/// GFP (Get Free Pages) flags - compatible with Linux kernel
|
||||||
|
@ -6,7 +6,7 @@ use crate::types::{Pid, Tid, Uid, Gid};
|
|||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::sync::Spinlock;
|
use crate::sync::Spinlock;
|
||||||
use crate::memory::VirtAddr;
|
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};
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
/// Process state - compatible with Linux kernel
|
/// Process state - compatible with Linux kernel
|
||||||
@ -71,6 +71,67 @@ impl Process {
|
|||||||
pub fn is_running(&self) -> bool {
|
pub fn is_running(&self) -> bool {
|
||||||
self.state == ProcessState::Running
|
self.state == ProcessState::Running
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fork the current process (create a copy)
|
||||||
|
pub fn fork(&self) -> Result<Process> {
|
||||||
|
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<String>) -> 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
|
/// Thread structure - Linux-compatible
|
||||||
@ -185,6 +246,7 @@ impl ProcessTable {
|
|||||||
self.processes.get_mut(&pid)
|
self.processes.get_mut(&pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn remove_process(&mut self, pid: Pid) -> Option<Process> {
|
fn remove_process(&mut self, pid: Pid) -> Option<Process> {
|
||||||
let process = self.processes.remove(&pid);
|
let process = self.processes.remove(&pid);
|
||||||
if self.current_process == Some(pid) {
|
if self.current_process == Some(pid) {
|
||||||
@ -224,14 +286,24 @@ pub fn create_process(name: String, uid: Uid, gid: Gid) -> Result<Pid> {
|
|||||||
Ok(pid)
|
Ok(pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current process
|
/// Get current process PID
|
||||||
pub fn current_process() -> Option<Pid> {
|
pub fn current_process_pid() -> Option<Pid> {
|
||||||
let table = PROCESS_TABLE.lock();
|
let table = PROCESS_TABLE.lock();
|
||||||
table.current_process
|
table.current_process
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get current process object
|
||||||
|
pub fn current_process() -> Option<Process> {
|
||||||
|
let table = PROCESS_TABLE.lock();
|
||||||
|
if let Some(pid) = table.current_process {
|
||||||
|
table.get_process(pid).cloned()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get process by PID
|
/// Get process by PID
|
||||||
pub fn get_process(pid: Pid) -> Option<Process> {
|
pub fn find_process(pid: Pid) -> Option<Process> {
|
||||||
let table = PROCESS_TABLE.lock();
|
let table = PROCESS_TABLE.lock();
|
||||||
table.get_process(pid).cloned()
|
table.get_process(pid).cloned()
|
||||||
}
|
}
|
||||||
@ -254,6 +326,11 @@ pub fn list_processes() -> Vec<Pid> {
|
|||||||
table.list_processes()
|
table.list_processes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize process management
|
||||||
|
pub fn init_process_management() -> Result<()> {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the process subsystem
|
/// Initialize the process subsystem
|
||||||
pub fn init() -> Result<()> {
|
pub fn init() -> Result<()> {
|
||||||
// Create kernel process (PID 0)
|
// Create kernel process (PID 0)
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
//! Task scheduler compatible with Linux kernel CFS (Completely Fair Scheduler)
|
//! Task scheduler compatible with Linux kernel CFS (Completely Fair Scheduler)
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::process::{Process, Thread, ProcessState, ThreadContext};
|
use crate::types::Tid;
|
||||||
use crate::types::{Pid, Tid, Nanoseconds};
|
|
||||||
use crate::sync::Spinlock;
|
use crate::sync::Spinlock;
|
||||||
use crate::time;
|
use crate::time;
|
||||||
use alloc::{collections::{BTreeMap, VecDeque}, vec::Vec};
|
use alloc::{collections::{BTreeMap, VecDeque}, vec::Vec};
|
||||||
|
@ -28,7 +28,7 @@ impl<T> Spinlock<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lock(&self) -> SpinlockGuard<T> {
|
pub fn lock(&self) -> SpinlockGuard<'_, T> {
|
||||||
while self.locked.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
|
while self.locked.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
|
||||||
// Busy wait
|
// Busy wait
|
||||||
while self.locked.load(Ordering::Relaxed) {
|
while self.locked.load(Ordering::Relaxed) {
|
||||||
@ -39,7 +39,7 @@ impl<T> Spinlock<T> {
|
|||||||
SpinlockGuard { lock: self }
|
SpinlockGuard { lock: self }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_lock(&self) -> Option<SpinlockGuard<T>> {
|
pub fn try_lock(&self) -> Option<SpinlockGuard<'_, T>> {
|
||||||
if self.locked.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok() {
|
if self.locked.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok() {
|
||||||
Some(SpinlockGuard { lock: self })
|
Some(SpinlockGuard { lock: self })
|
||||||
} else {
|
} else {
|
||||||
|
291
kernel/src/syscalls.rs
Normal file
291
kernel/src/syscalls.rs
Normal file
@ -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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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<u64> {
|
||||||
|
// 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(())
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
//! Time management compatible with Linux kernel
|
//! Time management compatible with Linux kernel
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::types::{Nanoseconds, Microseconds, Milliseconds, Seconds, Jiffies};
|
use crate::types::Jiffies;
|
||||||
use core::sync::atomic::{AtomicU64, Ordering};
|
use core::sync::atomic::{AtomicU64, Ordering};
|
||||||
use alloc::vec::Vec; // Add Vec import
|
use alloc::vec::Vec; // Add Vec import
|
||||||
|
|
||||||
@ -121,6 +121,21 @@ pub fn init() -> Result<()> {
|
|||||||
Ok(())
|
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)
|
/// Read hardware clock (placeholder)
|
||||||
fn read_hardware_clock() -> u64 {
|
fn read_hardware_clock() -> u64 {
|
||||||
// TODO: Read from actual hardware clock (RTC, TSC, etc.)
|
// TODO: Read from actual hardware clock (RTC, TSC, etc.)
|
||||||
@ -282,7 +297,10 @@ fn get_timer_wheel() -> &'static Spinlock<TimerWheel> {
|
|||||||
TIMER_WHEEL_INIT.store(true, Ordering::Release);
|
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
|
/// Add a timer to the system
|
||||||
|
Loading…
x
Reference in New Issue
Block a user