From ca75e961f90b9a086eb04c7f1d00e24d5f0b35b8 Mon Sep 17 00:00:00 2001 From: RarDog Date: Thu, 3 Sep 2026 10:20:25 +0300 Subject: [PATCH] feat: native .opc executable binary format, hardware PCI GPU driver, terminal text editor, and web browser --- Makefile | 7 + apps/hello.c | 13 ++ hal/include/pci.h | 17 +++ hal/src/pci.c | 185 ++++++++++++++++++++++++++ include/abi/net.h | 20 +++ include/abi/opc.h | 52 ++++++++ include/abi/syscalls.h | 17 +++ kernel/src/arch/syscall.rs | 121 ++++++++++++++++- kernel/src/loader/mod.rs | 1 + kernel/src/loader/opc.rs | 136 +++++++++++++++++++ kernel/src/main.rs | 15 +++ lib/libc/src/net.c | 102 +++++++++++++++ limine.cfg | 22 ++-- limine.conf | 1 + servers/gui_server/main.cpp | 147 ++++++++++++++++++++- servers/sh/main.cpp | 254 +++++++++++++++++++++++++++++++++++- tools/elf2opc.py | 122 +++++++++++++++++ 17 files changed, 1213 insertions(+), 19 deletions(-) create mode 100644 apps/hello.c create mode 100644 include/abi/opc.h create mode 100644 kernel/src/loader/opc.rs create mode 100755 tools/elf2opc.py diff --git a/Makefile b/Makefile index 74b9935..e36df44 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,7 @@ UART_DRIVER := $(BUILD_DIR)/uart_driver.elf SH_SERVER := $(BUILD_DIR)/sh.elf PROCMGR := $(BUILD_DIR)/procmgr.elf GUI_SERVER := $(BUILD_DIR)/gui_server.elf +HELLO_APP := $(BUILD_DIR)/hello.opc LVGL_LIB := $(BUILD_DIR)/liblvgl.a DISK_IMG := $(BUILD_DIR)/opencoreC.img @@ -110,6 +111,11 @@ servers: libc hal $(LVGL_LIB) @$(CXX) $(GUI_CXXFLAGS) -Ilib/lvgl -c $(SERVERS_DIR)/gui_server/main.cpp -o $(BUILD_DIR)/gui_main.o @$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(GUI_SERVER) \ $(BUILD_DIR)/gui_main.o $(BUILD_DIR)/ipc.o $(NET_OBJS) $(LVGL_LIB) $(LIBC_OBJS) + @# Native .opc Executable Application (hello.opc) + @$(CC) $(CFLAGS) -c apps/hello.c -o $(BUILD_DIR)/hello.o + @$(LD) -T $(CONFIG_DIR)/user.ld -nostdlib -static -o $(BUILD_DIR)/hello.elf \ + $(BUILD_DIR)/hello.o $(LIBC_OBJS) + @python3 tools/elf2opc.py $(BUILD_DIR)/hello.elf $(HELLO_APP) # Download and build the Limine bootloader host tool limine-setup: @@ -134,6 +140,7 @@ image: all limine-setup @mcopy -i $(DISK_IMG)@@1M $(SH_SERVER) ::/boot/ @mcopy -i $(DISK_IMG)@@1M $(PROCMGR) ::/boot/ @mcopy -i $(DISK_IMG)@@1M $(GUI_SERVER) ::/boot/ + @mcopy -i $(DISK_IMG)@@1M $(HELLO_APP) ::/boot/ @# Bootloader config @mcopy -i $(DISK_IMG)@@1M limine.conf ::/ @mcopy -i $(DISK_IMG)@@1M limine.cfg ::/ diff --git a/apps/hello.c b/apps/hello.c new file mode 100644 index 0000000..6efa72d --- /dev/null +++ b/apps/hello.c @@ -0,0 +1,13 @@ +#include "abi/syscalls.h" +#include "stdio.h" + +void _start(void) { + puts("\n======================================================="); + puts(" 🎉 Hello from Native opencoreC Binary (.opc)! 🎉"); + puts(" Binary Format: .OPC (OpenCore Executable v1.0)"); + puts(" Architecture: x86_64 Long Mode (Ring 3 Userspace)"); + puts(" Sandbox: Isolated Virtual Memory (PML4)"); + puts("=======================================================\n"); + + sys_exit(0); +} diff --git a/hal/include/pci.h b/hal/include/pci.h index 1137a38..f50cd35 100644 --- a/hal/include/pci.h +++ b/hal/include/pci.h @@ -17,6 +17,17 @@ typedef struct pci_device { uint8_t header_type; } pci_device_t; +typedef struct gpu_info { + uint16_t vendor_id; + uint16_t device_id; + uint8_t bus; + uint8_t slot; + uint8_t func; + uint32_t vram_mb; + char vendor_name[32]; + char model_name[64]; +} gpu_info_t; + #ifdef __cplusplus extern "C" { #endif @@ -25,6 +36,12 @@ uint32_t pci_read_config_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t void pci_write_config_dword(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t val); uint16_t pci_get_vendor_id(uint8_t bus, uint8_t slot, uint8_t func); uint16_t pci_get_device_id(uint8_t bus, uint8_t slot, uint8_t func); +uint32_t pci_get_bar_size(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_idx); + +const char *pci_class_to_string(uint8_t class_code, uint8_t subclass); +const char *pci_vendor_to_string(uint16_t vendor_id); + +int pci_detect_gpu(gpu_info_t *out_gpu); typedef void (*pci_scan_callback_t)(const pci_device_t *dev); void pci_scan_bus(pci_scan_callback_t callback); diff --git a/hal/src/pci.c b/hal/src/pci.c index dde4b0d..b210792 100644 --- a/hal/src/pci.c +++ b/hal/src/pci.c @@ -1,4 +1,5 @@ #include "../include/pci.h" +#include "string.h" static inline void outl(uint16_t port, uint32_t val) { __asm__ volatile("outl %0, %1" : : "a"(val), "Nd"(port)); @@ -40,6 +41,190 @@ uint16_t pci_get_device_id(uint8_t bus, uint8_t slot, uint8_t func) { return (uint16_t)((dword >> 16) & 0xFFFF); } +uint32_t pci_get_bar_size(uint8_t bus, uint8_t slot, uint8_t func, uint8_t bar_idx) { + if (bar_idx > 5) return 0; + uint8_t offset = 0x10 + (bar_idx * 4); + uint32_t orig = pci_read_config_dword(bus, slot, func, offset); + if (orig == 0) return 0; + + // Write all 1s to query size + pci_write_config_dword(bus, slot, func, offset, 0xFFFFFFFF); + uint32_t mask = pci_read_config_dword(bus, slot, func, offset); + pci_write_config_dword(bus, slot, func, offset, orig); + + if (mask == 0 || mask == 0xFFFFFFFF) return 0; + + if ((orig & 1) == 0) { + // Memory BAR + uint32_t size = ~(mask & 0xFFFFFFF0) + 1; + return size; + } else { + // I/O BAR + uint32_t size = ~(mask & 0xFFFFFFFC) + 1; + return size; + } +} + +const char *pci_vendor_to_string(uint16_t vendor_id) { + switch (vendor_id) { + case 0x10DE: return "NVIDIA Corporation"; + case 0x1002: return "Advanced Micro Devices (AMD/ATI)"; + case 0x8086: return "Intel Corporation"; + case 0x1234: return "QEMU / Bochs"; + case 0x1AF4: return "Red Hat (VirtIO)"; + case 0x15AD: return "VMware Inc."; + case 0x80EE: return "Oracle VirtualBox"; + case 0x10EC: return "Realtek Semiconductor"; + default: return "Unknown Vendor"; + } +} + +const char *pci_class_to_string(uint8_t class_code, uint8_t subclass) { + switch (class_code) { + case 0x01: + switch (subclass) { + case 0x01: return "IDE Controller"; + case 0x06: return "SATA AHCI Controller"; + case 0x08: return "NVMe Controller"; + default: return "Mass Storage Controller"; + } + case 0x02: + switch (subclass) { + case 0x00: return "Ethernet Controller"; + default: return "Network Controller"; + } + case 0x03: + switch (subclass) { + case 0x00: return "VGA Compatible Display Controller"; + case 0x02: return "3D Graphics Accelerator"; + default: return "Display Controller"; + } + case 0x04: + return "Multimedia Audio Device"; + case 0x06: + switch (subclass) { + case 0x00: return "Host / PCI Bridge"; + case 0x01: return "ISA Bridge"; + case 0x04: return "PCI-to-PCI Bridge"; + default: return "Bridge Device"; + } + case 0x0C: + switch (subclass) { + case 0x03: return "USB Controller"; + default: return "Serial Bus Controller"; + } + default: + return "Generic System Peripheral"; + } +} + +int pci_detect_gpu(gpu_info_t *out_gpu) { + if (!out_gpu) return -1; + memset(out_gpu, 0, sizeof(gpu_info_t)); + + 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) continue; + + uint32_t hdr_dword = pci_read_config_dword((uint8_t)bus, slot, 0, 0x0C); + uint8_t header_type = (uint8_t)((hdr_dword >> 16) & 0xFF); + uint8_t num_funcs = (header_type & 0x80) ? 8 : 1; + + for (uint8_t func = 0; func < num_funcs; func++) { + uint16_t func_vendor = pci_get_vendor_id((uint8_t)bus, slot, func); + if (func_vendor == 0xFFFF || func_vendor == 0x0000) continue; + + uint32_t class_dword = pci_read_config_dword((uint8_t)bus, slot, func, 0x08); + uint8_t class_code = (uint8_t)((class_dword >> 24) & 0xFF); + + if (class_code == 0x03) { // Display controller found! + uint16_t dev_id = pci_get_device_id((uint8_t)bus, slot, func); + out_gpu->vendor_id = func_vendor; + out_gpu->device_id = dev_id; + out_gpu->bus = (uint8_t)bus; + out_gpu->slot = slot; + out_gpu->func = func; + + // Calculate VRAM size from BARs + uint32_t max_bar_size = 0; + for (int b = 0; b < 6; b++) { + uint32_t sz = pci_get_bar_size((uint8_t)bus, slot, func, b); + if (sz > max_bar_size) { + max_bar_size = sz; + } + } + out_gpu->vram_mb = max_bar_size / (1024 * 1024); + if (out_gpu->vram_mb == 0 && max_bar_size > 0) { + out_gpu->vram_mb = 1; + } + + // Copy Vendor Name + const char *vname = pci_vendor_to_string(func_vendor); + strncpy(out_gpu->vendor_name, vname, sizeof(out_gpu->vendor_name) - 1); + + // Decode Specific GPU Models + const char *model = "Generic VGA Display"; + if (func_vendor == 0x10DE) { // NVIDIA + switch (dev_id) { + case 0x2684: model = "GeForce RTX 4090"; break; + case 0x2704: model = "GeForce RTX 4080"; break; + case 0x2786: model = "GeForce RTX 4070"; break; + case 0x2882: model = "GeForce RTX 4060"; break; + case 0x2204: model = "GeForce RTX 3090"; break; + case 0x2206: model = "GeForce RTX 3080"; break; + case 0x2484: model = "GeForce RTX 3070"; break; + case 0x2503: + case 0x2504: model = "GeForce RTX 3060"; break; + case 0x1E04: model = "GeForce RTX 2080 Ti"; break; + case 0x1E82: model = "GeForce RTX 2080"; break; + case 0x1F02: model = "GeForce RTX 2070"; break; + case 0x1F08: model = "GeForce RTX 2060"; break; + case 0x1B06: model = "GeForce GTX 1080 Ti"; break; + case 0x1B80: model = "GeForce GTX 1080"; break; + case 0x1B81: model = "GeForce GTX 1070"; break; + case 0x1C03: model = "GeForce GTX 1060"; break; + case 0x2184: model = "GeForce GTX 1660"; break; + default: model = "GeForce RTX/GTX GPU"; break; + } + } else if (func_vendor == 0x1002) { // AMD + switch (dev_id) { + case 0x744C: model = "Radeon RX 7900 XTX"; break; + case 0x73BF: model = "Radeon RX 6800 XT"; break; + case 0x73DF: model = "Radeon RX 6700 XT"; break; + case 0x731F: model = "Radeon RX 5700 XT"; break; + case 0x67DF: model = "Radeon RX 580"; break; + default: model = "Radeon RX Graphics"; break; + } + } else if (func_vendor == 0x8086) { // Intel + switch (dev_id) { + case 0x56A0: model = "Arc A770 Graphics"; break; + case 0x56A1: model = "Arc A750 Graphics"; break; + case 0x9A49: model = "Iris Xe Graphics"; break; + case 0x4680: model = "UHD Graphics 770"; break; + case 0x3E92: model = "UHD Graphics 630"; break; + default: model = "Intel HD / UHD Graphics"; break; + } + } else if (func_vendor == 0x1234 && dev_id == 0x1111) { + model = "QEMU Bochs Standard VGA"; + if (out_gpu->vram_mb == 0) out_gpu->vram_mb = 16; + } else if (func_vendor == 0x1AF4 && dev_id == 0x1050) { + model = "VirtIO 3D GPU Accelerator"; + if (out_gpu->vram_mb == 0) out_gpu->vram_mb = 64; + } else if (func_vendor == 0x15AD && dev_id == 0x0405) { + model = "VMware SVGA II Adapter"; + } + + strncpy(out_gpu->model_name, model, sizeof(out_gpu->model_name) - 1); + return 0; // Found primary GPU + } + } + } + } + + return -1; // Not found +} + void pci_scan_bus(pci_scan_callback_t callback) { for (uint16_t bus = 0; bus < 4; bus++) { for (uint8_t slot = 0; slot < 32; slot++) { diff --git a/include/abi/net.h b/include/abi/net.h index 4d99f7c..e20cf36 100644 --- a/include/abi/net.h +++ b/include/abi/net.h @@ -57,6 +57,25 @@ typedef struct { uint16_t sequence; } icmp_hdr_t; +typedef struct { + uint16_t src_port; + uint16_t dst_port; + uint32_t seq_num; + uint32_t ack_num; + uint8_t data_offset_reserved; + uint8_t flags; + uint16_t window_size; + uint16_t checksum; + uint16_t urgent_ptr; +} tcp_hdr_t; + +#define TCP_FLAG_FIN 0x01 +#define TCP_FLAG_SYN 0x02 +#define TCP_FLAG_RST 0x04 +#define TCP_FLAG_PSH 0x08 +#define TCP_FLAG_ACK 0x10 +#define TCP_FLAG_URG 0x20 + #pragma pack(pop) typedef struct { @@ -74,6 +93,7 @@ uint16_t net_checksum(const void *buf, size_t len); int net_send_arp_request(uint32_t target_ip); int net_send_icmp_ping(uint32_t target_ip, uint16_t seq); int net_poll_icmp_reply(uint32_t *from_ip, uint16_t *seq); +int net_http_get(uint32_t target_ip, uint16_t port, const char *path, char *out_body, size_t max_body_len); uint32_t parse_ip(const char *str); void format_ip(uint32_t ip, char *buf); diff --git a/include/abi/opc.h b/include/abi/opc.h new file mode 100644 index 0000000..05de66e --- /dev/null +++ b/include/abi/opc.h @@ -0,0 +1,52 @@ +#pragma once + +#include "stdint.h" + +#define OPC_MAGIC 0x0143504F /* 'O', 'P', 'C', 0x01 */ +#define OPC_ARCH_X86_64 0x8664 +#define OPC_VERSION_1 1 + +/* Capability flags for OPC binaries */ +#define OPC_CAP_KERNEL_CTRL (1 << 0) +#define OPC_CAP_UART (1 << 1) +#define OPC_CAP_NET (1 << 2) +#define OPC_CAP_FB (1 << 3) +#define OPC_CAP_NOTIFY (1 << 4) +#define OPC_CAP_ALL 0xFFFFFFFF + +/* Segment flags */ +#define OPC_SEG_X (1 << 0) /* Executable */ +#define OPC_SEG_W (1 << 1) /* Writable */ +#define OPC_SEG_R (1 << 2) /* Readable */ + +#define OPC_SEG_TYPE_LOAD 1 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct opc_header { + uint32_t magic; /* OPC_MAGIC ("OPC\x01") */ + uint16_t version; /* OPC_VERSION_1 */ + uint16_t arch; /* OPC_ARCH_X86_64 */ + uint64_t entry_rip; /* Virtual address of entry point */ + uint64_t stack_size; /* Required user stack size (bytes, e.g. 65536) */ + uint32_t caps_mask; /* Required capability bitmask */ + uint8_t preferred_core; /* Preferred CPU core (0..3, or 0xFF = any) */ + uint8_t num_segments; /* Number of opc_segment_t headers following */ + uint16_t reserved; /* Alignment padding */ +} __attribute__((packed)) opc_header_t; + +typedef struct opc_segment { + uint32_t seg_type; /* OPC_SEG_TYPE_LOAD */ + uint32_t flags; /* OPC_SEG_R | OPC_SEG_W | OPC_SEG_X */ + uint64_t file_offset; /* Offset within the .opc file */ + uint64_t vaddr; /* Target virtual address */ + uint64_t filesz; /* Size in file (bytes) */ + uint64_t memsz; /* Size in memory (bytes, memsz >= filesz, rest is BSS) */ + uint64_t align; /* Alignment requirement (usually 4096) */ +} __attribute__((packed)) opc_segment_t; + +#ifdef __cplusplus +} +#endif diff --git a/include/abi/syscalls.h b/include/abi/syscalls.h index 50fd6e5..52dfb4c 100644 --- a/include/abi/syscalls.h +++ b/include/abi/syscalls.h @@ -29,6 +29,8 @@ #define SYS_CPU_INFO 24 /* Query multi-core CPU core count and current core ID */ #define SYS_NOTIFY_SEND 25 /* Post desktop notification to GUI compositor */ #define SYS_NOTIFY_RECV 26 /* Poll pending desktop notifications non-blockingly */ +#define SYS_MODULE_EXEC 27 /* Find and execute boot module by name (.opc or .elf) */ +#define SYS_MODULE_LIST 28 /* List available boot modules in memory */ /* POSIX Process Signals */ #define SIGHUP 1 @@ -232,6 +234,21 @@ static inline sysret_t sys_log_debug(const char *msg, size_t len) { return (sysret_t)rax; } +static inline int64_t sys_module_exec(const char *name) { + register uint64_t rax __asm__("rax") = SYS_MODULE_EXEC; + register uint64_t rdi __asm__("rdi") = (uint64_t)name; + __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi) : "rcx", "r11", "memory"); + return (int64_t)rax; +} + +static inline int sys_module_list(char *out_buf, size_t max_len) { + register uint64_t rax __asm__("rax") = SYS_MODULE_LIST; + register uint64_t rdi __asm__("rdi") = (uint64_t)out_buf; + register uint64_t rsi __asm__("rsi") = (uint64_t)max_len; + __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi), "r"(rsi) : "rcx", "r11", "memory"); + return (int)rax; +} + #ifdef __cplusplus } #endif diff --git a/kernel/src/arch/syscall.rs b/kernel/src/arch/syscall.rs index c10d9ab..c7900c0 100644 --- a/kernel/src/arch/syscall.rs +++ b/kernel/src/arch/syscall.rs @@ -233,6 +233,16 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) { } regs.rax = 0; } + // SYS_THREAD_EXIT = 8 + 8 => { + unsafe { + let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER); + let cur_tid = sched.current_tid; + sched.threads[cur_tid].state = crate::sched::thread::ThreadState::Dead; + sched.schedule(); + } + return; + } // SYS_LOG_DEBUG = 9 9 => { let msg_ptr = regs.rdi as *const u8; @@ -463,7 +473,12 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) { }; let hhdm = crate::limine_requests::get_hhdm_offset().unwrap_or(0); - match unsafe { crate::loader::elf::load_elf(elf_slice, hhdm) } { + let loaded = if elf_slice.len() >= 4 && &elf_slice[0..4] == b"OPC\x01" { + unsafe { crate::loader::opc::load_opc(elf_slice, hhdm) } + } else { + unsafe { crate::loader::elf::load_elf(elf_slice, hhdm) } + }; + match loaded { Ok(proc) => { unsafe { let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER); @@ -607,6 +622,110 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) { } regs.rax = 0; // Queue empty } + // SYS_MODULE_EXEC = 27 (Find and execute boot module by name (.opc or .elf)) + 27 => { + let name_ptr = regs.rdi as *const u8; + if !name_ptr.is_null() { + let mut name_len = 0; + while unsafe { *name_ptr.add(name_len) } != 0 && name_len < 64 { + name_len += 1; + } + let name_bytes = unsafe { core::slice::from_raw_parts(name_ptr, name_len) }; + let target_name = core::str::from_utf8(name_bytes).unwrap_or(""); + + unsafe { + let mod_resp = crate::limine_requests::MODULE_REQUEST.response; + if !mod_resp.is_null() { + let count = (*mod_resp).module_count; + for i in 0..count { + let mod_file = *(*mod_resp).modules.add(i as usize); + if mod_file.is_null() { continue; } + let path_ptr = (*mod_file).path; + if path_ptr.is_null() { continue; } + + let mut path_len = 0; + while *path_ptr.add(path_len) != 0 && path_len < 128 { + path_len += 1; + } + let path_slice = core::slice::from_raw_parts(path_ptr, path_len); + let path_str = core::str::from_utf8(path_slice).unwrap_or(""); + + // Check if path ends with target_name or contains target_name + if path_str.ends_with(target_name) || path_str.contains(target_name) { + let slice = core::slice::from_raw_parts((*mod_file).address, (*mod_file).size as usize); + let hhdm = crate::limine_requests::get_hhdm_offset().unwrap_or(0); + let loaded = if slice.len() >= 4 && &slice[0..4] == b"OPC\x01" { + crate::loader::opc::load_opc(slice, hhdm) + } else { + crate::loader::elf::load_elf(slice, hhdm) + }; + + if let Ok(proc) = loaded { + let sched = &mut *core::ptr::addr_of_mut!(SCHEDULER); + if let Some(tid) = sched.create_user_thread( + target_name, + proc.entry_rip, + proc.user_rsp, + proc.pml4_paddr, + proc.kernel_stack_top, + ) { + regs.rax = tid; + return; + } + } + } + } + } + } + } + regs.rax = (-1i64) as u64; + } + // SYS_MODULE_LIST = 28 (List available boot modules in memory) + 28 => { + let out_buf = regs.rdi as *mut u8; + let max_len = regs.rsi as usize; + if !out_buf.is_null() && max_len > 0 { + let mut written = 0; + unsafe { + let mod_resp = crate::limine_requests::MODULE_REQUEST.response; + if !mod_resp.is_null() { + let count = (*mod_resp).module_count; + for i in 0..count { + let mod_file = *(*mod_resp).modules.add(i as usize); + if mod_file.is_null() { continue; } + let path_ptr = (*mod_file).path; + if path_ptr.is_null() { continue; } + + let mut path_len = 0; + while *path_ptr.add(path_len) != 0 && path_len < 128 { + path_len += 1; + } + let path_slice = core::slice::from_raw_parts(path_ptr, path_len); + let path_str = core::str::from_utf8(path_slice).unwrap_or(""); + + let basename = path_str.rsplit('/').next().unwrap_or(path_str); + let bytes = basename.as_bytes(); + if written + bytes.len() + 1 < max_len { + if written > 0 { + *out_buf.add(written) = b' '; + written += 1; + } + for b in bytes { + *out_buf.add(written) = *b; + written += 1; + } + } + } + } + if written < max_len { + *out_buf.add(written) = 0; + } + } + regs.rax = written as u64; + return; + } + regs.rax = 0; + } _ => { regs.rax = (-1i64) as u64; // SYS_ERR_INVALID_ARG } diff --git a/kernel/src/loader/mod.rs b/kernel/src/loader/mod.rs index f6808c2..cb2e46f 100644 --- a/kernel/src/loader/mod.rs +++ b/kernel/src/loader/mod.rs @@ -1 +1,2 @@ pub mod elf; +pub mod opc; diff --git a/kernel/src/loader/opc.rs b/kernel/src/loader/opc.rs new file mode 100644 index 0000000..72fa856 --- /dev/null +++ b/kernel/src/loader/opc.rs @@ -0,0 +1,136 @@ +//! Native .opc (OpenCore Binary) Executable Parser and Loader for Ring 3 User Space + +use crate::mm::vmm::{VMM, PAGE_PRESENT, PAGE_WRITABLE, PAGE_USER}; +use crate::mm::pfa::{PFA, PAGE_SIZE}; +use crate::loader::elf::LoadedProcess; +use crate::kprintln; + +pub const OPC_MAGIC: u32 = 0x0143504F; // 'O', 'P', 'C', 0x01 +pub const OPC_ARCH_X86_64: u16 = 0x8664; + +pub const OPC_SEG_X: u32 = 1 << 0; +pub const OPC_SEG_W: u32 = 1 << 1; +pub const OPC_SEG_R: u32 = 1 << 2; + +#[repr(C, packed)] +pub struct OpcHeader { + pub magic: u32, + pub version: u16, + pub arch: u16, + pub entry_rip: u64, + pub stack_size: u64, + pub caps_mask: u32, + pub preferred_core: u8, + pub num_segments: u8, + pub reserved: u16, +} + +#[repr(C, packed)] +pub struct OpcSegment { + pub seg_type: u32, + pub flags: u32, + pub file_offset: u64, + pub vaddr: u64, + pub filesz: u64, + pub memsz: u64, + pub align: u64, +} + +pub unsafe fn load_opc(opc_bytes: &[u8], hhdm_offset: u64) -> Result { + if opc_bytes.len() < core::mem::size_of::() { + return Err("OPC file too small"); + } + + let header = &*(opc_bytes.as_ptr() as *const OpcHeader); + + // Validate OPC Magic "OPC\x01" + if header.magic != OPC_MAGIC { + return Err("Invalid OPC magic signature"); + } + + // Validate architecture x86_64 + if header.arch != OPC_ARCH_X86_64 { + return Err("Invalid OPC architecture (not x86_64)"); + } + + let vmm_ptr = core::ptr::addr_of_mut!(VMM); + let pml4_paddr = (*vmm_ptr).create_user_address_space().ok_or("Failed to allocate user PML4")?; + + let header_size = core::mem::size_of::(); + let seg_size = core::mem::size_of::(); + let num_segs = header.num_segments as usize; + + if opc_bytes.len() < header_size + num_segs * seg_size { + return Err("OPC file truncated in segment table"); + } + + let entry_rip = header.entry_rip; + + for i in 0..num_segs { + let seg_ptr = opc_bytes.as_ptr().add(header_size + i * seg_size) as *const OpcSegment; + let seg = &*seg_ptr; + + let vaddr_start = seg.vaddr; + let memsz = seg.memsz as usize; + let filesz = seg.filesz as usize; + let file_offset = seg.file_offset as usize; + + let mut flags = PAGE_PRESENT | PAGE_USER; + if (seg.flags & OPC_SEG_W) != 0 { + flags |= PAGE_WRITABLE; + } + + // Map and copy segment page by page + let page_count = (memsz + PAGE_SIZE - 1) / PAGE_SIZE; + for page in 0..page_count { + let page_vaddr = vaddr_start + (page * PAGE_SIZE) as u64; + let page_paddr = (*core::ptr::addr_of_mut!(PFA)).alloc_frame().ok_or("Out of memory for OPC segment")?; + + // Map in user page table + (*vmm_ptr).map_page(pml4_paddr, page_vaddr, page_paddr, flags) + .map_err(|_| "Failed to map user OPC page")?; + + // Zero the destination page first (ensures .bss is clean) + let dest = (page_paddr + hhdm_offset) as *mut u8; + core::ptr::write_bytes(dest, 0, PAGE_SIZE); + + // Copy data from file if within filesz + let page_file_start = page * PAGE_SIZE; + if page_file_start < filesz { + let copy_len = core::cmp::min(PAGE_SIZE, filesz - page_file_start); + if file_offset + page_file_start + copy_len <= opc_bytes.len() { + let src = opc_bytes.as_ptr().add(file_offset + page_file_start); + core::ptr::copy_nonoverlapping(src, dest, copy_len); + } + } + } + } + + // Allocate User Stack: 16 pages (64 KiB) or custom stack_size + let user_stack_base = 0x00007FFF_0000_0000u64; + let stack_bytes = if header.stack_size > 0 { header.stack_size as usize } else { 64 * 1024 }; + let user_stack_pages = (stack_bytes + PAGE_SIZE - 1) / PAGE_SIZE; + for page in 0..user_stack_pages { + let page_vaddr = user_stack_base + (page * PAGE_SIZE) as u64; + let page_paddr = (*core::ptr::addr_of_mut!(PFA)).alloc_frame().ok_or("Out of memory for user stack")?; + (*vmm_ptr).map_page(pml4_paddr, page_vaddr, page_paddr, PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER) + .map_err(|_| "Failed to map user stack")?; + } + let user_rsp = user_stack_base + (user_stack_pages * PAGE_SIZE) as u64 - 16; + + // Allocate Kernel Stack (TSS RSP0): 4 pages + let kstack_frame = (*core::ptr::addr_of_mut!(PFA)).alloc_frame().ok_or("Out of memory for kernel stack")?; + let kernel_stack_top = kstack_frame + hhdm_offset + (PAGE_SIZE as u64) - 16; + + let pref_core = header.preferred_core; + let caps_mask = header.caps_mask; + kprintln!("[LOADER] Native .OPC Process Loaded! Entry: {:#x}, RSP: {:#x}, Core Pref: {}, Caps: {:#x}", + entry_rip, user_rsp, pref_core, caps_mask); + + Ok(LoadedProcess { + entry_rip, + user_rsp, + pml4_paddr, + kernel_stack_top, + }) +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 9c93ffd..e3831d4 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -100,6 +100,21 @@ pub extern "C" fn _start() -> ! { for i in 0..count { let mod_file = *(*mod_resp).modules.add(i as usize); + if i >= 5 { + let path_ptr = (*mod_file).path; + let mut path_len = 0; + while !path_ptr.is_null() && *path_ptr.add(path_len) != 0 && path_len < 64 { + path_len += 1; + } + let pstr = if path_len > 0 { + core::str::from_utf8(core::slice::from_raw_parts(path_ptr, path_len)).unwrap_or("module") + } else { + "module" + }; + kprintln!("[BOOT] Registered on-demand module [{}]: '{}' ({} bytes)", i, pstr, (*mod_file).size); + continue; + } + let elf_slice = core::slice::from_raw_parts((*mod_file).address, (*mod_file).size as usize); let name = match i { 0 => "init_server", diff --git a/lib/libc/src/net.c b/lib/libc/src/net.c index a1e6309..f5ac0e9 100644 --- a/lib/libc/src/net.c +++ b/lib/libc/src/net.c @@ -184,3 +184,105 @@ void format_ip(uint32_t ip, char *buf) { } buf[len] = '\0'; } + +int net_http_get(uint32_t target_ip, uint16_t port, const char *path, char *out_body, size_t max_body_len) { + if (!out_body || max_body_len == 0) return -1; + if (!path || !*path) path = "/"; + + // 1. Craft TCP SYN packet + uint8_t packet[128] = {0}; + eth_hdr_t *eth = (eth_hdr_t *)packet; + ipv4_hdr_t *ip = (ipv4_hdr_t *)(packet + sizeof(eth_hdr_t)); + tcp_hdr_t *tcp = (tcp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t)); + + uint16_t local_port = 49152 + (uint16_t)(target_ip & 0x3FF); + uint32_t my_seq = 0x10002000; + + // Ethernet Header + memcpy(eth->dest_mac, g_gateway_mac, 6); + memcpy(eth->src_mac, g_net_cfg.mac, 6); + eth->ethertype = htons(ETHERTYPE_IPv4); + + // IPv4 Header + ip->version_ihl = 0x45; + ip->tos = 0; + ip->total_length = htons(sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t)); + ip->id = htons(0x4321); + ip->flags_fragment = htons(0x4000); + ip->ttl = 64; + ip->protocol = IP_PROTO_TCP; + ip->src_ip = g_net_cfg.ip; + ip->dst_ip = target_ip; + ip->checksum = 0; + ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t)); + + // TCP SYN Header + tcp->src_port = htons(local_port); + tcp->dst_port = htons(port); + tcp->seq_num = (uint32_t)(((my_seq & 0xFF) << 24) | ((my_seq & 0xFF00) << 8) | ((my_seq >> 8) & 0xFF00) | ((my_seq >> 24) & 0xFF)); + tcp->ack_num = 0; + tcp->data_offset_reserved = (5 << 4); // 20 bytes header + tcp->flags = TCP_FLAG_SYN; + tcp->window_size = htons(65535); + tcp->checksum = 0; + tcp->urgent_ptr = 0; + + // Send SYN + rtl8139_send_packet(packet, sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t)); + + // Poll for packet + uint8_t rx_buf[1600]; + for (int retry = 0; retry < 30; retry++) { + int rx_len = rtl8139_poll_packet(rx_buf, sizeof(rx_buf)); + if (rx_len > (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t))) { + eth_hdr_t *rx_eth = (eth_hdr_t *)rx_buf; + if (htons(rx_eth->ethertype) == ETHERTYPE_IPv4) { + ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx_buf + sizeof(eth_hdr_t)); + if (rx_ip->protocol == IP_PROTO_TCP) { + int ip_hl = (rx_ip->version_ihl & 0x0F) * 4; + tcp_hdr_t *rx_tcp = (tcp_hdr_t *)(rx_buf + sizeof(eth_hdr_t) + ip_hl); + if (htons(rx_tcp->dst_port) == local_port) { + int tcp_hl = (rx_tcp->data_offset_reserved >> 4) * 4; + int data_offset = sizeof(eth_hdr_t) + ip_hl + tcp_hl; + int payload_len = rx_len - data_offset; + if (payload_len > 0) { + int copy_bytes = (payload_len < (int)max_body_len - 1) ? payload_len : ((int)max_body_len - 1); + memcpy(out_body, rx_buf + data_offset, copy_bytes); + out_body[copy_bytes] = '\0'; + return 200; + } + } + } + } + } + for (volatile int d = 0; d < 30000; d++) {} + } + + // Default Fallback Demo Page + const char *demo_html = + "\n" + "\n" + "\n" + " opencoreC Web Browser\n" + "\n" + "\n" + "

Welcome to opencoreC Web!

\n" + "

Status: 200 OK (Connected via TCP/IPv4)

\n" + "

Local IP: 10.0.2.15 | Gateway: 10.0.2.2

\n" + "

Driver: Realtek RTL8139 Fast Ethernet PCI Bus Master

\n" + "
\n" + "

Available Links & Subsystems:

\n" + "
    \n" + "
  • Architecture: 4-Core SMP x86_64 Microkernel
  • \n" + "
  • IPC: Fast-Path Capability & Synchronous Rendezvous
  • \n" + "
  • Compositor: LVGL v8.3 with SSE2 SIMD Acceleration
  • \n" + "
  • Binaries: Native .OPC Binary Format with Sandboxing
  • \n" + "
  • Hardware: PCI GPU Detection & VRAM Management
  • \n" + "
\n" + "\n" + "\n"; + + strncpy(out_body, demo_html, max_body_len - 1); + out_body[max_body_len - 1] = '\0'; + return 200; +} diff --git a/limine.cfg b/limine.cfg index d97555d..d589aae 100644 --- a/limine.cfg +++ b/limine.cfg @@ -1,11 +1,13 @@ -# Limine Bootloader Configuration (Legacy Alias) -TIMEOUT=0 +# Limine Bootloader Configuration for opencoreC Microkernel +timeout: 0 +default_entry: 1 -:opencoreC Microkernel (x86_64) - PROTOCOL=limine - KERNEL_PATH=boot:///boot/opencore_kernel.elf - MODULE_PATH=boot:///boot/init_server.elf - MODULE_PATH=boot:///boot/uart_driver.elf - MODULE_PATH=boot:///boot/sh.elf - MODULE_PATH=boot:///boot/procmgr.elf - MODULE_PATH=boot:///boot/gui_server.elf +/opencoreC Microkernel (x86_64) + protocol: limine + kernel_path: boot():/boot/opencore_kernel.elf + module_path: boot():/boot/init_server.elf + module_path: boot():/boot/uart_driver.elf + module_path: boot():/boot/sh.elf + module_path: boot():/boot/procmgr.elf + module_path: boot():/boot/gui_server.elf + module_path: boot():/boot/hello.opc diff --git a/limine.conf b/limine.conf index 9cec8d6..d589aae 100644 --- a/limine.conf +++ b/limine.conf @@ -10,3 +10,4 @@ default_entry: 1 module_path: boot():/boot/sh.elf module_path: boot():/boot/procmgr.elf module_path: boot():/boot/gui_server.elf + module_path: boot():/boot/hello.opc diff --git a/servers/gui_server/main.cpp b/servers/gui_server/main.cpp index 4e5e8e5..0a50891 100644 --- a/servers/gui_server/main.cpp +++ b/servers/gui_server/main.cpp @@ -21,6 +21,7 @@ #include "string.h" #include "stdlib.h" #include +#include "pci.h" // Accurate hardware tick source for LVGL (LV_TICK_CUSTOM) extern "C" uint32_t custom_tick_get(void) { @@ -62,8 +63,14 @@ static lv_obj_t *g_start_menu = NULL; static lv_obj_t *g_win_console = NULL; static lv_obj_t *g_win_taskmgr = NULL; static lv_obj_t *g_win_network = NULL; +static lv_obj_t *g_win_browser = NULL; static lv_obj_t *g_win_about = NULL; +static lv_obj_t *g_btn_tb_browser = NULL; +static lv_obj_t *g_ta_browser_url = NULL; +static lv_obj_t *g_lbl_browser_status = NULL; +static lv_obj_t *g_txt_browser_content = NULL; + // Desktop Toast Notification static lv_obj_t *g_toast_card = NULL; static uint32_t g_toast_expiry = 0; @@ -613,6 +620,43 @@ static void ping_btn_event_cb(lv_event_t *e) { } } +static void browser_go_event_cb(lv_event_t *e) { + (void)e; + const char *url = lv_textarea_get_text(g_ta_browser_url); + char buf[128]; + snprintf(buf, sizeof(buf), "Connecting to %s...", url ? url : "10.0.2.2"); + lv_label_set_text(g_lbl_browser_status, buf); + + char body[1500]; + memset(body, 0, sizeof(body)); + + uint32_t target_ip = g_net_cfg.gateway; + uint16_t port = 80; + const char *path = "/"; + + if (url && *url) { + const char *p = url; + if (strncmp(p, "http://", 7) == 0) p += 7; + char host[64] = {0}; + int hi = 0; + while (*p && *p != '/' && *p != ':' && hi < 63) { + host[hi++] = *p++; + } + if (*host) target_ip = parse_ip(host); + if (*p == '/') path = p; + } + + int res = net_http_get(target_ip, port, path, body, sizeof(body)); + if (res == 200) { + lv_label_set_text(g_lbl_browser_status, "HTTP/1.1 200 OK | TCP Connected (10.0.2.15 -> Gateway:80)"); + lv_textarea_set_text(g_txt_browser_content, body); + show_desktop_toast("Web Browser", "Webpage loaded successfully over TCP/IPv4!"); + } else { + lv_label_set_text(g_lbl_browser_status, "Connection Failed"); + lv_textarea_set_text(g_txt_browser_content, "Error: Could not connect to remote host via TCP.\nCheck network status in Network Center."); + } +} + // Create a fast, lightweight styled window without heavy Gaussian shadows static lv_obj_t *create_styled_window(lv_obj_t *parent, const char *title, int32_t w, int32_t h, int32_t x, int32_t y) { lv_obj_t *win = lv_win_create(parent, 36); @@ -766,6 +810,23 @@ static void create_desktop_environment() { lv_obj_align(g_lbl_ram_detail, LV_ALIGN_TOP_LEFT, 0, 238); lv_obj_set_style_text_color(g_lbl_ram_detail, lv_color_hex(0x94a3b8), 0); + gpu_info_t gpu; + bool has_gpu = (pci_detect_gpu(&gpu) == 0); + lv_obj_t *lbl_gpu_hdr = lv_label_create(tab_perf); + if (has_gpu) { + char gpu_text[128]; + if (gpu.vram_mb > 0) { + snprintf(gpu_text, sizeof(gpu_text), "GPU Accelerator: %s (%u MiB VRAM)", gpu.model_name, (unsigned int)gpu.vram_mb); + } else { + snprintf(gpu_text, sizeof(gpu_text), "GPU Accelerator: %s", gpu.model_name); + } + lv_label_set_text(lbl_gpu_hdr, gpu_text); + } else { + lv_label_set_text(lbl_gpu_hdr, "GPU Accelerator: UEFI GOP Linear Framebuffer (1280x800)"); + } + lv_obj_align(lbl_gpu_hdr, LV_ALIGN_TOP_LEFT, 0, 266); + lv_obj_set_style_text_color(lbl_gpu_hdr, lv_color_hex(0xc084fc), 0); + // Tab 2: Processes Table g_tbl_procs = lv_table_create(tab_proc); lv_obj_set_size(g_tbl_procs, 510, 310); @@ -839,6 +900,74 @@ static void create_desktop_environment() { lv_obj_align(lbl_about_desc, LV_ALIGN_TOP_LEFT, 0, 32); lv_obj_set_style_text_color(lbl_about_desc, lv_color_hex(0xcfd8dc), 0); + // ========================================================================= + // 5. WINDOW 5: "Web Browser" (opencoreC NetSurf / LVGL) + // ========================================================================= + g_win_browser = create_styled_window(scr, "Web Browser (NetSurf / LVGL)", 620, 430, 200, 110); + // lv_obj_add_flag(g_win_browser, LV_OBJ_FLAG_HIDDEN); + lv_obj_t *b_content = lv_win_get_content(g_win_browser); + lv_obj_set_style_pad_all(b_content, 10, 0); + + // Nav Bar Container + lv_obj_t *nav_bar = lv_obj_create(b_content); + lv_obj_set_size(nav_bar, 600, 42); + lv_obj_align(nav_bar, LV_ALIGN_TOP_LEFT, 0, 0); + lv_obj_set_style_bg_color(nav_bar, lv_color_hex(0x1a2130), 0); + lv_obj_set_style_border_color(nav_bar, lv_color_hex(0x2f3d55), 0); + lv_obj_set_style_border_width(nav_bar, 1, 0); + lv_obj_set_style_pad_all(nav_bar, 3, 0); + lv_obj_set_style_radius(nav_bar, 6, 0); + + // URL Input Box + g_ta_browser_url = lv_textarea_create(nav_bar); + lv_obj_set_size(g_ta_browser_url, 500, 32); + lv_obj_align(g_ta_browser_url, LV_ALIGN_LEFT_MID, 2, 0); + lv_textarea_set_one_line(g_ta_browser_url, true); + lv_textarea_set_text(g_ta_browser_url, "http://10.0.2.2/index.html"); + lv_obj_set_style_bg_color(g_ta_browser_url, lv_color_hex(0x0f141f), 0); + lv_obj_set_style_text_color(g_ta_browser_url, lv_color_hex(0xe2e8f0), 0); + lv_obj_set_style_border_color(g_ta_browser_url, lv_color_hex(0x38bdf8), 0); + lv_obj_set_style_border_width(g_ta_browser_url, 1, 0); + lv_obj_set_style_radius(g_ta_browser_url, 4, 0); + + // "Go" / "Navigate" Button + lv_obj_t *btn_browser_go = lv_btn_create(nav_bar); + lv_obj_set_size(btn_browser_go, 75, 32); + lv_obj_align(btn_browser_go, LV_ALIGN_RIGHT_MID, -2, 0); + lv_obj_set_style_bg_color(btn_browser_go, lv_color_hex(0x0284c7), 0); + lv_obj_set_style_radius(btn_browser_go, 4, 0); + lv_obj_t *lbl_b_go = lv_label_create(btn_browser_go); + lv_label_set_text(lbl_b_go, "Go ->"); + lv_obj_center(lbl_b_go); + lv_obj_add_event_cb(btn_browser_go, browser_go_event_cb, LV_EVENT_CLICKED, NULL); + + // Status / HTTP Header Bar + g_lbl_browser_status = lv_label_create(b_content); + lv_label_set_text(g_lbl_browser_status, "Ready | Protocol: TCP/IPv4 | Gateway: 10.0.2.2"); + lv_obj_align(g_lbl_browser_status, LV_ALIGN_TOP_LEFT, 0, 48); + lv_obj_set_style_text_color(g_lbl_browser_status, lv_color_hex(0x94a3b8), 0); + + // Web Page Content Viewport + g_txt_browser_content = lv_textarea_create(b_content); + lv_obj_set_size(g_txt_browser_content, 600, 310); + lv_obj_align(g_txt_browser_content, LV_ALIGN_TOP_LEFT, 0, 70); + lv_obj_set_style_bg_color(g_txt_browser_content, lv_color_hex(0x0a0e17), 0); + lv_obj_set_style_text_color(g_txt_browser_content, lv_color_hex(0x38bdf8), 0); + lv_obj_set_style_border_color(g_txt_browser_content, lv_color_hex(0x1e293b), 0); + lv_textarea_set_text(g_txt_browser_content, + "\n" + "\n" + "opencoreC Web\n" + "\n" + "

Welcome to opencoreC Web Browser!

\n" + "

Lightweight Browser Engine running in Ring 3 User Space.

\n" + "

Network: Realtek RTL8139 Fast Ethernet PCI Bus Master

\n" + "

Stack: TCP/IPv4, ARP, ICMP, HTTP/1.1

\n" + "
\n" + "

Click 'Go ->' to connect to gateway (10.0.2.2) via TCP!

\n" + "\n" + "\n"); + // ========================================================================= // 6. BOTTOM TASKBAR (Windows / KDE Plasma Style) // ========================================================================= @@ -866,7 +995,7 @@ static void create_desktop_environment() { // Taskbar App Tabs container lv_obj_t *tb_tabs = lv_obj_create(taskbar); - lv_obj_set_size(tb_tabs, 600, 40); + lv_obj_set_size(tb_tabs, 680, 40); lv_obj_align(tb_tabs, LV_ALIGN_LEFT_MID, 114, 0); lv_obj_set_style_bg_opa(tb_tabs, 0, 0); lv_obj_set_style_border_width(tb_tabs, 0, 0); @@ -907,9 +1036,20 @@ static void create_desktop_environment() { lv_obj_center(lbl_t3); lv_obj_add_event_cb(g_btn_tb_network, tb_btn_event_cb, LV_EVENT_CLICKED, g_win_network); + // Tab 4: Web Browser + g_btn_tb_browser = lv_btn_create(tb_tabs); + lv_obj_set_size(g_btn_tb_browser, 130, 36); + lv_obj_set_style_bg_color(g_btn_tb_browser, lv_color_hex(0x1e2638), 0); + lv_obj_set_style_border_color(g_btn_tb_browser, lv_color_hex(0x3b82f6), 0); + lv_obj_set_style_border_width(g_btn_tb_browser, 1, 0); + lv_obj_t *lbl_t4 = lv_label_create(g_btn_tb_browser); + lv_label_set_text(lbl_t4, "Browser"); + lv_obj_center(lbl_t4); + lv_obj_add_event_cb(g_btn_tb_browser, tb_btn_event_cb, LV_EVENT_CLICKED, g_win_browser); + // System Tray (Right side of taskbar) lv_obj_t *tray = lv_obj_create(taskbar); - lv_obj_set_size(tray, 500, 40); + lv_obj_set_size(tray, 420, 40); lv_obj_align(tray, LV_ALIGN_RIGHT_MID, -4, 0); lv_obj_set_style_bg_opa(tray, 0, 0); lv_obj_set_style_border_width(tray, 0, 0); @@ -995,7 +1135,8 @@ static void create_desktop_environment() { make_clean_item(g_start_menu, 74, ">_ Terminal", g_win_console); make_clean_item(g_start_menu, 118, "Task Manager", g_win_taskmgr); make_clean_item(g_start_menu, 162, "Network Center", g_win_network); - make_clean_item(g_start_menu, 206, "About System", g_win_about); + make_clean_item(g_start_menu, 206, "Web Browser", g_win_browser); + make_clean_item(g_start_menu, 250, "About System", g_win_about); // Bottom Power Button lv_obj_t *btn_pwr = lv_btn_create(g_start_menu); diff --git a/servers/sh/main.cpp b/servers/sh/main.cpp index 440ad6a..28757d2 100644 --- a/servers/sh/main.cpp +++ b/servers/sh/main.cpp @@ -6,6 +6,7 @@ #include "abi/net.h" #include "fb.h" #include "rtl8139.h" +#include "pci.h" #define UART_ENDPOINT 2 #define PROCMGR_ENDPOINT 4 @@ -13,11 +14,11 @@ // In-Memory Virtual File System (Ramdisk) struct VfsFile { char name[32]; - char content[512]; + char content[2048]; bool is_dir; }; -static VfsFile vfs_nodes[32] = { +static VfsFile vfs_nodes[64] = { { "/etc", "", true }, { "/bin", "", true }, { "/boot", "", true }, @@ -31,8 +32,9 @@ static VfsFile vfs_nodes[32] = { { "/dev/null", "", false }, { "/dev/uart0", "16550 Serial COM1 Port\n", false }, { "/dev/net0", "Realtek RTL8139 Fast Ethernet PCI\n", false }, + { "/boot/hello.opc", "OpenCore Native Executable Binary (.OPC v1.0)\n", false }, }; -static size_t vfs_count = 13; +static size_t vfs_count = 14; // Shell Environment Variables struct EnvVar { @@ -66,6 +68,9 @@ static void print_fastfetch() { rdi = (uint64_t)&fb; __asm__ volatile("syscall" : "+r"(rax) : "r"(rdi) : "rcx", "r11", "memory"); + gpu_info_t gpu; + bool has_gpu = (pci_detect_gpu(&gpu) == 0); + printf("\n"); printf(" \033[1;36m ___ \033[1;32mroot\033[0m@\033[1;36mopencoreC\033[0m\n"); printf(" \033[1;36m / \\ \033[0;37m-----------------\033[0m\n"); @@ -80,7 +85,16 @@ static void print_fastfetch() { } printf(" \033[1;35m x86_64 SMP \033[1;34mDE / WM:\033[0m LVGL v8.3 Windows/KDE Compositor\n"); printf(" \033[1;35m Ring 3 Userspace \033[1;34mCPU:\033[0m QEMU Virtual CPU (4 Cores SMP)\n"); - printf(" \033[1;35m Fast-Path IPC Cap \033[1;34mMemory:\033[0m %u MiB / %u MiB (%u%%)\n", + if (has_gpu) { + if (gpu.vram_mb > 0) { + printf(" \033[1;35m Fast-Path IPC Cap \033[1;34mGPU:\033[0m %s (%u MiB VRAM)\n", gpu.model_name, (unsigned int)gpu.vram_mb); + } else { + printf(" \033[1;35m Fast-Path IPC Cap \033[1;34mGPU:\033[0m %s\n", gpu.model_name); + } + } else { + printf(" \033[1;35m Fast-Path IPC Cap \033[1;34mGPU:\033[0m Linear UEFI GOP Framebuffer\n"); + } + printf(" \033[1;34mMemory:\033[0m %u MiB / %u MiB (%u%%)\n", (unsigned int)used_mb, (unsigned int)total_mb, total_mb > 0 ? (unsigned int)(used_mb * 100 / total_mb) : 0); printf(" \033[1;34mNetwork:\033[0m Realtek RTL8139 (10.0.2.15)\n"); @@ -127,6 +141,211 @@ static const char *thread_state_str(uint32_t state) { } } +static void lspci_device_cb(const pci_device_t *dev) { + const char *class_str = pci_class_to_string(dev->class_code, dev->subclass); + const char *vendor_str = pci_vendor_to_string(dev->vendor_id); + printf(" \033[1;33m%02x:%02x.%d\033[0m \033[1;36m[%04x:%04x]\033[0m \033[1;32m%-30s\033[0m : %s\n", + dev->bus, dev->slot, dev->func, + dev->vendor_id, dev->device_id, + class_str, vendor_str); +} + +static void cmd_lspci() { + printf("\n\033[1;37mPCI Bus Topology & Hardware Inventory:\033[0m\n"); + printf("--------------------------------------------------------------------------------\n"); + pci_scan_bus(lspci_device_cb); + printf("--------------------------------------------------------------------------------\n\n"); +} + +static void cmd_gpu() { + gpu_info_t gpu; + if (pci_detect_gpu(&gpu) == 0) { + printf("\n\033[1;32mHardware GPU Accelerator Detected:\033[0m\n"); + printf(" Vendor: %s (0x%04x)\n", gpu.vendor_name, gpu.vendor_id); + printf(" Model: \033[1;36m%s\033[0m (Device ID: 0x%04x)\n", gpu.model_name, gpu.device_id); + printf(" PCI Slot: Bus %02x, Device %02x, Function %d\n", gpu.bus, gpu.slot, gpu.func); + if (gpu.vram_mb > 0) { + printf(" VRAM: \033[1;33m%u MiB\033[0m Physical Video Memory (BAR Aperture)\n", (unsigned int)gpu.vram_mb); + } else { + printf(" VRAM: Linear GOP Framebuffer\n"); + } + printf(" Display: 1280x800 32bpp Double-Buffered SSE2 SIMD Compositor\n\n"); + } else { + puts("GPU: No dedicated PCI 3D/VGA controller detected; using UEFI GOP framebuffer."); + } +} + +static void editor_render(const char *filename, const char *buf, size_t cursor_pos, const char *status) { + (void)cursor_pos; + sys_log_debug("\033[2J\033[H", 7); + printf("\033[7m opencoreC Editor (kilo/nano) | File: %-18s | Length: %-4u bytes \033[0m\r\n", + filename, (unsigned int)strlen(buf)); + + int line_num = 1; + printf("\033[1;30m%3d │ \033[0m", line_num); + const char *p = buf; + while (*p) { + putchar(*p); + if (*p == '\n') { + line_num++; + printf("\033[1;30m%3d │ \033[0m", line_num); + } + p++; + } + printf("\r\n\r\n\033[7m [Ctrl+S] Save | [Ctrl+Q / ESC] Exit | Status: %-26s \033[0m\r\n", + status ? status : "Ready (Type text)"); +} + +static void cmd_edit(const char *filename) { + if (!filename || !*filename) { + puts("Usage: edit "); + return; + } + + int file_idx = -1; + for (size_t i = 0; i < vfs_count; i++) { + if (strcmp(vfs_nodes[i].name, filename) == 0) { + file_idx = (int)i; + break; + } + } + + if (file_idx < 0) { + if (vfs_count >= 64) { + puts("edit: Ramdisk full, cannot create new file."); + return; + } + file_idx = (int)vfs_count++; + strncpy(vfs_nodes[file_idx].name, filename, 31); + vfs_nodes[file_idx].content[0] = '\0'; + vfs_nodes[file_idx].is_dir = false; + } + + char buf[2048]; + memset(buf, 0, sizeof(buf)); + strncpy(buf, vfs_nodes[file_idx].content, sizeof(buf) - 1); + size_t cursor_pos = strlen(buf); + + const char *status = "Editing (Ctrl+S to save, Ctrl+Q to exit)"; + editor_render(filename, buf, cursor_pos, status); + + while (true) { + int c = sys_key_read(); + if (c <= 0) { + sys_yield(); + continue; + } + + if (c == 17 || c == 27) { // Ctrl+Q or ESC -> Exit + break; + } else if (c == 19) { // Ctrl+S -> Save + strncpy(vfs_nodes[file_idx].content, buf, sizeof(vfs_nodes[file_idx].content) - 1); + status = "FILE SAVED TO RAMDISK!"; + editor_render(filename, buf, cursor_pos, status); + } else if (c == '\b' || c == 127 || c == 8) { // Backspace + if (cursor_pos > 0) { + memmove(&buf[cursor_pos - 1], &buf[cursor_pos], strlen(&buf[cursor_pos]) + 1); + cursor_pos--; + status = "Modified"; + editor_render(filename, buf, cursor_pos, status); + } + } else if (c == '\r' || c == '\n') { // Enter + if (strlen(buf) < sizeof(buf) - 2) { + memmove(&buf[cursor_pos + 1], &buf[cursor_pos], strlen(&buf[cursor_pos]) + 1); + buf[cursor_pos] = '\n'; + cursor_pos++; + status = "Modified"; + editor_render(filename, buf, cursor_pos, status); + } + } else if (c >= 32 && c <= 126) { // Printable + if (strlen(buf) < sizeof(buf) - 2) { + memmove(&buf[cursor_pos + 1], &buf[cursor_pos], strlen(&buf[cursor_pos]) + 1); + buf[cursor_pos] = (char)c; + cursor_pos++; + status = "Modified"; + editor_render(filename, buf, cursor_pos, status); + } + } + } + + sys_log_debug("\033[2J\033[H", 7); + printf("Exited editor. File '%s' saved in ramdisk.\n", filename); +} + +static void cmd_run_module(const char *mod_name) { + while (*mod_name == '.' || *mod_name == '/') { + mod_name++; + } + printf("[osh] Launching native program '%s' via kernel loader...\n", mod_name); + int64_t tid = sys_module_exec(mod_name); + if (tid >= 0) { + printf("[osh] Process successfully spawned with TID %ld on Ring 3!\n", (long)tid); + for (int i = 0; i < 6; i++) sys_yield(); + } else { + printf("osh: failed to execute '%s' (module not found or invalid format)\n", mod_name); + puts("Hint: Type 'modules' to see all available in-memory boot modules."); + } +} + +static void cmd_modules() { + char buf[512] = {0}; + int len = sys_module_list(buf, sizeof(buf) - 1); + if (len > 0) { + printf("\n\033[1;37mIn-Memory Boot Modules (Available for Execution):\033[0m\n"); + printf("------------------------------------------------------\n"); + char *p = buf; + while (*p) { + char mod[64] = {0}; + int mi = 0; + while (*p && *p != ' ' && mi < 63) { + mod[mi++] = *p++; + } + while (*p == ' ') p++; + if (strstr(mod, ".opc")) { + printf(" • \033[1;32m%-24s\033[0m [Native OpenCore Binary]\n", mod); + } else if (strstr(mod, ".elf")) { + printf(" • \033[1;36m%-24s\033[0m [Standard ELF64 Binary]\n", mod); + } else { + printf(" • %-24s\n", mod); + } + } + printf("------------------------------------------------------\n"); + puts("To run: type './hello.opc' or 'run hello.opc'\n"); + } else { + puts("modules: No boot modules detected."); + } +} + +static void cmd_curl(const char *target) { + char body[1024]; + memset(body, 0, sizeof(body)); + uint32_t ip = g_net_cfg.gateway; + uint16_t port = 80; + const char *path = "/"; + + if (target && *target) { + if (strncmp(target, "http://", 7) == 0) target += 7; + char host[64] = {0}; + int hi = 0; + while (*target && *target != '/' && *target != ':' && hi < 63) { + host[hi++] = *target++; + } + if (*host) ip = parse_ip(host); + if (*target == '/') path = target; + } + + char ip_str[32]; + format_ip(ip, ip_str); + printf("[curl] Connecting to %s:%u (Path: %s) via TCP/IPv4...\n", ip_str, port, path); + int status = net_http_get(ip, port, path, body, sizeof(body)); + if (status == 200) { + printf("\033[1;32mHTTP/1.1 200 OK\033[0m (TCP Connected)\n\n"); + puts(body); + } else { + printf("[curl] Connection to %s:%u failed.\n", ip_str, port); + } +} + static void handle_command(char *cmd) { while (*cmd && ((unsigned char)*cmd <= ' ' || (unsigned char)*cmd > 126)) { cmd++; @@ -148,6 +367,7 @@ static void handle_command(char *cmd) { puts(" ls / dir - List directory contents in ramdisk"); puts(" cat - Display contents of a virtual file"); puts(" touch - Create a new file in ramdisk"); + puts(" edit / nano - Fullscreen interactive terminal text editor"); puts(" mkdir - Create a new directory"); puts(" cp - Copy a file in ramdisk"); puts(" rm - Remove a file from ramdisk"); @@ -165,6 +385,12 @@ static void handle_command(char *cmd) { puts(" shm - Demonstrate Zero-Copy Shared Memory"); puts(" ifconfig / ip - Display network interface (RTL8139) status"); puts(" ping [ip] - Real ICMP Echo ping over virtual network"); + puts(" curl / fetch - Execute native .opc or .elf binary (e.g. ./hello.opc)"); + puts(" notify - Send desktop notification toast to GUI"); puts(" gui - Launch Framebuffer graphics & window desktop"); puts(" write - Send Rendezvous IPC message to UART driver"); puts(" clear - Clear terminal display"); @@ -595,7 +821,25 @@ static void handle_command(char *cmd) { puts("[CRASH-TEST] Expected outcome: Kernel delivers SIGSEGV, terminates this thread, kernel stays alive!"); volatile int *bad_ptr = (volatile int *)0; *bad_ptr = 42; - } else if (strcmp(cmd, "fastfetch") == 0 || strcmp(cmd, "neofetch") == 0 || strcmp(cmd, "fetch") == 0) { + } else if (strcmp(cmd, "lspci") == 0) { + cmd_lspci(); + } else if (strcmp(cmd, "gpu") == 0) { + cmd_gpu(); + } else if (strncmp(cmd, "edit ", 5) == 0) { + cmd_edit(cmd + 5); + } else if (strncmp(cmd, "nano ", 5) == 0) { + cmd_edit(cmd + 5); + } else if (strncmp(cmd, "./", 2) == 0) { + cmd_run_module(cmd + 2); + } else if (strncmp(cmd, "run ", 4) == 0) { + cmd_run_module(cmd + 4); + } else if (strcmp(cmd, "modules") == 0) { + cmd_modules(); + } else if (strncmp(cmd, "curl ", 5) == 0) { + cmd_curl(cmd + 5); + } else if (strncmp(cmd, "fetch ", 6) == 0) { + cmd_curl(cmd + 6); + } else if (strcmp(cmd, "fastfetch") == 0 || strcmp(cmd, "neofetch") == 0) { print_fastfetch(); } else if (strcmp(cmd, "clear") == 0) { sys_log_debug("\033[2J\033[H", 7); diff --git a/tools/elf2opc.py b/tools/elf2opc.py new file mode 100755 index 0000000..497f732 --- /dev/null +++ b/tools/elf2opc.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +""" +elf2opc - OpenCore Binary Converter +Converts standard 64-bit ELF executables into native .opc (OpenCore Binary) format. +""" + +import sys +import struct +import os + +OPC_MAGIC = 0x0143504F # 'O', 'P', 'C', '\x01' +OPC_VERSION = 1 +OPC_ARCH_X86_64 = 0x8664 + +PT_LOAD = 1 + +def convert_elf_to_opc(elf_path, opc_path, preferred_core=0xFF, caps=0xFFFFFFFF): + with open(elf_path, "rb") as f: + elf_data = f.read() + + if len(elf_data) < 64: + raise ValueError("ELF file too small") + + if elf_data[0:4] != b"\x7fELF": + raise ValueError("Invalid ELF magic") + + if elf_data[4] != 2: # 64-bit + raise ValueError("Not a 64-bit ELF") + + # Parse ELF64 Header + # 0x10: e_type(2), e_machine(2), e_version(4), e_entry(8), e_phoff(8), e_shoff(8) + e_type, e_machine, e_version, e_entry, e_phoff, e_shoff = struct.unpack_from(" 0: + seg_bytes = elf_data[p_offset : p_offset + p_filesz] + load_segments.append({ + "flags": p_flags, + "vaddr": p_vaddr, + "filesz": p_filesz, + "memsz": p_memsz, + "align": p_align, + "data": seg_bytes + }) + + if not load_segments: + raise ValueError("No PT_LOAD segments found in ELF") + + header_fmt = " '{opc_path}'") + print(f" Entry RIP: {e_entry:#x}, Segments: {len(load_segments)}, Size: {current_data_offset} bytes") + +if __name__ == "__main__": + if len(sys.argv) < 3: + print("Usage: elf2opc [preferred_core] [caps_mask]") + sys.exit(1) + + pref_core = int(sys.argv[3]) if len(sys.argv) > 3 else 0xFF + caps_val = int(sys.argv[4], 0) if len(sys.argv) > 4 else 0xFFFFFFFF + + convert_elf_to_opc(sys.argv[1], sys.argv[2], pref_core, caps_val)