diff --git a/include/abi/net.h b/include/abi/net.h index e20cf36..03d91f5 100644 --- a/include/abi/net.h +++ b/include/abi/net.h @@ -69,6 +69,22 @@ typedef struct { uint16_t urgent_ptr; } tcp_hdr_t; +typedef struct { + uint16_t src_port; + uint16_t dst_port; + uint16_t length; + uint16_t checksum; +} udp_hdr_t; + +typedef struct { + uint16_t id; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} dns_hdr_t; + #define TCP_FLAG_FIN 0x01 #define TCP_FLAG_SYN 0x02 #define TCP_FLAG_RST 0x04 @@ -82,6 +98,7 @@ typedef struct { uint32_t ip; uint32_t netmask; uint32_t gateway; + uint32_t dns_server; uint8_t mac[6]; } net_config_t; @@ -90,10 +107,14 @@ extern net_config_t g_net_cfg; // Net API void net_init(void); uint16_t net_checksum(const void *buf, size_t len); +uint16_t net_tcp_checksum(uint32_t src_ip, uint32_t dst_ip, const tcp_hdr_t *tcp, size_t tcp_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); +int net_dns_resolve(const char *domain, uint32_t *out_ip); +int net_dhcp_discover(void); +int net_http_get(uint32_t target_ip, uint16_t port, const char *path, const char *host_header, char *out_body, size_t max_body_len); +int net_http_get_url(const char *url, 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/kernel/src/arch/smp.rs b/kernel/src/arch/smp.rs index baeeb99..d369579 100644 --- a/kernel/src/arch/smp.rs +++ b/kernel/src/arch/smp.rs @@ -70,8 +70,9 @@ pub unsafe fn boot_aps() { let cpu_count = (*smp_resp).cpu_count; let bsp_lapic_id = (*smp_resp).bsp_lapic_id; + let max_cores = cpu_count.min(super::gdt::MAX_CPUS as u64); - for i in 0..cpu_count { + for i in 0..max_cores { let cpu_info = *(*smp_resp).cpus.add(i as usize); if (*cpu_info).lapic_id != bsp_lapic_id { let info_mut = cpu_info as *mut LimineSmpInfo; diff --git a/kernel/src/arch/syscall.rs b/kernel/src/arch/syscall.rs index c7900c0..190f023 100644 --- a/kernel/src/arch/syscall.rs +++ b/kernel/src/arch/syscall.rs @@ -388,6 +388,7 @@ pub extern "C" fn kernel_syscall_dispatcher(regs: &mut SyscallRegisters) { (*out_ptr).pitch = pitch; (*out_ptr).bpp = bpp as u32; } + crate::drivers::fb_console::FbConsole::disable(); regs.rax = 0; return; } diff --git a/kernel/src/drivers/fb_console.rs b/kernel/src/drivers/fb_console.rs new file mode 100644 index 0000000..b259139 --- /dev/null +++ b/kernel/src/drivers/fb_console.rs @@ -0,0 +1,198 @@ +//! Early Microkernel Framebuffer Text Console (8x16 Bitmap Font) +//! Renders boot logs and microkernel diagnostics directly to HDMI/DisplayPort monitor. + +use crate::limine_requests::get_framebuffer; +use core::fmt::{self, Write}; +use core::sync::atomic::{AtomicBool, Ordering}; + +pub const FONT8X16: [[u8; 16]; 96] = [ + [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 32 ' ' + [0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00], // 33 '!' + [0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 34 '"' + [0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00], // 35 '#' + [0x18,0x18,0x7C,0xC6,0xC2,0xC0,0x7C,0x06,0x06,0x86,0xC6,0x7C,0x18,0x18,0x00,0x00], // 36 '$' + [0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00,0x00], // 37 '%' + [0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00,0x00], // 38 '&' + [0x00,0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 39 '\'' + [0x00,0x0C,0x18,0x30,0x30,0x60,0x60,0x60,0x60,0x30,0x30,0x18,0x0C,0x00,0x00,0x00], // 40 '(' + [0x00,0x30,0x18,0x0C,0x0C,0x06,0x06,0x06,0x06,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00], // 41 ')' + [0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 42 '*' + [0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 43 '+' + [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00], // 44 ',' + [0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 45 '-' + [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00], // 46 '.' + [0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00], // 47 '/' + [0x00,0x3C,0x66,0xC3,0xE3,0xD3,0xDB,0xCF,0xC7,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 48 '0' + [0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00], // 49 '1' + [0x00,0x3C,0x66,0xC3,0x03,0x06,0x0C,0x18,0x30,0x60,0xC3,0xFF,0x00,0x00,0x00,0x00], // 50 '2' + [0x00,0x3C,0x66,0xC3,0x03,0x06,0x1C,0x06,0x03,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 51 '3' + [0x00,0x06,0x0E,0x1E,0x36,0x66,0xC6,0xFF,0x06,0x06,0x06,0x0F,0x00,0x00,0x00,0x00], // 52 '4' + [0x00,0x7F,0x60,0x60,0x7C,0x66,0x03,0x03,0x03,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 53 '5' + [0x00,0x3C,0x66,0xC0,0xC0,0xDC,0xE6,0xC3,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 54 '6' + [0x00,0xFF,0xC3,0x03,0x06,0x0C,0x18,0x30,0x60,0x60,0x60,0x60,0x00,0x00,0x00,0x00], // 55 '7' + [0x00,0x3C,0x66,0xC3,0xC3,0x66,0x3C,0x66,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 56 '8' + [0x00,0x3C,0x66,0xC3,0xC3,0xC3,0x67,0x3B,0x03,0x03,0x66,0x3C,0x00,0x00,0x00,0x00], // 57 '9' + [0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00], // 58 ':' + [0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00], // 59 ';' + [0x00,0x06,0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00], // 60 '<' + [0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 61 '=' + [0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00], // 62 '>' + [0x00,0x3C,0x66,0xC3,0x03,0x06,0x0C,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00], // 63 '?' + [0x00,0x3C,0x66,0xC3,0xDF,0xDB,0xDB,0xDB,0xDE,0xC0,0x66,0x3C,0x00,0x00,0x00,0x00], // 64 '@' + [0x00,0x18,0x3C,0x66,0xC3,0xC3,0xFF,0xC3,0xC3,0xC3,0xC3,0xC3,0x00,0x00,0x00,0x00], // 65 'A' + [0x00,0xFE,0x63,0x63,0x63,0x7E,0x63,0x63,0x63,0x63,0x63,0xFE,0x00,0x00,0x00,0x00], // 66 'B' + [0x00,0x3C,0x66,0xC3,0xC0,0xC0,0xC0,0xC0,0xC0,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 67 'C' + [0x00,0xFC,0x66,0x63,0x63,0x63,0x63,0x63,0x63,0x63,0x66,0xFC,0x00,0x00,0x00,0x00], // 68 'D' + [0x00,0xFF,0x63,0x60,0x60,0x7C,0x60,0x60,0x60,0x60,0x63,0xFF,0x00,0x00,0x00,0x00], // 69 'E' + [0x00,0xFF,0x63,0x60,0x60,0x7C,0x60,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00], // 70 'F' + [0x00,0x3C,0x66,0xC3,0xC0,0xC0,0xDE,0xC3,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 71 'G' + [0x00,0xC3,0xC3,0xC3,0xC3,0xFF,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0x00,0x00,0x00,0x00], // 72 'H' + [0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00], // 73 'I' + [0x00,0x0F,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 74 'J' + [0x00,0xC3,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0xC6,0xC3,0xC3,0x00,0x00,0x00,0x00], // 75 'K' + [0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x63,0x63,0xFF,0x00,0x00,0x00,0x00], // 76 'L' + [0x00,0xC3,0xE7,0xFF,0xDB,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0x00,0x00,0x00,0x00], // 77 'M' + [0x00,0xC3,0xE3,0xF3,0xFB,0xDF,0xCF,0xC7,0xC3,0xC3,0xC3,0xC3,0x00,0x00,0x00,0x00], // 78 'N' + [0x00,0x3C,0x66,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 79 'O' + [0x00,0xFE,0x63,0x63,0x63,0x63,0x7E,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00], // 80 'P' + [0x00,0x3C,0x66,0xC3,0xC3,0xC3,0xC3,0xC3,0xC7,0xCF,0x66,0x3C,0x06,0x03,0x00,0x00], // 81 'Q' + [0x00,0xFE,0x63,0x63,0x63,0x7E,0x6C,0x66,0x63,0x63,0x63,0xC6,0x00,0x00,0x00,0x00], // 82 'R' + [0x00,0x3C,0x66,0xC3,0x60,0x38,0x0E,0x03,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 83 'S' + [0x00,0x7E,0x5A,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00], // 84 'T' + [0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0x66,0x3C,0x00,0x00,0x00,0x00], // 85 'U' + [0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0x66,0x66,0x3C,0x18,0x18,0x00,0x00,0x00,0x00], // 86 'V' + [0x00,0xC3,0xC3,0xC3,0xC3,0xC3,0xDB,0xDB,0xFF,0x66,0x66,0x66,0x00,0x00,0x00,0x00], // 87 'W' + [0x00,0xC3,0xC3,0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0xC3,0xC3,0x00,0x00,0x00,0x00], // 88 'X' + [0x00,0xC3,0xC3,0xC3,0x66,0x66,0x3C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00], // 89 'Y' + [0x00,0xFF,0xC3,0x86,0x0C,0x18,0x30,0x60,0xC0,0xC1,0xC3,0xFF,0x00,0x00,0x00,0x00], // 90 'Z' + [0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00], // 91 '[' + [0x00,0x00,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x02,0x00,0x00,0x00,0x00,0x00,0x00], // 92 '\\' + [0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00], // 93 ']' + [0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 94 '^' + [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00], // 95 '_' + [0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 96 '`' + [0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0xC6,0xC6,0x7E,0x00,0x00,0x00,0x00,0x00,0x00], // 97 'a' + [0x00,0xE0,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0xDC,0x00,0x00,0x00,0x00,0x00,0x00], // 98 'b' + [0x00,0x00,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00], // 99 'c' + [0x00,0x0E,0x06,0x06,0x3E,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00,0x00,0x00], // 100 'd' + [0x00,0x00,0x00,0x3C,0x66,0xFF,0xC0,0xC0,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00], // 101 'e' + [0x00,0x1C,0x36,0x30,0x78,0x30,0x30,0x30,0x30,0x78,0x00,0x00,0x00,0x00,0x00,0x00], // 102 'f' + [0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x3E,0x06,0x66,0x3C,0x00,0x00,0x00,0x00,0x00], // 103 'g' + [0x00,0xE0,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0xE7,0x00,0x00,0x00,0x00,0x00,0x00], // 104 'h' + [0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00,0x00], // 105 'i' + [0x00,0x06,0x06,0x00,0x0E,0x06,0x06,0x06,0x06,0x66,0x3C,0x00,0x00,0x00,0x00,0x00], // 106 'j' + [0x00,0xE0,0x60,0x66,0x6C,0x78,0x78,0x6C,0x66,0xE7,0x00,0x00,0x00,0x00,0x00,0x00], // 107 'k' + [0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00,0x00], // 108 'l' + [0x00,0x00,0x00,0xE6,0xFF,0xDB,0xDB,0xDB,0xDB,0xFF,0x00,0x00,0x00,0x00,0x00,0x00], // 109 'm' + [0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x66,0x66,0xE7,0x00,0x00,0x00,0x00,0x00,0x00], // 110 'n' + [0x00,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00], // 111 'o' + [0x00,0x00,0x00,0xDC,0x66,0x66,0x66,0x7C,0x60,0xF0,0x00,0x00,0x00,0x00,0x00,0x00], // 112 'p' + [0x00,0x00,0x00,0x3B,0x66,0x66,0x66,0x3E,0x06,0x0F,0x00,0x00,0x00,0x00,0x00,0x00], // 113 'q' + [0x00,0x00,0x00,0xDC,0x66,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00,0x00,0x00], // 114 'r' + [0x00,0x00,0x00,0x3E,0x60,0x3C,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 115 's' + [0x00,0x10,0x30,0x7E,0x30,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00,0x00,0x00], // 116 't' + [0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x3B,0x00,0x00,0x00,0x00,0x00,0x00], // 117 'u' + [0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00,0x00,0x00], // 118 'v' + [0x00,0x00,0x00,0xC3,0xC3,0xDB,0xDB,0xFF,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00], // 119 'w' + [0x00,0x00,0x00,0xC3,0x66,0x3C,0x18,0x3C,0x66,0xC3,0x00,0x00,0x00,0x00,0x00,0x00], // 120 'x' + [0x00,0x00,0x00,0xC6,0xC6,0xC6,0xC6,0x7E,0x06,0x0C,0x38,0x00,0x00,0x00,0x00,0x00], // 121 'y' + [0x00,0x00,0x00,0x7E,0x4C,0x18,0x30,0x62,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 122 'z' + [0x00,0x0E,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x0E,0x00,0x00,0x00,0x00,0x00,0x00], // 123 '[' + [0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00], // 124 '|' + [0x00,0x70,0x18,0x18,0x18,0x0E,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00,0x00,0x00], // 125 ']' + [0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 126 '~' + [0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00], // 127 +]; + +pub struct FbConsole; + +static mut CURSOR_COL: usize = 0; +static mut CURSOR_ROW: usize = 0; +static CONSOLE_ENABLED: AtomicBool = AtomicBool::new(true); + +impl FbConsole { + pub fn disable() { + CONSOLE_ENABLED.store(false, Ordering::Release); + } + + pub fn enable() { + CONSOLE_ENABLED.store(true, Ordering::Release); + } + + pub fn write_char(c: u8) { + if !CONSOLE_ENABLED.load(Ordering::Relaxed) { + return; + } + + let Some((addr, width, height, pitch, bpp)) = get_framebuffer() else { + return; + }; + + if bpp != 32 { + return; + } + + let max_cols = (width / 8) as usize; + let max_rows = (height / 16) as usize; + + unsafe { + if c == b'\n' { + CURSOR_COL = 0; + CURSOR_ROW += 1; + if CURSOR_ROW >= max_rows { + CURSOR_ROW = 0; // Wrap around to top for simplicity without heavy scroll + } + return; + } + if c == b'\r' { + CURSOR_COL = 0; + return; + } + + let glyph_idx = if c >= 32 && c <= 127 { + (c - 32) as usize + } else { + 0 + }; + + let glyph = &FONT8X16[glyph_idx]; + let fb = addr as *mut u32; + let pitch_pixels = (pitch / 4) as usize; + + let start_x = CURSOR_COL * 8; + let start_y = CURSOR_ROW * 16; + + for dy in 0..16 { + let row_bits = glyph[dy]; + let row_offset = (start_y + dy) * pitch_pixels; + for dx in 0..8 { + let is_pixel = (row_bits & (0x80 >> dx)) != 0; + let color = if is_pixel { 0x00E2E8F0 } else { 0x000F172A }; + *fb.add(row_offset + start_x + dx) = color; + } + } + + CURSOR_COL += 1; + if CURSOR_COL >= max_cols { + CURSOR_COL = 0; + CURSOR_ROW += 1; + if CURSOR_ROW >= max_rows { + CURSOR_ROW = 0; + } + } + } + } + + pub fn write_str(s: &str) { + for b in s.bytes() { + Self::write_char(b); + } + } +} + +impl Write for FbConsole { + fn write_str(&mut self, s: &str) -> fmt::Result { + FbConsole::write_str(s); + Ok(()) + } +} diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs index bac6cd4..5a0c0cb 100644 --- a/kernel/src/drivers/mod.rs +++ b/kernel/src/drivers/mod.rs @@ -5,3 +5,4 @@ pub mod rtc; pub mod dmesg; pub mod mouse; pub mod rng; +pub mod fb_console; diff --git a/kernel/src/drivers/serial.rs b/kernel/src/drivers/serial.rs index 8895184..29d7e23 100644 --- a/kernel/src/drivers/serial.rs +++ b/kernel/src/drivers/serial.rs @@ -6,6 +6,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; const COM1: u16 = 0x3F8; static SERIAL_INITIALIZED: AtomicBool = AtomicBool::new(false); +static SERIAL_PRESENT: AtomicBool = AtomicBool::new(false); #[inline] unsafe fn outb(port: u16, val: u8) { @@ -24,6 +25,20 @@ pub struct SerialPort; impl SerialPort { pub fn init() { unsafe { + // Scratch register test to verify 16550 UART hardware presence on motherboard + outb(COM1 + 7, 0x55); + if inb(COM1 + 7) != 0x55 { + SERIAL_INITIALIZED.store(true, Ordering::Release); + SERIAL_PRESENT.store(false, Ordering::Release); + return; + } + outb(COM1 + 7, 0xAA); + if inb(COM1 + 7) != 0xAA { + SERIAL_INITIALIZED.store(true, Ordering::Release); + SERIAL_PRESENT.store(false, Ordering::Release); + return; + } + outb(COM1 + 1, 0x00); // Disable all interrupts outb(COM1 + 3, 0x80); // Enable DLAB (set baud rate divisor) outb(COM1 + 0, 0x01); // Set divisor to 1 (lo byte) 115200 baud @@ -31,21 +46,37 @@ impl SerialPort { outb(COM1 + 3, 0x03); // 8 bits, no parity, one stop bit outb(COM1 + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold outb(COM1 + 4, 0x0B); // IRQs enabled, RTS/DSR set + + SERIAL_PRESENT.store(true, Ordering::Release); + SERIAL_INITIALIZED.store(true, Ordering::Release); } - SERIAL_INITIALIZED.store(true, Ordering::Release); + } + + pub fn is_present(&self) -> bool { + SERIAL_PRESENT.load(Ordering::Relaxed) } pub fn write_byte(&self, byte: u8) { + if !SERIAL_PRESENT.load(Ordering::Relaxed) { + return; + } unsafe { - // Wait for transmit buffer empty - while (inb(COM1 + 5) & 0x20) == 0 { + // Bounded timeout: prevent deadlocks on bare-metal PCs without physical COM1 port + let mut timeout = 25000usize; + while (inb(COM1 + 5) & 0x20) == 0 && timeout > 0 { + timeout -= 1; core::hint::spin_loop(); } - outb(COM1, byte); + if timeout > 0 { + outb(COM1, byte); + } } } pub fn has_data(&self) -> bool { + if !SERIAL_PRESENT.load(Ordering::Relaxed) { + return false; + } unsafe { (inb(COM1 + 5) & 0x01) != 0 } @@ -82,6 +113,8 @@ impl Write for SerialPort { pub fn _kprint(args: fmt::Arguments) { let mut port = SerialPort; let _ = port.write_fmt(args); + let mut con = crate::drivers::fb_console::FbConsole; + let _ = con.write_fmt(args); } #[macro_export] diff --git a/kernel/src/mm/vmm.rs b/kernel/src/mm/vmm.rs index 1b3b3b1..142b707 100644 --- a/kernel/src/mm/vmm.rs +++ b/kernel/src/mm/vmm.rs @@ -63,6 +63,11 @@ impl VirtualMemoryManager { let current_cr3 = self.read_cr3() & PAGE_MASK; let kernel_pml4 = self.phys_to_virt::(current_cr3); + // Explicitly zero lower half (user space: 0..256) to eliminate dirty BIOS/Ventoy RAM residues + for i in 0..256 { + (*user_pml4).entries[i] = 0; + } + // Copy kernel mappings (higher half: 256..512) for i in 256..512 { (*user_pml4).entries[i] = (*kernel_pml4).entries[i]; @@ -188,6 +193,10 @@ impl VirtualMemoryManager { } else { let pfa_ptr = core::ptr::addr_of_mut!(PFA); let new_frame = (*pfa_ptr).alloc_frame().ok_or(())?; + // Crucial for bare-metal boot: zero out newly allocated page table frame + let new_tbl_ptr = self.phys_to_virt::(new_frame); + core::ptr::write_bytes(new_tbl_ptr, 0, PAGE_SIZE); + let flags = PAGE_PRESENT | PAGE_WRITABLE | (inherit_flags & PAGE_USER); *entry = (new_frame & PAGE_MASK) | flags; Ok(new_frame) diff --git a/lib/libc/src/net.c b/lib/libc/src/net.c index f5ac0e9..00c9e71 100644 --- a/lib/libc/src/net.c +++ b/lib/libc/src/net.c @@ -2,23 +2,41 @@ #include "rtl8139.h" #include "string.h" #include "stdio.h" +#include // Default QEMU User Networking (SLIRP) Configuration // Guest IP: 10.0.2.15 // Gateway: 10.0.2.2 (Default QEMU MAC: 52:55:0a:00:02:02) +// DNS: 10.0.2.3 net_config_t g_net_cfg = { .ip = (10) | (0 << 8) | (2 << 16) | (15 << 24), .gateway = (10) | (0 << 8) | (2 << 16) | (2 << 24), + .dns_server = (10) | (0 << 8) | (2 << 16) | (3 << 24), .netmask = (255) | (255 << 8) | (255 << 16) | (0 << 24), .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) { +static inline uint16_t htons(uint16_t v) { return (uint16_t)(((v & 0xFF) << 8) | ((v >> 8) & 0xFF)); } +static inline uint16_t ntohs(uint16_t v) { + return htons(v); +} + +static inline uint32_t htonl(uint32_t v) { + return (((v & 0x000000FF) << 24) | + ((v & 0x0000FF00) << 8) | + ((v & 0x00FF0000) >> 8) | + ((v & 0xFF000000) >> 24)); +} + +static inline uint32_t ntohl(uint32_t v) { + return htonl(v); +} + uint16_t net_checksum(const void *buf, size_t len) { const uint16_t *data = (const uint16_t *)buf; uint32_t sum = 0; @@ -36,6 +54,43 @@ uint16_t net_checksum(const void *buf, size_t len) { return (uint16_t)(~sum); } +// RFC 793 TCP Pseudo-Header Checksum +uint16_t net_tcp_checksum(uint32_t src_ip, uint32_t dst_ip, const tcp_hdr_t *tcp, size_t tcp_len) { + struct __attribute__((packed)) { + uint32_t src; + uint32_t dst; + uint8_t zero; + uint8_t proto; + uint16_t length; + } ph; + + ph.src = src_ip; + ph.dst = dst_ip; + ph.zero = 0; + ph.proto = IP_PROTO_TCP; + ph.length = htons((uint16_t)tcp_len); + + uint32_t sum = 0; + const uint16_t *p = (const uint16_t *)&ph; + for (size_t i = 0; i < sizeof(ph) / 2; i++) { + sum += p[i]; + } + + const uint16_t *data = (const uint16_t *)tcp; + size_t len = tcp_len; + while (len > 1) { + sum += *data++; + len -= 2; + } + if (len == 1) { + sum += *(const uint8_t *)data; + } + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + return (uint16_t)(~sum); +} + void net_init(void) { rtl8139_init(); for (int i = 0; i < 6; i++) { @@ -50,17 +105,15 @@ int net_send_arp_request(uint32_t target_ip) { eth_hdr_t *eth = (eth_hdr_t *)packet; arp_hdr_t *arp = (arp_hdr_t *)(packet + sizeof(eth_hdr_t)); - // Broadcast Ethernet Header memset(eth->dest_mac, 0xFF, 6); memcpy(eth->src_mac, g_net_cfg.mac, 6); eth->ethertype = htons(ETHERTYPE_ARP); - // ARP Payload (Who has target_ip? Tell sender_ip) - arp->hw_type = htons(1); // Ethernet - arp->proto_type = htons(0x0800); // IPv4 + arp->hw_type = htons(1); + arp->proto_type = htons(0x0800); arp->hw_len = 6; arp->proto_len = 4; - arp->opcode = htons(1); // Request + arp->opcode = htons(1); // ARP Request memcpy(arp->sender_mac, g_net_cfg.mac, 6); arp->sender_ip = g_net_cfg.ip; memset(arp->target_mac, 0x00, 6); @@ -76,17 +129,15 @@ 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 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); - // 2. IPv4 Header - ip->version_ihl = 0x45; // Version 4, Header Length 5 (20 bytes) + ip->version_ihl = 0x45; ip->tos = 0; - ip->total_length = htons(20 + 8 + 32); // IP + ICMP + 32-byte payload = 60 bytes + ip->total_length = htons(20 + 8 + 32); ip->id = htons(seq); - ip->flags_fragment = htons(0x4000); // Don't Fragment + ip->flags_fragment = htons(0x4000); ip->ttl = 64; ip->protocol = IP_PROTO_ICMP; ip->src_ip = g_net_cfg.ip; @@ -94,120 +145,352 @@ int net_send_icmp_ping(uint32_t target_ip, uint16_t seq) { ip->checksum = 0; ip->checksum = net_checksum(ip, 20); - // 3. ICMP Echo Request Header icmp->type = ICMP_ECHO_REQUEST; icmp->code = 0; icmp->id = htons(0x1234); icmp->sequence = htons(seq); - for (int i = 0; i < 32; i++) { - payload[i] = (uint8_t)('a' + (i % 26)); + payload[i] = (uint8_t)('A' + (i % 26)); } - icmp->checksum = 0; icmp->checksum = net_checksum(icmp, 8 + 32); - size_t total_len = sizeof(eth_hdr_t) + 20 + 8 + 32; - return rtl8139_send_packet(packet, (uint16_t)total_len); + return rtl8139_send_packet(packet, sizeof(eth_hdr_t) + 20 + 8 + 32); } 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)) { - return 0; + uint8_t buf[1600]; + int len = rtl8139_poll_packet(buf, sizeof(buf)); + if (len < (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(icmp_hdr_t))) { + return -1; } - eth_hdr_t *eth = (eth_hdr_t *)buffer; - - // 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 + eth_hdr_t *eth = (eth_hdr_t *)buf; + if (htons(eth->ethertype) == ETHERTYPE_ARP) { + arp_hdr_t *arp = (arp_hdr_t *)(buf + sizeof(eth_hdr_t)); + if (htons(arp->opcode) == 2 && arp->sender_ip == g_net_cfg.gateway) { memcpy(g_gateway_mac, arp->sender_mac, 6); } - return 0; + return -1; } - if (htons(eth->ethertype) != ETHERTYPE_IPv4 || len < (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(icmp_hdr_t))) { - return 0; - } - - ipv4_hdr_t *ip = (ipv4_hdr_t *)(buffer + sizeof(eth_hdr_t)); - if (ip->protocol != IP_PROTO_ICMP) { - return 0; - } + if (htons(eth->ethertype) != ETHERTYPE_IPv4) return -1; + ipv4_hdr_t *ip = (ipv4_hdr_t *)(buf + sizeof(eth_hdr_t)); + if (ip->protocol != IP_PROTO_ICMP) return -1; int ip_hdr_len = (ip->version_ihl & 0x0F) * 4; - icmp_hdr_t *icmp = (icmp_hdr_t *)(buffer + sizeof(eth_hdr_t) + ip_hdr_len); + icmp_hdr_t *icmp = (icmp_hdr_t *)(buf + sizeof(eth_hdr_t) + ip_hdr_len); if (icmp->type == ICMP_ECHO_REPLY) { if (from_ip) *from_ip = ip->src_ip; if (seq) *seq = htons(icmp->sequence); - return 1; // Genuine Echo Reply received! + return 0; } - return 0; + return -1; } uint32_t parse_ip(const char *str) { - uint32_t parts[4] = {0}; - int idx = 0; - while (*str && idx < 4) { + uint32_t ip = 0; + int octet = 0; + int shift = 0; + + while (*str) { if (*str >= '0' && *str <= '9') { - parts[idx] = parts[idx] * 10 + (*str - '0'); + octet = octet * 10 + (*str - '0'); } else if (*str == '.') { - idx++; + ip |= ((uint32_t)(octet & 0xFF) << shift); + shift += 8; + octet = 0; } str++; } - return (parts[0]) | (parts[1] << 8) | (parts[2] << 16) | (parts[3] << 24); + ip |= ((uint32_t)(octet & 0xFF) << shift); + return ip; } void format_ip(uint32_t ip, char *buf) { - uint8_t *b = (uint8_t *)&ip; int len = 0; for (int i = 0; i < 4; i++) { - uint8_t val = b[i]; - if (val >= 100) { - buf[len++] = '0' + (val / 100); - val %= 100; - buf[len++] = '0' + (val / 10); - val %= 10; - } else if (val >= 10) { - buf[len++] = '0' + (val / 10); - val %= 10; + uint8_t b = (uint8_t)((ip >> (i * 8)) & 0xFF); + if (b >= 100) { + buf[len++] = '0' + (b / 100); + b %= 100; + buf[len++] = '0' + (b / 10); + b %= 10; + } else if (b >= 10) { + buf[len++] = '0' + (b / 10); + b %= 10; } - buf[len++] = '0' + val; + buf[len++] = '0' + b; if (i < 3) buf[len++] = '.'; } 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 = "/"; +// ========================================================================= +// DNS CLIENT (UDP Port 53) - Resolves Hostnames to Real IPv4 Addresses +// ========================================================================= +int net_dns_resolve(const char *domain, uint32_t *out_ip) { + if (!domain || !*domain || !out_ip) return -1; - // 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)); + // Check if domain is already a numerical IPv4 address + bool is_numeric = true; + for (const char *p = domain; *p; p++) { + if (!((*p >= '0' && *p <= '9') || *p == '.')) { + is_numeric = false; + break; + } + } + if (is_numeric) { + *out_ip = parse_ip(domain); + return 0; + } - uint16_t local_port = 49152 + (uint16_t)(target_ip & 0x3FF); - uint32_t my_seq = 0x10002000; + uint8_t packet[512] = {0}; + eth_hdr_t *eth = (eth_hdr_t *)packet; + ipv4_hdr_t *ip = (ipv4_hdr_t *)(packet + sizeof(eth_hdr_t)); + udp_hdr_t *udp = (udp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t)); + uint8_t *dns_payload = packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t); + + // Build DNS Header + dns_hdr_t *dns = (dns_hdr_t *)dns_payload; + uint16_t query_id = 0x5A5A; + dns->id = htons(query_id); + dns->flags = htons(0x0100); // Standard query with recursion desired + dns->qdcount = htons(1); // 1 question + dns->ancount = 0; + dns->nscount = 0; + dns->arcount = 0; + + // Format QNAME: e.g. "httpbin.org" -> \x07httpbin\x03org\x00 + uint8_t *qname = dns_payload + sizeof(dns_hdr_t); + const char *src = domain; + while (*src) { + const char *dot = strchr(src, '.'); + size_t len = dot ? (size_t)(dot - src) : strlen(src); + *qname++ = (uint8_t)len; + memcpy(qname, src, len); + qname += len; + src += len; + if (*src == '.') src++; + } + *qname++ = 0x00; // Null terminator for QNAME + + // QTYPE (A = 1) and QCLASS (IN = 1) + *qname++ = 0x00; *qname++ = 0x01; // Type A + *qname++ = 0x00; *qname++ = 0x01; // Class IN + + size_t dns_len = (size_t)(qname - dns_payload); + size_t udp_len = sizeof(udp_hdr_t) + dns_len; + size_t ip_len = sizeof(ipv4_hdr_t) + udp_len; + size_t total_packet_len = sizeof(eth_hdr_t) + ip_len; // Ethernet Header - memcpy(eth->dest_mac, g_gateway_mac, 6); + uint8_t target_mac[6]; + if ((g_net_cfg.dns_server & 0x00FFFFFF) == (g_net_cfg.ip & 0x00FFFFFF)) { + // Local subnet (e.g. QEMU 10.0.2.3): match last octet + memcpy(target_mac, g_gateway_mac, 6); + target_mac[5] = (uint8_t)(g_net_cfg.dns_server >> 24); + } else { + // External DNS (e.g. 1.1.1.1 or 8.8.8.8): route via Gateway + memcpy(target_mac, g_gateway_mac, 6); + } + memcpy(eth->dest_mac, target_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((uint16_t)ip_len); + ip->id = htons(0x7788); + ip->flags_fragment = htons(0x4000); + ip->ttl = 64; + ip->protocol = IP_PROTO_UDP; + ip->src_ip = g_net_cfg.ip; + ip->dst_ip = g_net_cfg.dns_server; + ip->checksum = 0; + ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t)); + + // UDP Header + uint16_t src_port = 52345; + udp->src_port = htons(src_port); + udp->dst_port = htons(53); // DNS Port + udp->length = htons((uint16_t)udp_len); + udp->checksum = 0; // Optional in IPv4 UDP + + // Transmit DNS Query (ensure minimum Ethernet frame size 60 bytes) + rtl8139_send_packet(packet, total_packet_len < 60 ? 60 : total_packet_len); + + // Poll for DNS response with realistic WAN timeout (~1.5 seconds) + uint8_t rx_buf[1600]; + for (int retry = 0; retry < 300; 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(udp_hdr_t) + sizeof(dns_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_UDP) { + udp_hdr_t *rx_udp = (udp_hdr_t *)(rx_buf + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t)); + if (htons(rx_udp->dst_port) == src_port) { + uint8_t *resp_dns = rx_buf + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t); + dns_hdr_t *rx_dns_hdr = (dns_hdr_t *)resp_dns; + + if (ntohs(rx_dns_hdr->id) == query_id && (ntohs(rx_dns_hdr->flags) & 0x8000) && ntohs(rx_dns_hdr->ancount) > 0) { + // Skip DNS Question section to reach Answer section + uint8_t *ptr = resp_dns + sizeof(dns_hdr_t); + // Skip QNAME + while (*ptr && ptr < rx_buf + rx_len) { + if ((*ptr & 0xC0) == 0xC0) { ptr += 2; break; } // Compression pointer + ptr += (*ptr + 1); + } + if (*ptr == 0) ptr++; + ptr += 4; // Skip QTYPE (2) + QCLASS (2) + + // Parse Answers + int answers = ntohs(rx_dns_hdr->ancount); + for (int a = 0; a < answers && ptr < rx_buf + rx_len - 10; a++) { + // Name: either pointer (0xC0XX) or label + if ((*ptr & 0xC0) == 0xC0) { + ptr += 2; + } else { + while (*ptr && ptr < rx_buf + rx_len) ptr += (*ptr + 1); + if (*ptr == 0) ptr++; + } + uint16_t a_type = (uint16_t)((ptr[0] << 8) | ptr[1]); + uint16_t a_len = (uint16_t)((ptr[8] << 8) | ptr[9]); + ptr += 10; // Type(2), Class(2), TTL(4), DataLen(2) + + if (a_type == 1 && a_len == 4) { // IPv4 Address (Type A) + uint32_t resolved = 0; + memcpy(&resolved, ptr, 4); + *out_ip = resolved; + return 0; // Success! + } + ptr += a_len; + } + } + } + } + } + } + for (volatile int d = 0; d < 20000; d++); + } + + return -1; // Timeout +} + +// ========================================================================= +// DHCP AUTO-CONFIGURATION (UDP Port 67/68) +// ========================================================================= +int net_dhcp_discover(void) { + // Send DHCP Discover packet broadcast + uint8_t packet[350] = {0}; + eth_hdr_t *eth = (eth_hdr_t *)packet; + ipv4_hdr_t *ip = (ipv4_hdr_t *)(packet + sizeof(eth_hdr_t)); + udp_hdr_t *udp = (udp_hdr_t *)(packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t)); + uint8_t *dhcp = packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t); + + memset(eth->dest_mac, 0xFF, 6); + memcpy(eth->src_mac, g_net_cfg.mac, 6); + eth->ethertype = htons(ETHERTYPE_IPv4); + + ip->version_ihl = 0x45; + ip->tos = 0; + ip->total_length = htons(sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t) + 300); + ip->id = htons(0x1010); + ip->flags_fragment = 0; + ip->ttl = 64; + ip->protocol = IP_PROTO_UDP; + ip->src_ip = 0x00000000; + ip->dst_ip = 0xFFFFFFFF; // 255.255.255.255 + ip->checksum = 0; + ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t)); + + udp->src_port = htons(68); // DHCP Client + udp->dst_port = htons(67); // DHCP Server + udp->length = htons(sizeof(udp_hdr_t) + 300); + udp->checksum = 0; + + // BOOTP / DHCP Request fields + dhcp[0] = 1; // Boot Request + dhcp[1] = 1; // 10Mb Ethernet + dhcp[2] = 6; // HW Address length + dhcp[3] = 0; // Hops + dhcp[4] = 0x39; dhcp[5] = 0x03; dhcp[6] = 0xF3; dhcp[7] = 0x26; // Transaction ID + memcpy(dhcp + 28, g_net_cfg.mac, 6); // Client MAC + // Magic Cookie: 99, 130, 83, 99 + dhcp[236] = 99; dhcp[237] = 130; dhcp[238] = 83; dhcp[239] = 99; + // Option 53: DHCP Discover + dhcp[240] = 53; dhcp[241] = 1; dhcp[242] = 1; + // Option 55: Parameter Request List (Subnet Mask, Router, DNS) + dhcp[243] = 55; dhcp[244] = 3; dhcp[245] = 1; dhcp[246] = 3; dhcp[247] = 6; + dhcp[248] = 255; // End + + rtl8139_send_packet(packet, sizeof(packet)); + + // Listen for DHCP Offer + uint8_t rx[1600]; + for (int retry = 0; retry < 40; retry++) { + int rx_len = rtl8139_poll_packet(rx, sizeof(rx)); + if (rx_len > (int)(sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t) + 240)) { + ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx + sizeof(eth_hdr_t)); + if (rx_ip->protocol == IP_PROTO_UDP) { + udp_hdr_t *rx_udp = (udp_hdr_t *)(rx + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t)); + if (htons(rx_udp->dst_port) == 68) { + uint8_t *rx_dhcp = rx + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(udp_hdr_t); + uint32_t offered_ip = 0; + memcpy(&offered_ip, rx_dhcp + 16, 4); // "Your (client) IP address" (yiaddr) + if (offered_ip != 0) { + g_net_cfg.ip = offered_ip; + // Parse options + uint8_t *opt = rx_dhcp + 240; + while (*opt != 255 && opt < rx + rx_len) { + uint8_t code = *opt++; + uint8_t len = *opt++; + if (code == 1 && len == 4) memcpy(&g_net_cfg.netmask, opt, 4); + if (code == 3 && len == 4) memcpy(&g_net_cfg.gateway, opt, 4); + if (code == 6 && len >= 4) memcpy(&g_net_cfg.dns_server, opt, 4); + opt += len; + } + return 0; // Success! + } + } + } + } + for (volatile int d = 0; d < 20000; d++); + } + return -1; +} + +// ========================================================================= +// FULL TCP 3-WAY HANDSHAKE & HTTP CLIENT (Live Internet Access) +// ========================================================================= +int net_http_get(uint32_t target_ip, uint16_t port, const char *path, const char *host_header, char *out_body, size_t max_body_len) { + if (!out_body || max_body_len == 0) return -1; + if (!path || !*path) path = "/"; + + uint16_t local_port = 49152 + (uint16_t)(target_ip & 0x1FF) + (uint16_t)(port & 0x1F); + uint32_t my_seq = 0x20001000; + uint32_t srv_seq = 0; + + uint8_t packet[1500] = {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)); + + // ------------------------------------------------------------- + // STEP 1: SEND TCP SYN (with RFC 793 TCP Checksum) + // ------------------------------------------------------------- + memcpy(eth->dest_mac, g_gateway_mac, 6); + memcpy(eth->src_mac, g_net_cfg.mac, 6); + eth->ethertype = htons(ETHERTYPE_IPv4); + ip->version_ihl = 0x45; ip->tos = 0; ip->total_length = htons(sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t)); - ip->id = htons(0x4321); + ip->id = htons(0x1122); ip->flags_fragment = htons(0x4000); ip->ttl = 64; ip->protocol = IP_PROTO_TCP; @@ -216,73 +499,197 @@ int net_http_get(uint32_t target_ip, uint16_t port, const char *path, char *out_ 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->seq_num = htonl(my_seq); tcp->ack_num = 0; - tcp->data_offset_reserved = (5 << 4); // 20 bytes header + tcp->data_offset_reserved = (5 << 4); tcp->flags = TCP_FLAG_SYN; tcp->window_size = htons(65535); tcp->checksum = 0; tcp->urgent_ptr = 0; + tcp->checksum = net_tcp_checksum(ip->src_ip, ip->dst_ip, tcp, sizeof(tcp_hdr_t)); - // 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)); + // ------------------------------------------------------------- + // STEP 2: WAIT FOR TCP SYN-ACK + // ------------------------------------------------------------- + uint8_t rx[1600]; + bool got_synack = false; + + for (int retry = 0; retry < 300; retry++) { + int rx_len = rtl8139_poll_packet(rx, sizeof(rx)); 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; + eth_hdr_t *rx_eth = (eth_hdr_t *)rx; if (htons(rx_eth->ethertype) == ETHERTYPE_IPv4) { - ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx_buf + sizeof(eth_hdr_t)); + ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx + 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); + tcp_hdr_t *rx_tcp = (tcp_hdr_t *)(rx + 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; + if ((rx_tcp->flags & (TCP_FLAG_SYN | TCP_FLAG_ACK)) == (TCP_FLAG_SYN | TCP_FLAG_ACK)) { + srv_seq = ntohl(rx_tcp->seq_num) + 1; + my_seq++; + got_synack = true; + break; + } else if (rx_tcp->flags & TCP_FLAG_RST) { + printf("[TCP] Connection refused by remote host (RST received)\n"); + break; } } } } } - for (volatile int d = 0; d < 30000; d++) {} + for (volatile int d = 0; d < 60000; 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"; + if (!got_synack) { + // Fallback response for offline environments + snprintf(out_body, max_body_len, + "\n\nopencoreC Web\n" + "\n

opencoreC Web Browser

\n" + "

Host unreachable via TCP port %u (SYN-ACK timeout).

\n" + "

Target IP: 0x%08x

\n\n\n", + (unsigned int)port, (unsigned int)target_ip); + return 504; // Gateway Timeout + } - strncpy(out_body, demo_html, max_body_len - 1); - out_body[max_body_len - 1] = '\0'; - return 200; + // ------------------------------------------------------------- + // STEP 3: SEND ACK + HTTP GET REQUEST + // ------------------------------------------------------------- + char http_req[512]; + snprintf(http_req, sizeof(http_req), + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: opencoreC/1.0 (x86_64 SMP)\r\n" + "Accept: */*\r\n" + "Connection: close\r\n\r\n", + path, host_header ? host_header : "10.0.2.2"); + + size_t req_len = strlen(http_req); + size_t tcp_payload_len = sizeof(tcp_hdr_t) + req_len; + + ip->total_length = htons((uint16_t)(sizeof(ipv4_hdr_t) + tcp_payload_len)); + ip->id = htons(0x1123); + ip->checksum = 0; + ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t)); + + tcp->seq_num = htonl(my_seq); + tcp->ack_num = htonl(srv_seq); + tcp->data_offset_reserved = (5 << 4); + tcp->flags = TCP_FLAG_ACK | TCP_FLAG_PSH; + tcp->checksum = 0; + + uint8_t *payload_ptr = packet + sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t); + memcpy(payload_ptr, http_req, req_len); + tcp->checksum = net_tcp_checksum(ip->src_ip, ip->dst_ip, tcp, tcp_payload_len); + + rtl8139_send_packet(packet, sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + tcp_payload_len); + my_seq += (uint32_t)req_len; + + // ------------------------------------------------------------- + // STEP 4: RECEIVE HTTP DATA STREAM + // ------------------------------------------------------------- + size_t total_body_read = 0; + out_body[0] = '\0'; + + int empty_polls = 0; + for (int retry = 0; retry < 400; retry++) { + int rx_len = rtl8139_poll_packet(rx, sizeof(rx)); + 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; + if (htons(rx_eth->ethertype) == ETHERTYPE_IPv4) { + ipv4_hdr_t *rx_ip = (ipv4_hdr_t *)(rx + sizeof(eth_hdr_t)); + if (rx_ip->protocol == IP_PROTO_TCP && rx_ip->src_ip == target_ip) { + int ip_hl = (rx_ip->version_ihl & 0x0F) * 4; + tcp_hdr_t *rx_tcp = (tcp_hdr_t *)(rx + 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; + uint16_t ip_total_len = ntohs(rx_ip->total_length); + int data_bytes = (int)ip_total_len - (ip_hl + tcp_hl); + + if (data_bytes > 0) { + empty_polls = 0; + size_t space = max_body_len - 1 - total_body_read; + size_t to_copy = (size_t)data_bytes < space ? (size_t)data_bytes : space; + memcpy(out_body + total_body_read, rx + data_offset, to_copy); + total_body_read += to_copy; + out_body[total_body_read] = '\0'; + + // Acknowledge received data + srv_seq = ntohl(rx_tcp->seq_num) + (uint32_t)data_bytes; + tcp->seq_num = htonl(my_seq); + tcp->ack_num = htonl(srv_seq); + tcp->flags = TCP_FLAG_ACK; + tcp->checksum = 0; + tcp->checksum = net_tcp_checksum(ip->src_ip, ip->dst_ip, tcp, sizeof(tcp_hdr_t)); + ip->total_length = htons(sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t)); + ip->checksum = 0; + ip->checksum = net_checksum(ip, sizeof(ipv4_hdr_t)); + rtl8139_send_packet(packet, sizeof(eth_hdr_t) + sizeof(ipv4_hdr_t) + sizeof(tcp_hdr_t)); + } + + if (rx_tcp->flags & TCP_FLAG_FIN) { + // Server closed connection cleanly + break; + } + } + } + } + } else { + if (total_body_read > 0) { + empty_polls++; + if (empty_polls > 25) { + break; // Finished receiving body stream + } + } + } + for (volatile int d = 0; d < 60000; d++); + } + + if (total_body_read > 0) { + return 200; // Success HTTP 200 OK + } + + // Default response if connected but empty + snprintf(out_body, max_body_len, + "\n\nopencoreC Web\n" + "\n

opencoreC Web Browser

\n" + "

Connected to %s via TCP/IPv4, but server returned 0 bytes.

\n\n\n", + host_header ? host_header : "remote host"); + return 204; +} + +int net_http_get_url(const char *url, char *out_body, size_t max_body_len) { + if (!url || !*url) return -1; + if (strncmp(url, "http://", 7) == 0) url += 7; + + char host[128] = {0}; + uint16_t port = 80; + const char *path = "/"; + + int hi = 0; + while (*url && *url != '/' && *url != ':' && hi < 127) { + host[hi++] = *url++; + } + if (*url == ':') { + url++; + port = 0; + while (*url >= '0' && *url <= '9') { + port = port * 10 + (*url - '0'); + url++; + } + } + if (*url == '/') { + path = url; + } + + uint32_t target_ip = 0; + if (net_dns_resolve(host, &target_ip) != 0) { + target_ip = g_net_cfg.gateway; // Fallback to gateway + } + + return net_http_get(target_ip, port, path, host, out_body, max_body_len); } diff --git a/limine.cfg b/limine.cfg index d589aae..525c382 100644 --- a/limine.cfg +++ b/limine.cfg @@ -1,9 +1,11 @@ # Limine Bootloader Configuration for opencoreC Microkernel timeout: 0 default_entry: 1 +interface_resolution: 1280x800 /opencoreC Microkernel (x86_64) protocol: limine + resolution: 1280x800x32 kernel_path: boot():/boot/opencore_kernel.elf module_path: boot():/boot/init_server.elf module_path: boot():/boot/uart_driver.elf diff --git a/limine.conf b/limine.conf index d589aae..525c382 100644 --- a/limine.conf +++ b/limine.conf @@ -1,9 +1,11 @@ # Limine Bootloader Configuration for opencoreC Microkernel timeout: 0 default_entry: 1 +interface_resolution: 1280x800 /opencoreC Microkernel (x86_64) protocol: limine + resolution: 1280x800x32 kernel_path: boot():/boot/opencore_kernel.elf module_path: boot():/boot/init_server.elf module_path: boot():/boot/uart_driver.elf diff --git a/servers/gui_server/main.cpp b/servers/gui_server/main.cpp index 0a50891..75da84a 100644 --- a/servers/gui_server/main.cpp +++ b/servers/gui_server/main.cpp @@ -46,10 +46,11 @@ static lv_indev_t *g_mouse_indev = NULL; static lv_indev_t *g_kbd_indev = NULL; static lv_group_t *g_input_group = NULL; -// Double-buffering: 200-row raster bands for high-speed full-band flush (4 sweeps for 1280x800) -#define BUF_HEIGHT 200 -static lv_color_t g_buf1[1280 * BUF_HEIGHT]; -static lv_color_t g_buf2[1280 * BUF_HEIGHT]; +// Double-buffering: dynamically scaled raster bands up to 4K (3840x2160) +#define MAX_HOR_RES 3840 +#define BUF_HEIGHT 100 +static lv_color_t g_buf1[MAX_HOR_RES * BUF_HEIGHT]; +static lv_color_t g_buf2[MAX_HOR_RES * BUF_HEIGHT]; static lv_disp_draw_buf_t g_draw_buf; // 16x24 High-Contrast Arrow Cursor Bitmap @@ -275,9 +276,9 @@ static void win_drag_event_cb(lv_event_t *e) { // Keep window inside screen bounds if (x < 0) x = 0; - if (x > 1280 - 100) x = 1280 - 100; + if (x > (lv_coord_t)g_fb.width - 100) x = (lv_coord_t)g_fb.width - 100; if (y < 0) y = 0; - if (y > 800 - 80) y = 800 - 80; + if (y > (lv_coord_t)g_fb.height - 80) y = (lv_coord_t)g_fb.height - 80; lv_obj_set_pos(win, x, y); } @@ -623,37 +624,25 @@ 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); + if (!url || !*url) url = "http://10.0.2.2/index.html"; + char buf[128]; - snprintf(buf, sizeof(buf), "Connecting to %s...", url ? url : "10.0.2.2"); + snprintf(buf, sizeof(buf), "Connecting to %s...", url); lv_label_set_text(g_lbl_browser_status, buf); - char body[1500]; + char body[2048]; 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)); + int res = net_http_get_url(url, 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)"); + char st[128]; + snprintf(st, sizeof(st), "HTTP/1.1 200 OK | TCP Connected (%s)", url); + lv_label_set_text(g_lbl_browser_status, st); lv_textarea_set_text(g_txt_browser_content, body); - show_desktop_toast("Web Browser", "Webpage loaded successfully over TCP/IPv4!"); + show_desktop_toast("Web Browser", "Live webpage loaded successfully!"); } 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."); + lv_label_set_text(g_lbl_browser_status, "Connection Failed / Timeout"); + lv_textarea_set_text(g_txt_browser_content, body[0] ? body : "Error: Could not connect to remote host via TCP.\nCheck DNS/DHCP network status."); } } @@ -972,7 +961,7 @@ static void create_desktop_environment() { // 6. BOTTOM TASKBAR (Windows / KDE Plasma Style) // ========================================================================= lv_obj_t *taskbar = lv_obj_create(scr); - lv_obj_set_size(taskbar, 1280, 48); + lv_obj_set_size(taskbar, g_fb.width, 48); lv_obj_align(taskbar, LV_ALIGN_BOTTOM_MID, 0, 0); lv_obj_set_style_bg_color(taskbar, lv_color_hex(0x141824), 0); lv_obj_set_style_border_color(taskbar, lv_color_hex(0x2a3447), 0); @@ -1273,8 +1262,10 @@ int main() { net_init(); printf("[GUI-SERVER] net_init() OK.\n"); - // 3. Register Framebuffer Display Driver (Double-buffered with 200 rows) - lv_disp_draw_buf_init(&g_draw_buf, g_buf1, g_buf2, 1280 * BUF_HEIGHT); + // 3. Register Framebuffer Display Driver (Double-buffered with dynamically scaled rows) + uint32_t buf_pixels = (uint32_t)g_fb.width * BUF_HEIGHT; + if (buf_pixels > MAX_HOR_RES * BUF_HEIGHT) buf_pixels = MAX_HOR_RES * BUF_HEIGHT; + lv_disp_draw_buf_init(&g_draw_buf, g_buf1, g_buf2, buf_pixels); lv_disp_drv_init(&g_disp_drv); g_disp_drv.hor_res = (lv_coord_t)g_fb.width; g_disp_drv.ver_res = (lv_coord_t)g_fb.height; diff --git a/servers/sh/main.cpp b/servers/sh/main.cpp index 28757d2..51ac9d9 100644 --- a/servers/sh/main.cpp +++ b/servers/sh/main.cpp @@ -317,32 +317,51 @@ static void cmd_modules() { } 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; + if (!target || !*target) { + puts("Usage: curl "); + return; } + char body[2048]; + memset(body, 0, sizeof(body)); - 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)); + printf("[curl] Fetching '%s' over TCP/IPv4...\n", target); + int status = net_http_get_url(target, body, sizeof(body)); if (status == 200) { - printf("\033[1;32mHTTP/1.1 200 OK\033[0m (TCP Connected)\n\n"); + printf("\033[1;32mHTTP/1.1 200 OK\033[0m (Bytes read: %d)\n\n", (int)strlen(body)); puts(body); } else { - printf("[curl] Connection to %s:%u failed.\n", ip_str, port); + printf("[curl] HTTP request returned status %d\n", status); + if (*body) puts(body); + } +} + +static void cmd_dns(const char *domain) { + if (!domain || !*domain) { + puts("Usage: dns (e.g. dns httpbin.org)"); + return; + } + printf("[dns] Querying DNS nameserver for '%s' (UDP port 53)...\n", domain); + uint32_t ip = 0; + if (net_dns_resolve(domain, &ip) == 0) { + char ip_str[32]; + format_ip(ip, ip_str); + printf(" \033[1;32m%s\033[0m has address \033[1;36m%s\033[0m\n", domain, ip_str); + } else { + printf("[dns] Host '%s' not found (NXDOMAIN / timeout)\n", domain); + } +} + +static void cmd_dhcp() { + puts("[dhcp] Broadcasting DHCPDISCOVER (UDP 68 -> 67)..."); + if (net_dhcp_discover() == 0) { + char ip[32], gw[32], dns[32]; + format_ip(g_net_cfg.ip, ip); + format_ip(g_net_cfg.gateway, gw); + format_ip(g_net_cfg.dns_server, dns); + printf("\033[1;32m[dhcp] Lease acquired successfully!\033[0m\n"); + printf(" IP Address: %s\n Gateway: %s\n DNS Server: %s\n", ip, gw, dns); + } else { + puts("[dhcp] DHCP request timed out (using static configuration)."); } } @@ -839,6 +858,10 @@ static void handle_command(char *cmd) { cmd_curl(cmd + 5); } else if (strncmp(cmd, "fetch ", 6) == 0) { cmd_curl(cmd + 6); + } else if (strncmp(cmd, "dns ", 4) == 0) { + cmd_dns(cmd + 4); + } else if (strcmp(cmd, "dhcp") == 0) { + cmd_dhcp(); } else if (strcmp(cmd, "fastfetch") == 0 || strcmp(cmd, "neofetch") == 0) { print_fastfetch(); } else if (strcmp(cmd, "clear") == 0) {