feat(shell): implement full interactive UNIX shell (osh), CMOS RTC, dual serial+PS2 keyboard input, and power management

This commit is contained in:
RarDog
2026-09-02 17:01:32 +03:00
parent 4d7ee738b6
commit ca3555bd09
7 changed files with 405 additions and 68 deletions
+100 -3
View File
@@ -4,14 +4,29 @@ use crate::ipc::endpoint::EP_MANAGER;
use crate::ipc::fastpath::{CAP_KERNEL_CONTROL, handle_fastpath_call};
use crate::sched::SCHEDULER;
use crate::mm::vmm::VMM;
use crate::mm::pfa::{PFA, PAGE_SIZE};
use crate::drivers::serial::SerialPort;
use crate::drivers::keyboard::KEY_BUFFER;
use crate::drivers::rtc::read_datetime;
use crate::arch::smp::CORES_ONLINE;
use crate::kprintln;
use core::sync::atomic::Ordering;
const IA32_EFER: u32 = 0xC0000080;
const IA32_STAR: u32 = 0xC0000081;
const IA32_LSTAR: u32 = 0xC0000082;
const IA32_FMASK: u32 = 0xC0000084;
#[inline]
unsafe fn outb(port: u16, val: u8) {
core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags));
}
#[inline]
unsafe fn outw(port: u16, val: u16) {
core::arch::asm!("out dx, ax", in("dx") port, in("ax") val, options(nomem, nostack, preserves_flags));
}
#[inline]
unsafe fn wrmsr(msr: u32, value: u64) {
let low = value as u32;
@@ -65,6 +80,25 @@ pub struct SyscallRegisters {
pub rax: u64, // Syscall number in, return code out
}
#[repr(C)]
pub struct SysInfoPayload {
pub uptime_ticks: u64,
pub total_memory_bytes: u64,
pub free_memory_bytes: u64,
pub active_threads: u32,
pub cpu_cores: u32,
}
#[repr(C)]
pub struct RtcPayload {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}
/// Dispatcher called directly from syscall_entry assembly trampoline
#[no_mangle]
pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) {
@@ -180,23 +214,86 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) {
if !msg_ptr.is_null() && len > 0 && len < 4096 {
let slice = unsafe { core::slice::from_raw_parts(msg_ptr, len) };
if let Ok(s) = core::str::from_utf8(slice) {
kprintln!("[USER TID {}] {}", current_tid, s);
SerialPort.write_str(s);
regs.rax = 0;
return;
}
}
regs.rax = (-1i64) as u64;
}
// SYS_KEY_READ = 10 (PS/2 Keyboard non-blocking read)
// SYS_KEY_READ = 10 (Checks both Serial COM1 and PS/2 Keyboard buffer)
10 => {
unsafe {
// 1. Check Serial Port (COM1) for direct terminal input in QEMU stdio
if let Some(ch) = SerialPort.read_byte() {
regs.rax = ch as u64;
return;
}
// 2. Check PS/2 Keyboard buffer (Hardware / GUI)
let kbuf = &mut *core::ptr::addr_of_mut!(KEY_BUFFER);
if let Some(ch) = kbuf.pop() {
regs.rax = ch as u64;
} else {
return;
}
regs.rax = 0;
}
}
// SYS_SYSINFO = 11 (Query system information)
11 => {
let out_ptr = regs.rdi as *mut SysInfoPayload;
if !out_ptr.is_null() {
unsafe {
let sched = &*core::ptr::addr_of!(SCHEDULER);
let pfa = &*core::ptr::addr_of!(PFA);
let active = sched.threads.iter().filter(|t| t.state != crate::sched::thread::ThreadState::Unused).count();
(*out_ptr).uptime_ticks = sched.ticks;
(*out_ptr).total_memory_bytes = (pfa.total_frames * PAGE_SIZE) as u64;
(*out_ptr).free_memory_bytes = (pfa.free_frames * PAGE_SIZE) as u64;
(*out_ptr).active_threads = active as u32;
(*out_ptr).cpu_cores = CORES_ONLINE.load(Ordering::Relaxed) as u32;
regs.rax = 0;
return;
}
}
regs.rax = (-1i64) as u64;
}
// SYS_SHUTDOWN = 12 (QEMU / ACPI Poweroff)
12 => {
kprintln!("[POWER] Powering off system...");
unsafe {
outw(0x604, 0x2000); // QEMU q35 ACPI poweroff
outw(0xB004, 0x2000); // QEMU i440fx poweroff
outw(0x4004, 0x3400); // VirtualBox shutdown
loop { core::arch::asm!("cli; hlt"); }
}
}
// SYS_REBOOT = 13 (8042 Keyboard Controller Reset)
13 => {
kprintln!("[POWER] Rebooting machine...");
unsafe {
outb(0x64, 0xFE); // Pulse reset line
loop { core::arch::asm!("cli; hlt"); }
}
}
// SYS_RTC_GET = 14 (CMOS Real-Time Clock)
14 => {
let out_ptr = regs.rdi as *mut RtcPayload;
if !out_ptr.is_null() {
let dt = read_datetime();
unsafe {
(*out_ptr).year = dt.year;
(*out_ptr).month = dt.month;
(*out_ptr).day = dt.day;
(*out_ptr).hour = dt.hour;
(*out_ptr).minute = dt.minute;
(*out_ptr).second = dt.second;
regs.rax = 0;
return;
}
}
regs.rax = (-1i64) as u64;
}
_ => {
regs.rax = (-1i64) as u64; // SYS_ERR_INVALID_ARG
+1
View File
@@ -1,3 +1,4 @@
pub mod serial;
pub mod timer;
pub mod keyboard;
pub mod rtc;
+54
View File
@@ -0,0 +1,54 @@
//! CMOS Real-Time Clock (RTC) Driver
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct RtcDateTime {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
}
#[inline]
unsafe fn outb(port: u16, val: u8) {
core::arch::asm!("out dx, al", in("dx") port, in("al") val, options(nomem, nostack, preserves_flags));
}
#[inline]
unsafe fn inb(port: u16) -> u8 {
let mut val: u8;
core::arch::asm!("in al, dx", in("dx") port, out("al") val, options(nomem, nostack, preserves_flags));
val
}
unsafe fn read_cmos_reg(reg: u8) -> u8 {
outb(0x70, reg);
inb(0x71)
}
#[inline]
fn bcd_to_bin(bcd: u8) -> u8 {
(bcd & 0x0F) + ((bcd / 16) * 10)
}
pub fn read_datetime() -> RtcDateTime {
unsafe {
let sec = bcd_to_bin(read_cmos_reg(0x00));
let min = bcd_to_bin(read_cmos_reg(0x02));
let hour = bcd_to_bin(read_cmos_reg(0x04));
let day = bcd_to_bin(read_cmos_reg(0x07));
let month = bcd_to_bin(read_cmos_reg(0x08));
let year = bcd_to_bin(read_cmos_reg(0x09)) as u16 + 2000;
RtcDateTime {
year,
month,
day,
hour,
minute: min,
second: sec,
}
}
}
+16
View File
@@ -45,6 +45,22 @@ impl SerialPort {
}
}
pub fn has_data(&self) -> bool {
unsafe {
(inb(COM1 + 5) & 0x01) != 0
}
}
pub fn read_byte(&self) -> Option<u8> {
if self.has_data() {
unsafe {
Some(inb(COM1))
}
} else {
None
}
}
pub fn write_str(&self, s: &str) {
for b in s.bytes() {
if b == b'\n' {
+5 -10
View File
@@ -7,11 +7,11 @@ use crate::kprintln;
pub const PAGE_SIZE: usize = 4096;
pub struct FrameAllocator {
bitmap: *mut u8,
bitmap_size_bytes: usize,
total_frames: usize,
free_frames: usize,
hhdm_offset: u64,
pub bitmap: *mut u8,
pub bitmap_size_bytes: usize,
pub total_frames: usize,
pub free_frames: usize,
pub hhdm_offset: u64,
}
unsafe impl Send for FrameAllocator {}
@@ -65,10 +65,8 @@ impl FrameAllocator {
}
self.bitmap = (bitmap_paddr + hhdm) as *mut u8;
// Initially mark all frames as used (1)
core::ptr::write_bytes(self.bitmap, 0xFF, self.bitmap_size_bytes);
// Mark only truly usable RAM regions as free (0)
self.free_frames = 0;
for i in 0..entry_count {
let entry = *entries.add(i);
@@ -82,7 +80,6 @@ impl FrameAllocator {
}
}
// Mark the bitmap memory itself as allocated
let bitmap_start_frame = (bitmap_paddr as usize) / PAGE_SIZE;
let bitmap_frame_count = (self.bitmap_size_bytes + PAGE_SIZE - 1) / PAGE_SIZE;
for f in bitmap_start_frame..(bitmap_start_frame + bitmap_frame_count) {
@@ -92,7 +89,6 @@ impl FrameAllocator {
}
}
// Also protect low 1MiB
let low_1mb_frames = 0x100000 / PAGE_SIZE;
for f in 0..low_1mb_frames {
if !self.test_bit(f) {
@@ -146,7 +142,6 @@ impl FrameAllocator {
self.set_bit(frame);
self.free_frames -= 1;
let paddr = (frame * PAGE_SIZE) as u64;
// Zero out the frame
unsafe {
let vaddr = (paddr + self.hhdm_offset) as *mut u8;
core::ptr::write_bytes(vaddr, 0, PAGE_SIZE);