feat(shell): implement full interactive UNIX shell (osh), CMOS RTC, dual serial+PS2 keyboard input, and power management
This commit is contained in:
+100
-3
@@ -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,3 +1,4 @@
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
pub mod keyboard;
|
||||
pub mod rtc;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user