diff --git a/hal/include/rtl8139.h b/hal/include/rtl8139.h index 0de485b..c088c97 100644 --- a/hal/include/rtl8139.h +++ b/hal/include/rtl8139.h @@ -9,8 +9,11 @@ extern "C" { typedef struct { uint16_t io_base; uint8_t mac[6]; - uint8_t rx_buffer[8192 + 16 + 1500] __attribute__((aligned(4))); - uint8_t tx_buffers[4][1536] __attribute__((aligned(4))); + uint8_t *rx_virt; + uint64_t rx_phys; + uint8_t *tx_virt[4]; + uint64_t tx_phys[4]; + uint16_t rx_offset; uint8_t tx_cur; int is_initialized; uint64_t rx_packets; diff --git a/hal/src/pci.c b/hal/src/pci.c index d3820c7..dde4b0d 100644 --- a/hal/src/pci.c +++ b/hal/src/pci.c @@ -41,7 +41,7 @@ uint16_t pci_get_device_id(uint8_t bus, uint8_t slot, uint8_t func) { } void pci_scan_bus(pci_scan_callback_t callback) { - for (uint16_t bus = 0; bus < 256; bus++) { + for (uint16_t bus = 0; bus < 4; bus++) { for (uint8_t slot = 0; slot < 32; slot++) { uint16_t vendor = pci_get_vendor_id((uint8_t)bus, slot, 0); if (vendor == 0xFFFF || vendor == 0x0000) { diff --git a/hal/src/rtl8139.c b/hal/src/rtl8139.c index 2938e2b..e5697b3 100644 --- a/hal/src/rtl8139.c +++ b/hal/src/rtl8139.c @@ -1,11 +1,11 @@ #include "../include/rtl8139.h" #include "../include/pci.h" +#include "abi/syscalls.h" #include "stdio.h" #include "string.h" rtl8139_dev_t g_rtl8139 = {0}; -/* Port I/O helpers */ static inline void outb(uint16_t port, uint8_t val) { __asm__ volatile("outb %0, %1" : : "a"(val), "Nd"(port)); } @@ -33,6 +33,7 @@ static inline uint32_t inl(uint16_t port) { #define RTL_CONFIG1 0x52 #define RTL_COMMAND 0x37 +#define RTL_CAPR 0x38 #define RTL_RBSTART 0x30 #define RTL_IMR 0x3C #define RTL_ISR 0x3E @@ -43,44 +44,59 @@ static inline uint32_t inl(uint16_t port) { static void pci_check_rtl8139(const pci_device_t *dev) { if (dev->vendor_id == 0x10EC && dev->device_id == 0x8139) { - // Read BAR0 (I/O Port Base) uint32_t bar0 = pci_read_config_dword(dev->bus, dev->slot, dev->func, 0x10); g_rtl8139.io_base = (uint16_t)(bar0 & ~0x3); - // Enable Bus Mastering and I/O Space in PCI Command register uint32_t cmd = pci_read_config_dword(dev->bus, dev->slot, dev->func, 0x04); cmd |= (1 << 0) | (1 << 2); // I/O Space + Bus Master pci_write_config_dword(dev->bus, dev->slot, dev->func, 0x04, cmd); + printf("[PCI] Detected RTL8139 on Bus %u, Slot %u, Func %u. Enabled Bus Master (cmd=0x%x)\n", + dev->bus, dev->slot, dev->func, cmd); } } int rtl8139_init(void) { if (g_rtl8139.is_initialized) return 0; - // 1. Find device on PCI bus - pci_scan_bus(pci_check_rtl8139); + // 1. Allocate Physical DMA buffer (6 pages = 24 KiB: 16 KiB RX, 8 KiB TX) + dma_alloc_data_t dma = {0}; + register uint64_t rax __asm__("rax") = SYS_DMA_ALLOC; + register uint64_t rdi __asm__("rdi") = 6; + register uint64_t rsi __asm__("rsi") = (uint64_t)&dma; + __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi) : "rcx", "r11", "memory"); + if (rax != 0 || dma.phys_addr == 0) { + return -1; + } + + g_rtl8139.rx_virt = (uint8_t *)dma.virt_addr; + g_rtl8139.rx_phys = dma.phys_addr; + + for (int i = 0; i < 4; i++) { + g_rtl8139.tx_virt[i] = (uint8_t *)(dma.virt_addr + 16384 + (i * 2048)); + g_rtl8139.tx_phys[i] = dma.phys_addr + 16384 + (i * 2048); + } + g_rtl8139.rx_offset = 0; + + // 2. Find device on PCI bus + pci_scan_bus(pci_check_rtl8139); if (g_rtl8139.io_base == 0) { - // Fallback default port commonly assigned in QEMU g_rtl8139.io_base = 0xC000; } uint16_t io = g_rtl8139.io_base; - // 2. Power on the card (Config1 = 0x00) + // 3. Power on (Config1 = 0x00) & Software Reset outb(io + RTL_CONFIG1, 0x00); - - // 3. Software Reset outb(io + RTL_COMMAND, 0x10); while ((inb(io + RTL_COMMAND) & 0x10) != 0) { - // Wait for reset to complete + // Wait for reset } // 4. Read MAC Address for (int i = 0; i < 6; i++) { g_rtl8139.mac[i] = inb(io + i); } - // If MAC is all 0 or all FF, set standard QEMU virtual NIC MAC if ((g_rtl8139.mac[0] == 0 && g_rtl8139.mac[1] == 0) || g_rtl8139.mac[0] == 0xFF) { g_rtl8139.mac[0] = 0x52; g_rtl8139.mac[1] = 0x54; @@ -90,18 +106,25 @@ int rtl8139_init(void) { g_rtl8139.mac[5] = 0x56; } - // 5. Initialize Receive Buffer - outl(io + RTL_RBSTART, (uint32_t)(uintptr_t)g_rtl8139.rx_buffer); + // 5. Initialize Receive Buffer with PHYSICAL address + outl(io + RTL_RBSTART, (uint32_t)g_rtl8139.rx_phys); - // 6. Set IMR (Interrupt Mask) & RCR (Accept Broadcast, Multicast, Physical Match, All packets) + // 6. IMR & RCR (Accept Broadcast, Multicast, Physical Match, Wrap) outw(io + RTL_IMR, 0x0005); // ROK + TOK - outl(io + RTL_RCR, 0x0F | (1 << 7)); // AB+AM+APM+AAP + Wrap + outl(io + RTL_RCR, 0x0000008F); // AB+AM+APM+AAP + Wrap + 8K buffer + outl(io + RTL_TCR, 0x03000000); // 2048-byte DMA burst // 7. Enable Transmitter and Receiver outb(io + RTL_COMMAND, 0x0C); + outw(io + RTL_ISR, 0xFFFF); // Clear all pending interrupts g_rtl8139.tx_cur = 0; g_rtl8139.is_initialized = 1; + + printf("[RTL8139] Init OK: I/O 0x%x, Virt 0x%lx, Phys 0x%lx, MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + io, (unsigned long)g_rtl8139.rx_virt, (unsigned long)g_rtl8139.rx_phys, + g_rtl8139.mac[0], g_rtl8139.mac[1], g_rtl8139.mac[2], + g_rtl8139.mac[3], g_rtl8139.mac[4], g_rtl8139.mac[5]); return 0; } @@ -111,23 +134,17 @@ int rtl8139_send_packet(const void *data, uint16_t len) { } if (len > 1514) len = 1514; - uint16_t io = g_rtl8139.io_base; uint8_t tx_id = g_rtl8139.tx_cur % 4; - // Copy payload into TX buffer - memcpy(g_rtl8139.tx_buffers[tx_id], data, len); - - // Pad to minimum Ethernet frame size (60 bytes excluding 4-byte CRC) + memcpy(g_rtl8139.tx_virt[tx_id], data, len); if (len < 60) { - memset(g_rtl8139.tx_buffers[tx_id] + len, 0, 60 - len); + memset(g_rtl8139.tx_virt[tx_id] + len, 0, 60 - len); len = 60; } - // Set TX buffer physical address - outl(io + RTL_TSAD0 + (tx_id * 4), (uint32_t)(uintptr_t)g_rtl8139.tx_buffers[tx_id]); - - // Set length and start transmission (clearing OWN bit) + // Send packet using PHYSICAL address + outl(io + RTL_TSAD0 + (tx_id * 4), (uint32_t)g_rtl8139.tx_phys[tx_id]); outl(io + RTL_TSD0 + (tx_id * 4), (uint32_t)len & 0x1FFF); g_rtl8139.tx_cur = (g_rtl8139.tx_cur + 1) % 4; @@ -142,24 +159,32 @@ int rtl8139_poll_packet(void *out_buf, uint16_t max_len) { uint16_t io = g_rtl8139.io_base; uint16_t isr = inw(io + RTL_ISR); - // Check Receive OK (bit 0) + uint8_t *packet = g_rtl8139.rx_virt + g_rtl8139.rx_offset; + uint16_t status = *(uint16_t *)packet; + uint16_t length = *(uint16_t *)(packet + 2); + if (isr & 0x01) { - outw(io + RTL_ISR, 0x01); // Acknowledge RX interrupt - - // Simple RX frame parsing from ring buffer - uint16_t *header = (uint16_t *)g_rtl8139.rx_buffer; - uint16_t status = header[0]; - uint16_t length = header[1]; - - if (status & 0x01 && length > 4 && length < 1600) { - uint16_t payload_len = length - 4; // Exclude 4-byte CRC - if (payload_len > max_len) payload_len = max_len; - - memcpy(out_buf, g_rtl8139.rx_buffer + 4, payload_len); - g_rtl8139.rx_packets++; - g_rtl8139.rx_bytes += payload_len; - return (int)payload_len; - } + outw(io + RTL_ISR, 0x01); // Acknowledge ROK } + + if ((status & 0x01) && length >= 4 && length < 1600) { + uint16_t payload_len = length - 4; // strip 4-byte CRC + if (payload_len > max_len) payload_len = max_len; + + memcpy(out_buf, packet + 4, payload_len); + g_rtl8139.rx_packets++; + g_rtl8139.rx_bytes += payload_len; + + // Clear packet status so it is not re-read + *(uint16_t *)packet = 0; + + // Advance read pointer (dword aligned) + g_rtl8139.rx_offset = (g_rtl8139.rx_offset + length + 4 + 3) & ~3; + g_rtl8139.rx_offset %= 8192; + + outw(io + RTL_CAPR, (g_rtl8139.rx_offset - 16) & 0xFFFF); + return (int)payload_len; + } + return 0; } diff --git a/include/abi/syscalls.h b/include/abi/syscalls.h index 330caa3..ff1e32a 100644 --- a/include/abi/syscalls.h +++ b/include/abi/syscalls.h @@ -20,6 +20,9 @@ #define SYS_FB_INFO 15 /* Query Limine Linear Framebuffer */ #define SYS_PROC_KILL 16 /* Terminate process/thread by PID */ #define SYS_DMESG 17 /* Retrieve kernel boot log */ +#define SYS_PROC_LIST 18 /* Query active kernel thread list */ +#define SYS_PROC_SPAWN 19 /* Dynamically load and run ELF process */ +#define SYS_DMA_ALLOC 20 /* Allocate physical DMA buffer */ #ifndef __ASSEMBLER__ @@ -27,6 +30,21 @@ extern "C" { #endif +typedef struct thread_info { + uint64_t tid; + char name[32]; + uint32_t state; // 0=Unused, 1=Ready, 2=Running, 3=Blocked, 4=Dead + uint64_t cpu_ticks; + uint64_t user_rip; + uint64_t user_rsp; +} thread_info_t; + +typedef struct dma_alloc_data { + uint64_t virt_addr; + uint64_t phys_addr; + uint64_t size_bytes; +} dma_alloc_data_t; + typedef struct fb_info_data { uint64_t fb_addr; uint64_t width; diff --git a/kernel/src/arch/syscall.rs b/kernel/src/arch/syscall.rs index 8464510..f0d7635 100644 --- a/kernel/src/arch/syscall.rs +++ b/kernel/src/arch/syscall.rs @@ -108,6 +108,23 @@ pub struct FbInfoPayload { pub bpp: u32, } +#[repr(C)] +pub struct ThreadInfoPayload { + pub tid: u64, + pub name: [u8; 32], + pub state: u32, + pub cpu_ticks: u64, + pub user_rip: u64, + pub user_rsp: u64, +} + +#[repr(C)] +pub struct DmaAllocPayload { + pub virt_addr: u64, + pub phys_addr: u64, + pub size_bytes: u64, +} + /// Dispatcher called directly from syscall_entry assembly trampoline #[no_mangle] pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) { @@ -257,7 +274,7 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) { 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).total_memory_bytes = pfa.usable_ram_bytes; (*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; @@ -354,16 +371,123 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) { let buf_ptr = regs.rdi as *mut u8; let max_len = regs.rsi as usize; if !buf_ptr.is_null() && max_len > 0 { - const DMESG_STR: &[u8] = b"[ 0.000000] Linux-compatible opencoreC x86_64 microkernel booting...\n[ 0.000100] HHDM direct map initialized.\n[ 0.000250] ACPI/SMP detected 4 cores.\n[ 0.000500] PFA bitmap initialized.\n[ 0.000800] VMM PML4 page directory active.\n[ 0.001200] Fast-path Syscall MSRs configured (STAR, LSTAR, FMASK).\n[ 0.002000] Preemptive 8254 PIT @ 100Hz quantum scheduler ready.\n[ 0.003500] Limine Framebuffer initialized.\n[ 0.004000] PCI Bus scan completed. Realtek RTL8139 NIC detected.\n[ 0.005000] User space ring 3 tasks created (init_server, uart_driver, sh, procmgr).\n"; - let copy_len = max_len.min(DMESG_STR.len()); - unsafe { - core::ptr::copy_nonoverlapping(DMESG_STR.as_ptr(), buf_ptr, copy_len); - } - regs.rax = copy_len as u64; + let slice = unsafe { core::slice::from_raw_parts_mut(buf_ptr, max_len) }; + let bytes_read = crate::drivers::dmesg::read_log(slice); + regs.rax = bytes_read as u64; return; } regs.rax = (-1i64) as u64; } + // SYS_PROC_LIST = 18 (Query active threads from scheduler) + 18 => { + let out_ptr = regs.rdi as *mut ThreadInfoPayload; + let max_threads = regs.rsi as usize; + if !out_ptr.is_null() && max_threads > 0 { + unsafe { + let sched = &*core::ptr::addr_of!(SCHEDULER); + let mut count = 0; + for t in sched.threads.iter() { + if t.state != crate::sched::thread::ThreadState::Unused && count < max_threads { + let entry = &mut *out_ptr.add(count); + entry.tid = t.id; + entry.state = match t.state { + crate::sched::thread::ThreadState::Unused => 0, + crate::sched::thread::ThreadState::Ready => 1, + crate::sched::thread::ThreadState::Running => 2, + crate::sched::thread::ThreadState::BlockedOnSend(_) | + crate::sched::thread::ThreadState::BlockedOnRecv(_) | + crate::sched::thread::ThreadState::BlockedOnReply(_) => 3, + crate::sched::thread::ThreadState::Dead => 4, + }; + entry.cpu_ticks = sched.ticks / 4; + entry.user_rip = t.user_rip; + entry.user_rsp = t.user_rsp; + let mut name_buf = [0u8; 32]; + let name_bytes = t.name.as_bytes(); + let nlen = name_bytes.len().min(31); + name_buf[..nlen].copy_from_slice(&name_bytes[..nlen]); + entry.name = name_buf; + count += 1; + } + } + regs.rax = count as u64; + return; + } + } + regs.rax = (-1i64) as u64; + } + // SYS_PROC_SPAWN = 19 (Dynamically load and run ELF in Ring 3) + 19 => { + let elf_ptr = regs.rdi as *const u8; + let elf_len = regs.rsi as usize; + let name_ptr = regs.rdx as *const u8; + if !elf_ptr.is_null() && elf_len > 64 { + let elf_slice = unsafe { core::slice::from_raw_parts(elf_ptr, elf_len) }; + let name = if !name_ptr.is_null() { + let mut len = 0; + while unsafe { *name_ptr.add(len) } != 0 && len < 31 { len += 1; } + let s = unsafe { core::slice::from_raw_parts(name_ptr, len) }; + core::str::from_utf8(s).unwrap_or("user_proc") + } else { + "user_proc" + }; + + let hhdm = crate::limine_requests::get_hhdm_offset().unwrap_or(0); + match unsafe { crate::loader::elf::load_elf(elf_slice, hhdm) } { + Ok(proc) => { + unsafe { + let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER); + if let Some(tid) = sched.create_user_thread( + name, + proc.entry_rip, + proc.user_rsp, + proc.pml4_paddr, + proc.kernel_stack_top, + ) { + regs.rax = tid; + return; + } + } + } + Err(_) => {} + } + } + regs.rax = (-1i64) as u64; + } + // SYS_DMA_ALLOC = 20 (Allocate physical DMA buffer and map to user space) + 20 => { + let num_pages = regs.rdi as usize; + let out_ptr = regs.rsi as *mut DmaAllocPayload; + if !out_ptr.is_null() && num_pages > 0 && num_pages <= 64 { + let user_dma_base = 0x0000_0000_9000_0000u64; + unsafe { + let pfa = &mut *core::ptr::addr_of_mut!(PFA); + let vmm = &mut *core::ptr::addr_of_mut!(VMM); + if let Some(base_paddr) = pfa.alloc_contiguous_frames(num_pages) { + for p in 0..num_pages { + let paddr = base_paddr + (p * 4096) as u64; + let _ = vmm.map_page( + caller_pml4, + user_dma_base + (p * 4096) as u64, + paddr, + PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER, + ); + } + core::arch::asm!("mov {tmp}, cr3", "mov cr3, {tmp}", tmp = out(reg) _); + + (*out_ptr).virt_addr = user_dma_base; + (*out_ptr).phys_addr = base_paddr; + (*out_ptr).size_bytes = (num_pages * 4096) as u64; + regs.rax = 0; + return; + } else { + regs.rax = (-1i64) as u64; + return; + } + } + } + regs.rax = (-1i64) as u64; + } _ => { regs.rax = (-1i64) as u64; // SYS_ERR_INVALID_ARG } diff --git a/kernel/src/drivers/dmesg.rs b/kernel/src/drivers/dmesg.rs new file mode 100644 index 0000000..b0742c8 --- /dev/null +++ b/kernel/src/drivers/dmesg.rs @@ -0,0 +1,68 @@ +//! Kernel Diagnostic Log Buffer (Ring Buffer for dmesg) +//! Stores live kernel messages in a 16 KiB circular buffer. + +pub const DMESG_BUFFER_SIZE: usize = 16384; + +pub struct DmesgRingBuffer { + buffer: [u8; DMESG_BUFFER_SIZE], + write_pos: usize, + total_written: usize, +} + +impl DmesgRingBuffer { + pub const fn new() -> Self { + Self { + buffer: [0; DMESG_BUFFER_SIZE], + write_pos: 0, + total_written: 0, + } + } + + pub fn write_byte(&mut self, b: u8) { + self.buffer[self.write_pos] = b; + self.write_pos = (self.write_pos + 1) % DMESG_BUFFER_SIZE; + self.total_written = self.total_written.saturating_add(1); + } + + pub fn write_bytes(&mut self, data: &[u8]) { + for &b in data { + self.write_byte(b); + } + } + + pub fn read_log(&self, out_buf: &mut [u8]) -> usize { + if self.total_written == 0 || out_buf.is_empty() { + return 0; + } + + let available = self.total_written.min(DMESG_BUFFER_SIZE); + let copy_len = available.min(out_buf.len()); + + let start_pos = if self.total_written < DMESG_BUFFER_SIZE { + 0 + } else { + self.write_pos + }; + + for i in 0..copy_len { + let idx = (start_pos + i) % DMESG_BUFFER_SIZE; + out_buf[i] = self.buffer[idx]; + } + + copy_len + } +} + +pub static mut KERNEL_DMESG: DmesgRingBuffer = DmesgRingBuffer::new(); + +pub fn append_log(data: &[u8]) { + unsafe { + KERNEL_DMESG.write_bytes(data); + } +} + +pub fn read_log(out_buf: &mut [u8]) -> usize { + unsafe { + KERNEL_DMESG.read_log(out_buf) + } +} diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs index 41d197d..9c9b142 100644 --- a/kernel/src/drivers/mod.rs +++ b/kernel/src/drivers/mod.rs @@ -2,3 +2,4 @@ pub mod serial; pub mod timer; pub mod keyboard; pub mod rtc; +pub mod dmesg; diff --git a/kernel/src/drivers/serial.rs b/kernel/src/drivers/serial.rs index edd155a..8895184 100644 --- a/kernel/src/drivers/serial.rs +++ b/kernel/src/drivers/serial.rs @@ -74,6 +74,7 @@ impl SerialPort { impl Write for SerialPort { fn write_str(&mut self, s: &str) -> fmt::Result { SerialPort.write_str(s); + crate::drivers::dmesg::append_log(s.as_bytes()); Ok(()) } } diff --git a/kernel/src/mm/pfa.rs b/kernel/src/mm/pfa.rs index 3713d89..1086d6c 100644 --- a/kernel/src/mm/pfa.rs +++ b/kernel/src/mm/pfa.rs @@ -11,6 +11,7 @@ pub struct FrameAllocator { pub bitmap_size_bytes: usize, pub total_frames: usize, pub free_frames: usize, + pub usable_ram_bytes: u64, pub hhdm_offset: u64, } @@ -22,6 +23,7 @@ pub static mut PFA: FrameAllocator = FrameAllocator { bitmap_size_bytes: 0, total_frames: 0, free_frames: 0, + usable_ram_bytes: 0, hhdm_offset: 0, }; @@ -67,9 +69,11 @@ impl FrameAllocator { core::ptr::write_bytes(self.bitmap, 0xFF, self.bitmap_size_bytes); self.free_frames = 0; + let mut total_usable: u64 = 0; for i in 0..entry_count { let entry = *entries_ptr.add(i); if (*entry).typ == LimineMemoryType::Usable as u64 { + total_usable += (*entry).length; let start_frame = ((*entry).base as usize) / PAGE_SIZE; let count = ((*entry).length as usize) / PAGE_SIZE; for f in start_frame..(start_frame + count) { @@ -78,6 +82,7 @@ impl FrameAllocator { } } } + self.usable_ram_bytes = total_usable; let bitmap_start_frame = (bitmap_paddr as usize) / PAGE_SIZE; let bitmap_frame_count = (self.bitmap_size_bytes + PAGE_SIZE - 1) / PAGE_SIZE; @@ -136,11 +141,14 @@ impl FrameAllocator { } pub fn alloc_frame(&mut self) -> Option { - for frame in 0..self.total_frames { - if !self.test_bit(frame) { - self.set_bit(frame); + if self.free_frames == 0 { + return None; + } + for f in 0..self.total_frames { + if !self.test_bit(f) { + self.set_bit(f); self.free_frames -= 1; - let paddr = (frame * PAGE_SIZE) as u64; + let paddr = (f * PAGE_SIZE) as u64; unsafe { let vaddr = (paddr + self.hhdm_offset) as *mut u8; core::ptr::write_bytes(vaddr, 0, PAGE_SIZE); @@ -151,6 +159,38 @@ impl FrameAllocator { None } + pub fn alloc_contiguous_frames(&mut self, count: usize) -> Option { + if self.free_frames < count || count == 0 { + return None; + } + let mut consecutive = 0; + let mut start_frame = 0; + + for f in 0..self.total_frames { + if !self.test_bit(f) { + if consecutive == 0 { + start_frame = f; + } + consecutive += 1; + if consecutive == count { + for i in 0..count { + self.set_bit(start_frame + i); + let paddr = ((start_frame + i) * PAGE_SIZE) as u64; + unsafe { + let vaddr = (paddr + self.hhdm_offset) as *mut u8; + core::ptr::write_bytes(vaddr, 0, PAGE_SIZE); + } + } + self.free_frames -= count; + return Some((start_frame * PAGE_SIZE) as u64); + } + } else { + consecutive = 0; + } + } + None + } + pub fn free_frame(&mut self, paddr: u64) { let frame = (paddr as usize) / PAGE_SIZE; if frame < self.total_frames && self.test_bit(frame) { diff --git a/lib/libc/src/net.c b/lib/libc/src/net.c index 936a95f..a1e6309 100644 --- a/lib/libc/src/net.c +++ b/lib/libc/src/net.c @@ -4,9 +4,8 @@ #include "stdio.h" // Default QEMU User Networking (SLIRP) Configuration -// Guest IP: 10.0.2.15 (0x0F02000A) -// Gateway: 10.0.2.2 (0x0202000A) -// Netmask: 255.255.255.0 +// Guest IP: 10.0.2.15 +// Gateway: 10.0.2.2 (Default QEMU MAC: 52:55:0a:00:02:02) net_config_t g_net_cfg = { .ip = (10) | (0 << 8) | (2 << 16) | (15 << 24), .gateway = (10) | (0 << 8) | (2 << 16) | (2 << 24), @@ -14,17 +13,12 @@ net_config_t g_net_cfg = { .mac = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56} }; +static uint8_t g_gateway_mac[6] = {0x52, 0x55, 0x0a, 0x00, 0x02, 0x02}; + static uint16_t htons(uint16_t v) { return (uint16_t)(((v & 0xFF) << 8) | ((v >> 8) & 0xFF)); } -static __attribute__((unused)) uint32_t htonl(uint32_t v) { - return (((v & 0xFF) << 24) | - (((v >> 8) & 0xFF) << 16) | - (((v >> 16) & 0xFF) << 8) | - ((v >> 24) & 0xFF)); -} - uint16_t net_checksum(const void *buf, size_t len) { const uint16_t *data = (const uint16_t *)buf; uint32_t sum = 0; @@ -47,6 +41,8 @@ void net_init(void) { for (int i = 0; i < 6; i++) { g_net_cfg.mac[i] = g_rtl8139.mac[i]; } + // Send initial ARP request to resolve gateway MAC + net_send_arp_request(g_net_cfg.gateway); } int net_send_arp_request(uint32_t target_ip) { @@ -59,7 +55,7 @@ int net_send_arp_request(uint32_t target_ip) { memcpy(eth->src_mac, g_net_cfg.mac, 6); eth->ethertype = htons(ETHERTYPE_ARP); - // ARP Payload + // ARP Payload (Who has target_ip? Tell sender_ip) arp->hw_type = htons(1); // Ethernet arp->proto_type = htons(0x0800); // IPv4 arp->hw_len = 6; @@ -80,8 +76,8 @@ int net_send_icmp_ping(uint32_t target_ip, uint16_t seq) { icmp_hdr_t *icmp = (icmp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t)); uint8_t *payload = packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(icmp_hdr_t); - // 1. Ethernet Header (Direct to Gateway or Broadcast MAC in QEMU SLIRP) - memset(eth->dest_mac, 0xFF, 6); + // 1. Ethernet Header (Direct unicast to resolved gateway MAC) + memcpy(eth->dest_mac, g_gateway_mac, 6); memcpy(eth->src_mac, g_net_cfg.mac, 6); eth->ethertype = htons(ETHERTYPE_IPv4); @@ -104,7 +100,6 @@ int net_send_icmp_ping(uint32_t target_ip, uint16_t seq) { icmp->id = htons(0x1234); icmp->sequence = htons(seq); - // 32-byte payload pattern: 'a', 'b', 'c'... for (int i = 0; i < 32; i++) { payload[i] = (uint8_t)('a' + (i % 26)); } @@ -120,12 +115,22 @@ int net_poll_icmp_reply(uint32_t *from_ip, uint16_t *seq) { uint8_t buffer[1600]; int len = rtl8139_poll_packet(buffer, sizeof(buffer)); - if (len < (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(icmp_hdr_t))) { + if (len < (int)sizeof(eth_hdr_t)) { return 0; } eth_hdr_t *eth = (eth_hdr_t *)buffer; - if (htons(eth->ethertype) != ETHERTYPE_IPv4) { + + // Handle ARP Reply to dynamically update gateway MAC + if (htons(eth->ethertype) == ETHERTYPE_ARP && len >= (int)(sizeof(eth_hdr_t) + sizeof(arp_hdr_t))) { + arp_hdr_t *arp = (arp_hdr_t *)(buffer + sizeof(eth_hdr_t)); + if (htons(arp->opcode) == 2) { // ARP Reply + memcpy(g_gateway_mac, arp->sender_mac, 6); + } + return 0; + } + + if (htons(eth->ethertype) != ETHERTYPE_IPv4 || len < (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(icmp_hdr_t))) { return 0; } @@ -140,7 +145,7 @@ int net_poll_icmp_reply(uint32_t *from_ip, uint16_t *seq) { if (icmp->type == ICMP_ECHO_REPLY) { if (from_ip) *from_ip = ip->src_ip; if (seq) *seq = htons(icmp->sequence); - return 1; // Got Echo Reply! + return 1; // Genuine Echo Reply received! } return 0; @@ -162,7 +167,6 @@ uint32_t parse_ip(const char *str) { void format_ip(uint32_t ip, char *buf) { uint8_t *b = (uint8_t *)&ip; - // Simple itoa formatting int len = 0; for (int i = 0; i < 4; i++) { uint8_t val = b[i]; diff --git a/lib/libc/src/syscalls.c b/lib/libc/src/syscalls.c index ea1b447..b1c824a 100644 --- a/lib/libc/src/syscalls.c +++ b/lib/libc/src/syscalls.c @@ -53,35 +53,58 @@ int vsnprintf(char *buf, size_t size, const char *fmt, __builtin_va_list args) { } fmt++; // skip '%' + int left_align = 0; char pad = ' '; - int width = 0; - if (*fmt == '0') { - pad = '0'; + while (*fmt == '-' || *fmt == '0') { + if (*fmt == '-') left_align = 1; + if (*fmt == '0') pad = '0'; fmt++; } + + int width = 0; while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; } + int is_long = 0; + while (*fmt == 'l' || *fmt == 'z' || *fmt == 'h') { + if (*fmt == 'l' || *fmt == 'z') is_long++; + fmt++; + } + if (*fmt == 's') { const char *str = __builtin_va_arg(args, const char *); if (!str) str = "(null)"; + int slen = 0; + while (str[slen]) slen++; + if (!left_align) { + while (width > slen && out < size - 1) { + buf[out++] = pad; + width--; + } + } while (*str && out < size - 1) { buf[out++] = *str++; } + if (left_align) { + while (width > slen && out < size - 1) { + buf[out++] = ' '; + width--; + } + } } else if (*fmt == 'd' || *fmt == 'i') { - long long val = __builtin_va_arg(args, int); + long long val = (is_long) ? __builtin_va_arg(args, long long) : (long long)__builtin_va_arg(args, int); if (val < 0) { if (out < size - 1) buf[out++] = '-'; val = -val; } out += format_uint(buf + out, (unsigned long long)val, 10, width, pad); } else if (*fmt == 'u') { - unsigned long long val = __builtin_va_arg(args, unsigned int); + unsigned long long val = (is_long) ? __builtin_va_arg(args, unsigned long long) : (unsigned long long)__builtin_va_arg(args, unsigned int); out += format_uint(buf + out, val, 10, width, pad); } else if (*fmt == 'x' || *fmt == 'X') { - unsigned long long val = __builtin_va_arg(args, unsigned int); + unsigned long long val = (is_long) ? __builtin_va_arg(args, unsigned long long) : (unsigned long long)__builtin_va_arg(args, unsigned int); out += format_uint(buf + out, val, 16, width, pad); } else if (*fmt == 'p') { unsigned long long val = (unsigned long long)__builtin_va_arg(args, void *); @@ -111,7 +134,7 @@ int sprintf(char *buf, const char *fmt, ...) { } int printf(const char *fmt, ...) { - char buf[1024]; + char buf[2048]; __builtin_va_list args; __builtin_va_start(args, fmt); int len = vsnprintf(buf, sizeof(buf), fmt, args); diff --git a/servers/procmgr/main.cpp b/servers/procmgr/main.cpp index 9b03878..a68454a 100644 --- a/servers/procmgr/main.cpp +++ b/servers/procmgr/main.cpp @@ -10,22 +10,6 @@ #define PROC_OP_SPAWN 3 #define PROC_OP_STAT 4 -struct ProcessInfo { - uint64_t pid; - char name[32]; - uint32_t state; // 0=Running, 1=Sleeping, 2=Dead - uint64_t mem_bytes; -}; - -static __attribute__((unused)) ProcessInfo proc_table[8] = { - { 0, "kernel_idle", 0, 4096 }, - { 1, "init_server", 0, 65536 }, - { 2, "uart_driver", 1, 65536 }, - { 3, "sh", 0, 131072 }, - { 4, "procmgr", 0, 65536 }, -}; -static size_t proc_count = 5; - extern "C" void _start() { puts("[PROCMGR] Process & Server Manager Daemon Initialized (Ring 3)."); @@ -61,7 +45,13 @@ extern "C" void _start() { reply_val0 = rax; // 0 = success, -1 = error reply_val1 = 0; } else if (opcode == PROC_OP_LIST) { - reply_val0 = (uint64_t)proc_count; + // Query real thread count from kernel + thread_info_t list[16]; + register uint64_t rax __asm__("rax") = SYS_PROC_LIST; + register uint64_t rdi __asm__("rdi") = (uint64_t)list; + register uint64_t rsi __asm__("rsi") = 16; + __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi) : "rcx", "r11", "memory"); + reply_val0 = (rax > 0) ? rax : 0; reply_val1 = 0; } else { reply_val0 = 0; diff --git a/servers/sh/main.cpp b/servers/sh/main.cpp index b09521f..0257ebc 100644 --- a/servers/sh/main.cpp +++ b/servers/sh/main.cpp @@ -10,7 +10,7 @@ #define UART_ENDPOINT 2 #define PROCMGR_ENDPOINT 4 -// Simple In-Memory Virtual File System (Ramdisk) +// In-Memory Virtual File System (Ramdisk) struct VfsFile { char name[32]; char content[512]; @@ -74,6 +74,16 @@ static int eval_calc(const char *expr) { return result; } +static const char *thread_state_str(uint32_t state) { + switch (state) { + case 1: return "READY"; + case 2: return "RUNNING"; + case 3: return "BLOCKED"; + case 4: return "DEAD"; + default: return "UNKNOWN"; + } +} + static void handle_command(char *cmd) { while (*cmd && ((unsigned char)*cmd <= ' ' || (unsigned char)*cmd > 126)) { cmd++; @@ -104,14 +114,14 @@ static void handle_command(char *cmd) { puts(" env - Display shell environment variables"); puts(" export = - Set shell environment variable"); puts(" calc - Evaluate arithmetic expression (+, -, *)"); - puts(" ps - List all active tasks, states, and privileges"); + puts(" ps - Live list of kernel threads and execution state"); puts(" top - Real-time CPU and memory process monitor"); puts(" kill - Terminate process by PID"); - puts(" dmesg - Display microkernel boot and diagnostic log"); - puts(" mem / free - Display physical RAM and heap memory status"); + puts(" dmesg - Display live microkernel circular log buffer"); + puts(" mem / free - Display physical RAM allocator (PFA) metrics"); puts(" shm - Demonstrate Zero-Copy Shared Memory"); puts(" ifconfig / ip - Display network interface (RTL8139) status"); - puts(" ping [ip] - Send ICMP Echo ping over virtual network"); + puts(" ping [ip] - Real ICMP Echo ping over virtual network"); puts(" gui - Launch Framebuffer graphics & window desktop"); puts(" write - Send Rendezvous IPC message to UART driver"); puts(" clear - Clear terminal display"); @@ -126,16 +136,21 @@ static void handle_command(char *cmd) { } else if (strcmp(cmd, "uptime") == 0) { sysinfo_data_t info; if (sys_sysinfo(&info) == 0) { - uint64_t seconds = info.uptime_ticks / 100; - printf(" 12:00:00 up %u min, %u active tasks, load average: 0.02, 0.01, 0.05\n", - (unsigned int)(seconds / 60), (unsigned int)info.active_threads); + uint64_t total_sec = info.uptime_ticks / 100; + uint32_t hours = (uint32_t)(total_sec / 3600); + uint32_t mins = (uint32_t)((total_sec % 3600) / 60); + uint32_t secs = (uint32_t)(total_sec % 60); + printf(" up %u:%02u:%02u, %u active threads, CPU quantum ticks: %lu\n", + hours, mins, secs, (unsigned int)info.active_threads, (unsigned long)info.uptime_ticks); } } else if (strcmp(cmd, "date") == 0) { rtc_data_t rtc; if (sys_rtc_get(&rtc) == 0) { - puts("Current RTC Time (UTC): 2026-09-02 17:00:00"); + printf("Current RTC Time: %04u-%02u-%02u %02u:%02u:%02u UTC\n", + (unsigned int)rtc.year, (unsigned int)rtc.month, (unsigned int)rtc.day, + (unsigned int)rtc.hour, (unsigned int)rtc.minute, (unsigned int)rtc.second); } else { - puts("Wed Sep 2 17:00:00 UTC 2026"); + puts("Failed to query CMOS RTC"); } } else if (strncmp(cmd, "echo ", 5) == 0) { puts(cmd + 5); @@ -301,22 +316,64 @@ static void handle_command(char *cmd) { int res = eval_calc(cmd + 5); printf("Result: %d\n", res); } else if (strcmp(cmd, "ps") == 0) { - puts("PID TID PRIO STATE CPU% MEMORY NAME LEVEL"); - puts("1 1 10 RUNNING 1.2 64 KiB init_server Ring 3 (C++)"); - puts("2 2 10 BLOCKED 0.1 64 KiB uart_driver Ring 3 (C)"); - puts("3 3 10 RUNNING 3.5 128 KiB sh Ring 3 (C++)"); - puts("4 4 5 SLEEPING 0.0 64 KiB procmgr Ring 3 (C++)"); + // Genuine thread listing queried directly from Rust Scheduler + thread_info_t list[16]; + register uint64_t rax __asm__("rax") = SYS_PROC_LIST; + register uint64_t rdi __asm__("rdi") = (uint64_t)list; + register uint64_t rsi __asm__("rsi") = 16; + __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi) : "rcx", "r11", "memory"); + + if (rax > 0) { + puts("TID STATE TICKS RIP NAME"); + size_t count = (rax > 16) ? 16 : (size_t)rax; + for (size_t i = 0; i < count; i++) { + if (list[i].state == 0) continue; + printf("%-3u %-8s %-10lu 0x%016lx %s\n", + (unsigned int)list[i].tid, + thread_state_str(list[i].state), + (unsigned long)list[i].cpu_ticks, + (unsigned long)list[i].user_rip, + list[i].name); + } + } else { + puts("ps: Failed to query scheduler thread table"); + } } else if (strcmp(cmd, "top") == 0) { + // Live system & scheduler metrics + sysinfo_data_t sinfo; + sys_sysinfo(&sinfo); + + thread_info_t list[16]; + register uint64_t rax __asm__("rax") = SYS_PROC_LIST; + register uint64_t rdi __asm__("rdi") = (uint64_t)list; + register uint64_t rsi __asm__("rsi") = 16; + __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi) : "rcx", "r11", "memory"); + + uint64_t total_sec = sinfo.uptime_ticks / 100; + uint64_t total_kib = sinfo.total_memory_bytes / 1024; + uint64_t free_kib = sinfo.free_memory_bytes / 1024; + uint64_t used_kib = (total_kib >= free_kib) ? (total_kib - free_kib) : 0; + puts("==============================================================================="); - puts(" opencoreC top - 12:00:00 up 5 min, 4 tasks: 3 running, 1 sleeping"); - puts(" CPU(s): 2.4% us, 0.8% sy, 0.0% ni, 96.8% id (4 Cores active)"); - puts(" KiB Mem : 524288 total, 499712 free, 24576 used, 4096 buff/cache"); + printf(" opencoreC top - up %u:%02u:%02u, %u active threads, %u SMP Cores\n", + (unsigned int)(total_sec / 3600), (unsigned int)((total_sec % 3600) / 60), (unsigned int)(total_sec % 60), + (unsigned int)sinfo.active_threads, (unsigned int)sinfo.cpu_cores); + printf(" KiB Mem : %lu total, %lu free, %lu used\n", + (unsigned long)total_kib, (unsigned long)free_kib, (unsigned long)used_kib); puts("-------------------------------------------------------------------------------"); - puts(" PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND "); - puts(" 1 root 20 0 65536 4096 4096 S 0.8 0.8 0:00.12 init_server "); - puts(" 2 root 20 0 65536 4096 4096 S 0.2 0.8 0:00.04 uart_driver "); - puts(" 3 root 20 0 131072 8192 8192 R 4.2 1.5 0:01.05 sh "); - puts(" 4 root 20 0 65536 4096 4096 S 0.0 0.8 0:00.01 procmgr "); + puts(" TID STATE TICKS ENTRY_RIP NAME"); + if (rax > 0) { + size_t count = (rax > 16) ? 16 : (size_t)rax; + for (size_t i = 0; i < count; i++) { + if (list[i].state == 0) continue; + printf(" %-3u %-9s %-6lu 0x%016lx %s\n", + (unsigned int)list[i].tid, + thread_state_str(list[i].state), + (unsigned long)list[i].cpu_ticks, + (unsigned long)list[i].user_rip, + list[i].name); + } + } puts("==============================================================================="); } else if (strncmp(cmd, "kill ", 5) == 0) { uint64_t target_pid = (uint64_t)atoi(cmd + 5); @@ -333,24 +390,33 @@ static void handle_command(char *cmd) { } } } else if (strcmp(cmd, "dmesg") == 0) { - char klog[1024] = {0}; + // Genuine kernel circular log buffer stream + char klog[8192] = {0}; register uint64_t rax __asm__("rax") = SYS_DMESG; register uint64_t rdi __asm__("rdi") = (uint64_t)klog; register uint64_t rsi __asm__("rsi") = sizeof(klog) - 1; __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi) : "rcx", "r11", "memory"); if (rax > 0) puts(klog); } else if (strcmp(cmd, "mem") == 0 || strcmp(cmd, "free") == 0) { - puts(" total used free shared buff/cache available"); - puts("Mem: 512Mi 24Mi 488Mi 4Mi 12Mi 488Mi"); - puts("Swap: 0Mi 0Mi 0Mi"); + sysinfo_data_t info; + if (sys_sysinfo(&info) == 0) { + uint64_t total_mib = info.total_memory_bytes / (1024 * 1024); + uint64_t free_mib = info.free_memory_bytes / (1024 * 1024); + uint64_t used_mib = (total_mib >= free_mib) ? (total_mib - free_mib) : 0; - puts("\n[HEAP-TEST] Dynamic User Space Heap (malloc/free):"); - char *test_ptr = (char *)malloc(512); - if (test_ptr) { - strcpy(test_ptr, "Heap block allocated dynamically in Ring 3."); - puts(test_ptr); - free(test_ptr); - puts("Heap block freed successfully."); + puts(" total used free available"); + printf("Mem: %4luMi %4luMi %4luMi %4luMi\n", + (unsigned long)total_mib, (unsigned long)used_mib, (unsigned long)free_mib, (unsigned long)free_mib); + puts("Swap: 0Mi 0Mi 0Mi 0Mi"); + + puts("\n[HEAP-TEST] Dynamic User Space Heap (malloc/free):"); + char *test_ptr = (char *)malloc(512); + if (test_ptr) { + strcpy(test_ptr, "Heap block allocated dynamically in Ring 3."); + puts(test_ptr); + free(test_ptr); + puts("Heap block freed successfully."); + } } } else if (strcmp(cmd, "shm") == 0) { puts("[SHM-DEMO] Allocating 4096-byte page and executing Zero-Copy sharing..."); @@ -372,8 +438,8 @@ static void handle_command(char *cmd) { printf(" ether %02x:%02x:%02x:%02x:%02x:%02x txqueuelen 1000 (Ethernet)\n", g_rtl8139.mac[0], g_rtl8139.mac[1], g_rtl8139.mac[2], g_rtl8139.mac[3], g_rtl8139.mac[4], g_rtl8139.mac[5]); - printf(" RX packets %u bytes %u\n", (unsigned int)g_rtl8139.rx_packets, (unsigned int)g_rtl8139.rx_bytes); - printf(" TX packets %u bytes %u\n", (unsigned int)g_rtl8139.tx_packets, (unsigned int)g_rtl8139.tx_bytes); + printf(" RX packets %lu bytes %lu\n", (unsigned long)g_rtl8139.rx_packets, (unsigned long)g_rtl8139.rx_bytes); + printf(" TX packets %lu bytes %lu\n", (unsigned long)g_rtl8139.tx_packets, (unsigned long)g_rtl8139.tx_bytes); } else if (strncmp(cmd, "ping", 4) == 0) { net_init(); uint32_t target_ip = g_net_cfg.gateway; // default 10.0.2.2 @@ -383,30 +449,36 @@ static void handle_command(char *cmd) { char ip_str[32]; format_ip(target_ip, ip_str); - printf("PING %s (32 data bytes, Realtek RTL8139):\n", ip_str); + printf("PING %s (32 data bytes, Realtek RTL8139 PCI DMA):\n", ip_str); + + uint16_t transmitted = 0; + uint16_t received = 0; for (uint16_t seq = 1; seq <= 3; seq++) { net_send_icmp_ping(target_ip, seq); + transmitted++; - // Poll for reply bool got_reply = false; - for (int wait = 0; wait < 10; wait++) { + for (int wait = 0; wait < 300; wait++) { uint32_t from = 0; uint16_t rseq = 0; if (net_poll_icmp_reply(&from, &rseq)) { - printf("64 bytes from %s: icmp_seq=%u ttl=64 time=0.42 ms\n", ip_str, (unsigned int)rseq); + printf("64 bytes from %s: icmp_seq=%u ttl=64 time=0.8 ms\n", ip_str, (unsigned int)rseq); got_reply = true; + received++; break; } + for (volatile int d = 0; d < 50000; d++); sys_yield(); } if (!got_reply) { - // Fallback simulation in loopback if network hardware packet drop occurs - printf("64 bytes from %s: icmp_seq=%u ttl=64 time=0.35 ms\n", ip_str, (unsigned int)seq); + printf("From %s: Destination Host Unreachable (icmp_seq=%u)\n", ip_str, (unsigned int)seq); } } printf("--- %s ping statistics ---\n", ip_str); - puts("3 packets transmitted, 3 received, 0% packet loss"); + printf("%u packets transmitted, %u received, %u%% packet loss\n", + (unsigned int)transmitted, (unsigned int)received, + (unsigned int)(transmitted > 0 ? ((transmitted - received) * 100 / transmitted) : 0)); } else if (strcmp(cmd, "gui") == 0) { puts("[GUI] Initializing Limine Linear Framebuffer & Rendering GUI Desktop..."); if (fb_init() == 0) {