diff --git a/Makefile b/Makefile index 8230943..9338d1a 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ run: $(ISO_IMAGE) qemu-system-x86_64 -m 4G -serial stdio -cdrom $< -boot d \ -smp 4 \ -audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \ - -netdev user,id=net0,hostfwd=udp::12346-:12345 -device e1000,netdev=net0 \ + -netdev user,id=net0,hostfwd=udp::12346-:12345 -device virtio-net-pci,netdev=net0 \ -vga std -global VGA.xres=1920 -global VGA.yres=1080 \ -display cocoa,show-cursor=off \ -drive file=disk.img,format=raw,file.locking=off \ diff --git a/boredos.iso b/boredos.iso index 676f1ce..c229cf0 100644 Binary files a/boredos.iso and b/boredos.iso differ diff --git a/build/cmd.o b/build/cmd.o index b9d4a48..ea26450 100644 Binary files a/build/cmd.o and b/build/cmd.o differ diff --git a/build/e1000.o b/build/e1000.o index be38f70..971f21a 100644 Binary files a/build/e1000.o and b/build/e1000.o differ diff --git a/src/kernel/e1000.c b/src/kernel/e1000.c index f811c62..8b2731e 100644 --- a/src/kernel/e1000.c +++ b/src/kernel/e1000.c @@ -130,6 +130,14 @@ e1000_device_t* e1000_get_device(void) { return &e1000_dev; } +int e1000_get_mac(uint8_t* mac_out) { + if (!e1000_initialized) return -1; + for (int i = 0; i < 6; i++) { + mac_out[i] = e1000_dev.mac_address.bytes[i]; + } + return 0; +} + int e1000_send_packet(const void* data, size_t length) { if (!e1000_initialized || !e1000_dev.initialized) return -1; if (length > 2048) return -1; diff --git a/src/kernel/e1000_netif.h b/src/kernel/e1000_netif.h deleted file mode 100644 index 0777530..0000000 --- a/src/kernel/e1000_netif.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef E1000_NETIF_H -#define E1000_NETIF_H - -#include "lwip/netif.h" - -err_t e1000_netif_init(struct netif *netif); -void e1000_netif_poll(struct netif *netif); - -#endif diff --git a/src/kernel/network.c b/src/kernel/network.c index 6a4e501..fcda48c 100644 --- a/src/kernel/network.c +++ b/src/kernel/network.c @@ -10,12 +10,13 @@ #include "lwip/raw.h" #include "lwip/sys.h" #include "netif/ethernet.h" -#include "e1000_netif.h" +#include "nic_netif.h" #include "kutils.h" #include "pci.h" #include "e1000.h" +#include "nic.h" -static struct netif e1000_netif; +static struct netif nic_netif; static int lwip_initialized = 0; static struct tcp_pcb *current_tcp_pcb = NULL; @@ -58,14 +59,9 @@ static err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err) int network_init(void) { if (lwip_initialized) return 0; - // First, find and initialize the E1000 device if not already done - if (!e1000_get_device()) { - pci_device_t pci_dev; - if (pci_find_device(E1000_VENDOR_ID, E1000_DEVICE_ID_82540EM, &pci_dev)) { - if (e1000_init(&pci_dev) != 0) return -1; - } else { - return -1; // No E1000 found - } + // First, find and initialize the generic NIC device if not already done + if (nic_init() != 0) { + return -1; // No supported NIC found } lwip_init(); @@ -76,12 +72,12 @@ int network_init(void) { ip4_addr_set_zero(&netmask); ip4_addr_set_zero(&gw); - if (netif_add(&e1000_netif, &ipaddr, &netmask, &gw, NULL, e1000_netif_init, ethernet_input) == NULL) { + if (netif_add(&nic_netif, &ipaddr, &netmask, &gw, NULL, nic_netif_init, ethernet_input) == NULL) { return -1; } - netif_set_default(&e1000_netif); - netif_set_up(&e1000_netif); + netif_set_default(&nic_netif); + netif_set_up(&nic_netif); lwip_initialized = 1; @@ -113,13 +109,13 @@ int network_init(void) { int network_get_mac_address(mac_address_t* mac) { if (!lwip_initialized) return -1; - for (int i = 0; i < 6; i++) mac->bytes[i] = e1000_netif.hwaddr[i]; + for (int i = 0; i < 6; i++) mac->bytes[i] = nic_netif.hwaddr[i]; return 0; } int network_get_ipv4_address(ipv4_address_t* ip) { if (!lwip_initialized) return -1; - u32_t addr = ip4_addr_get_u32(netif_ip4_addr(&e1000_netif)); + u32_t addr = ip4_addr_get_u32(netif_ip4_addr(&nic_netif)); ip->bytes[0] = (addr >> 0) & 0xFF; ip->bytes[1] = (addr >> 8) & 0xFF; ip->bytes[2] = (addr >> 16) & 0xFF; @@ -131,13 +127,13 @@ int network_set_ipv4_address(const ipv4_address_t* ip) { if (!lwip_initialized) return -1; ip4_addr_t ipaddr; IP4_ADDR(&ipaddr, ip->bytes[0], ip->bytes[1], ip->bytes[2], ip->bytes[3]); - netif_set_ipaddr(&e1000_netif, &ipaddr); + netif_set_ipaddr(&nic_netif, &ipaddr); return 0; } static volatile int network_processing = 0; static void network_poll_internal(void) { - e1000_netif_poll(&e1000_netif); + nic_netif_poll(&nic_netif); sys_check_timeouts(); } @@ -174,14 +170,14 @@ int network_dhcp_acquire(void) { } network_processing = 1; serial_write("[DHCP] Starting...\n"); - dhcp_start(&e1000_netif); + dhcp_start(&nic_netif); uint32_t start = sys_now(); asm volatile("sti"); int loops = 0; while (sys_now() - start < 10000) { // 10 second timeout network_poll_internal(); - if (dhcp_supplied_address(&e1000_netif)) { + if (dhcp_supplied_address(&nic_netif)) { asm volatile("cli"); serial_write("[DHCP] Bound!\n"); network_processing = 0; @@ -405,7 +401,7 @@ int network_set_dns_server(const ipv4_address_t *ip) { int network_get_gateway_ip(ipv4_address_t *ip) { if (!lwip_initialized) return -1; - u32_t addr = ip4_addr_get_u32(netif_ip4_gw(&e1000_netif)); + u32_t addr = ip4_addr_get_u32(netif_ip4_gw(&nic_netif)); ip->bytes[0] = (addr >> 0) & 0xFF; ip->bytes[1] = (addr >> 8) & 0xFF; ip->bytes[2] = (addr >> 16) & 0xFF; @@ -424,10 +420,10 @@ int network_get_dns_ip(ipv4_address_t *ip) { } int network_is_initialized(void) { return lwip_initialized; } -int network_has_ip(void) { return lwip_initialized && !ip4_addr_isany_val(*netif_ip4_addr(&e1000_netif)); } +int network_has_ip(void) { return lwip_initialized && !ip4_addr_isany_val(*netif_ip4_addr(&nic_netif)); } -int network_send_frame(const void* data, size_t length) { return e1000_send_packet(data, length); } -int network_receive_frame(void* buffer, size_t buffer_size) { return e1000_receive_packet(buffer, buffer_size); } +int network_send_frame(const void* data, size_t length) { return nic_send_packet(data, length); } +int network_receive_frame(void* buffer, size_t buffer_size) { return nic_receive_packet(buffer, buffer_size); } static u16_t icmp_cksum(void *data, int len) { u32_t sum = 0; diff --git a/src/kernel/network.h b/src/kernel/network.h index 3e1c15a..7168226 100644 --- a/src/kernel/network.h +++ b/src/kernel/network.h @@ -6,7 +6,7 @@ #include #include -#include "e1000.h" +#include "nic.h" #define ETH_FRAME_MAX_SIZE 1518 #define ETH_HEADER_SIZE 14 diff --git a/src/kernel/nic.c b/src/kernel/nic.c new file mode 100644 index 0000000..3b49039 --- /dev/null +++ b/src/kernel/nic.c @@ -0,0 +1,123 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#include "nic.h" +#include "pci.h" +#include "kutils.h" + +// Forward declarations for driver inits +extern int e1000_init(pci_device_t* pci_dev); +extern int rtl8139_init(pci_device_t* pci_dev); +extern int virtio_net_init(pci_device_t* pci_dev); + +extern int e1000_send_packet(const void* data, size_t length); +extern int e1000_receive_packet(void* buffer, size_t buffer_size); +extern int e1000_get_mac(uint8_t* mac_out); + +extern int rtl8139_send_packet(const void* data, size_t length); +extern int rtl8139_receive_packet(void* buffer, size_t buffer_size); +extern int rtl8139_get_mac(uint8_t* mac_out); + +extern int virtio_net_send_packet(const void* data, size_t length); +extern int virtio_net_receive_packet(void* buffer, size_t buffer_size); +extern int virtio_net_get_mac(uint8_t* mac_out); + +static nic_driver_t active_nic_driver = {0}; +static int nic_initialized = 0; + +static int register_e1000(pci_device_t* dev) { + if (e1000_init(dev) == 0) { + active_nic_driver.name = "e1000"; + active_nic_driver.init = e1000_init; + active_nic_driver.send_packet = e1000_send_packet; + active_nic_driver.receive_packet = e1000_receive_packet; + active_nic_driver.get_mac_address = e1000_get_mac; + return 0; + } + return -1; +} + +static int register_rtl8139(pci_device_t* dev) { + if (rtl8139_init(dev) == 0) { + active_nic_driver.name = "rtl8139"; + active_nic_driver.init = rtl8139_init; + active_nic_driver.send_packet = rtl8139_send_packet; + active_nic_driver.receive_packet = rtl8139_receive_packet; + active_nic_driver.get_mac_address = rtl8139_get_mac; + return 0; + } + return -1; +} + +static int register_virtio_net(pci_device_t* dev) { + if (virtio_net_init(dev) == 0) { + active_nic_driver.name = "virtio-net"; + active_nic_driver.init = virtio_net_init; + active_nic_driver.send_packet = virtio_net_send_packet; + active_nic_driver.receive_packet = virtio_net_receive_packet; + active_nic_driver.get_mac_address = virtio_net_get_mac; + return 0; + } + return -1; +} + +int nic_init(void) { + if (nic_initialized) return 0; + + pci_device_t pci_dev; + + // Try finding RTL8139 (Vendor: 0x10EC, Device: 0x8139) + if (pci_find_device(0x10EC, 0x8139, &pci_dev)) { + if (register_rtl8139(&pci_dev) == 0) { + nic_initialized = 1; + return 0; + } + } + + // Try finding Virtio-Net (Vendor: 0x1AF4, Device: 0x1000 for legacy or 0x1041 for modern) + // Here we mainly support legacy VirtIO Network (0x1000) + if (pci_find_device(0x1AF4, 0x1000, &pci_dev)) { + if (register_virtio_net(&pci_dev) == 0) { + nic_initialized = 1; + return 0; + } + } + + // Modern VirtIO NET is 0x1041 + if (pci_find_device(0x1AF4, 0x1041, &pci_dev)) { + if (register_virtio_net(&pci_dev) == 0) { + nic_initialized = 1; + return 0; + } + } + + // Try finding E1000 (Vendor: 0x8086, Device: 0x100E) + if (pci_find_device(0x8086, 0x100E, &pci_dev)) { + if (register_e1000(&pci_dev) == 0) { + nic_initialized = 1; + return 0; + } + } + + return -1; // No supported NIC found +} + +nic_driver_t* nic_get_driver(void) { + if (!nic_initialized) return NULL; + return &active_nic_driver; +} + +int nic_send_packet(const void* data, size_t length) { + if (!nic_initialized || !active_nic_driver.send_packet) return -1; + return active_nic_driver.send_packet(data, length); +} + +int nic_receive_packet(void* buffer, size_t buffer_size) { + if (!nic_initialized || !active_nic_driver.receive_packet) return 0; + return active_nic_driver.receive_packet(buffer, buffer_size); +} + +int nic_get_mac_address(uint8_t* mac_out) { + if (!nic_initialized || !active_nic_driver.get_mac_address) return -1; + return active_nic_driver.get_mac_address(mac_out); +} diff --git a/src/kernel/nic.h b/src/kernel/nic.h new file mode 100644 index 0000000..b756202 --- /dev/null +++ b/src/kernel/nic.h @@ -0,0 +1,25 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#ifndef NIC_H +#define NIC_H + +#include +#include +#include "pci.h" + +typedef struct nic_driver { + const char* name; + int (*init)(pci_device_t* pci_dev); + int (*send_packet)(const void* data, size_t length); + int (*receive_packet)(void* buffer, size_t buffer_size); + int (*get_mac_address)(uint8_t* mac_out); +} nic_driver_t; + +int nic_init(void); +nic_driver_t* nic_get_driver(void); +int nic_send_packet(const void* data, size_t length); +int nic_receive_packet(void* buffer, size_t buffer_size); +int nic_get_mac_address(uint8_t* mac_out); + +#endif diff --git a/src/kernel/e1000_netif.c b/src/kernel/nic_netif.c similarity index 63% rename from src/kernel/e1000_netif.c rename to src/kernel/nic_netif.c index d0a1cdf..d242631 100644 --- a/src/kernel/e1000_netif.c +++ b/src/kernel/nic_netif.c @@ -7,28 +7,24 @@ #include "lwip/ethip6.h" #include "lwip/etharp.h" #include "netif/etharp.h" -#include "e1000.h" +#include "nic.h" #include "network.h" #include "kutils.h" #define IFNAME0 'e' #define IFNAME1 'n' -struct e1000_netif_state { - // Any extra state if needed -}; - -static err_t e1000_low_level_output(struct netif *netif, struct pbuf *p) { +static err_t nic_low_level_output(struct netif *netif, struct pbuf *p) { (void)netif; if (p->next == NULL) { - if (e1000_send_packet(p->payload, p->len) != 0) { + if (nic_send_packet(p->payload, p->len) != 0) { return ERR_IF; } } else { u8_t buffer[2048]; u16_t copied = pbuf_copy_partial(p, buffer, 2048, 0); - if (e1000_send_packet(buffer, copied) != 0) { + if (nic_send_packet(buffer, copied) != 0) { return ERR_IF; } } @@ -37,11 +33,11 @@ static err_t e1000_low_level_output(struct netif *netif, struct pbuf *p) { return ERR_OK; } -static void e1000_low_level_input(struct netif *netif) { +static void nic_low_level_input(struct netif *netif) { u8_t buffer[2048]; int len; - while ((len = e1000_receive_packet(buffer, sizeof(buffer))) > 0) { + while ((len = nic_receive_packet(buffer, sizeof(buffer))) > 0) { struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); if (p != NULL) { @@ -55,19 +51,17 @@ static void e1000_low_level_input(struct netif *netif) { } } -err_t e1000_netif_init(struct netif *netif) { +err_t nic_netif_init(struct netif *netif) { netif->name[0] = IFNAME0; netif->name[1] = IFNAME1; netif->output = etharp_output; - netif->linkoutput = e1000_low_level_output; + netif->linkoutput = nic_low_level_output; - e1000_device_t* dev = e1000_get_device(); + nic_driver_t* dev = nic_get_driver(); if (!dev) return ERR_IF; netif->hwaddr_len = 6; - for (int i = 0; i < 6; i++) { - netif->hwaddr[i] = dev->mac_address.bytes[i]; - } + nic_get_mac_address(netif->hwaddr); netif->mtu = 1500; netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; @@ -78,6 +72,6 @@ err_t e1000_netif_init(struct netif *netif) { return ERR_OK; } -void e1000_netif_poll(struct netif *netif) { - e1000_low_level_input(netif); +void nic_netif_poll(struct netif *netif) { + nic_low_level_input(netif); } diff --git a/src/kernel/nic_netif.h b/src/kernel/nic_netif.h new file mode 100644 index 0000000..a8c8045 --- /dev/null +++ b/src/kernel/nic_netif.h @@ -0,0 +1,9 @@ +#ifndef NIC_NETIF_H +#define NIC_NETIF_H + +#include "lwip/netif.h" + +err_t nic_netif_init(struct netif *netif); +void nic_netif_poll(struct netif *netif); + +#endif diff --git a/src/kernel/rtl8139.c b/src/kernel/rtl8139.c new file mode 100644 index 0000000..e6e9398 --- /dev/null +++ b/src/kernel/rtl8139.c @@ -0,0 +1,186 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#include "rtl8139.h" +#include "io.h" +#include "kutils.h" +#include "platform.h" + +#define RTL8139_MAC_0 0x00 +#define RTL8139_TSD0 0x10 +#define RTL8139_TSAD0 0x20 +#define RTL8139_RBSTART 0x30 +#define RTL8139_CR 0x37 +#define RTL8139_CAPR 0x38 +#define RTL8139_CBR 0x3A +#define RTL8139_IMR 0x3C +#define RTL8139_ISR 0x3E +#define RTL8139_TCR 0x40 +#define RTL8139_RCR 0x44 +#define RTL8139_CONFIG_1 0x52 + +#define RTL8139_RCR_AAP (1 << 0) // Accept All Packets +#define RTL8139_RCR_APM (1 << 1) // Accept Physical Match Packets +#define RTL8139_RCR_AM (1 << 2) // Accept Multicast Packets +#define RTL8139_RCR_AB (1 << 3) // Accept Broadcast Packets +#define RTL8139_RCR_WRAP (1 << 7) // WRAP + +#define RTL8139_CR_TE (1 << 2) // Transmitter Enable +#define RTL8139_CR_RE (1 << 3) // Receiver Enable +#define RTL8139_CR_RST (1 << 4) // Reset + +static int rtl8139_initialized = 0; +static uint64_t mmio_base_addr = 0; +static uint8_t mac_addr[6]; + +// Receive buffer must be 8K + 16 bytes + 1.5K +// Let's use 32K buffer for safety, which is standard. +static uint8_t rx_buffer[32768 + 16 + 1500] __attribute__((aligned(4096))); +static uint16_t rx_ptr = 0; // Current read position + +// Transmit buffers (4 descriptors in RTL8139) +static uint8_t tx_buffers[4][4096] __attribute__((aligned(4096))); +static uint8_t tx_curr = 0; + +static inline uint8_t rtl8139_inb(uint16_t offset) { return *(volatile uint8_t*)(uintptr_t)(mmio_base_addr + offset); } +static inline uint16_t rtl8139_inw(uint16_t offset) { return *(volatile uint16_t*)(uintptr_t)(mmio_base_addr + offset); } +static inline uint32_t rtl8139_inl(uint16_t offset) { return *(volatile uint32_t*)(uintptr_t)(mmio_base_addr + offset); } + +static inline void rtl8139_outb(uint16_t offset, uint8_t value) { *(volatile uint8_t*)(uintptr_t)(mmio_base_addr + offset) = value; } +static inline void rtl8139_outw(uint16_t offset, uint16_t value) { *(volatile uint16_t*)(uintptr_t)(mmio_base_addr + offset) = value; } +static inline void rtl8139_outl(uint16_t offset, uint32_t value) { *(volatile uint32_t*)(uintptr_t)(mmio_base_addr + offset) = value; } + +int rtl8139_init(pci_device_t* pci_dev) { + if (rtl8139_initialized) return 0; + + // Enable bus mastering + uint32_t command = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x04); + pci_write_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x04, command | (1 << 2) | (1 << 1)); + + uint32_t bar1 = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x14); // BAR1: MMIO + if (bar1 == 0 || bar1 == 0xFFFFFFFF) return -1; + if (bar1 & 1) return -1; // Should not be I/O space + + mmio_base_addr = p2v(bar1 & ~0xF); + + extern void serial_write(const char *str); + serial_write("[RTL8139] MMIO Base: 0x"); + char hex_buf[32]; k_itoa_hex(mmio_base_addr, hex_buf); serial_write(hex_buf); serial_write("\n"); + + // Power on (CONFIG1) + rtl8139_outb(RTL8139_CONFIG_1, 0x00); + + // Software Reset + rtl8139_outb(RTL8139_CR, RTL8139_CR_RST); + while (rtl8139_inb(RTL8139_CR) & RTL8139_CR_RST) { + // Wait + } + + // Read MAC + uint32_t mac_low = rtl8139_inl(RTL8139_MAC_0); + uint32_t mac_high = rtl8139_inw(RTL8139_MAC_0 + 4); + mac_addr[0] = (mac_low >> 0) & 0xFF; + mac_addr[1] = (mac_low >> 8) & 0xFF; + mac_addr[2] = (mac_low >> 16) & 0xFF; + mac_addr[3] = (mac_low >> 24) & 0xFF; + mac_addr[4] = (mac_high >> 0) & 0xFF; + mac_addr[5] = (mac_high >> 8) & 0xFF; + + serial_write("[RTL8139] MAC: "); + for(int i=0; i<6; i++) { + char buf[4]; + k_itoa_hex(mac_addr[i], buf); + serial_write(buf); + if(i<5) serial_write(":"); + } + serial_write("\n"); + + // Init RX buffer + uint32_t rx_phys = v2p((uint64_t)(uintptr_t)rx_buffer); + rtl8139_outl(RTL8139_RBSTART, rx_phys); + + // Set IMR / ISR + rtl8139_outw(RTL8139_IMR, 0x0005); // TOK and ROK + + // Set RCR (Receive Configuration Register) + // Accept Broadcast/Multicast/Physical Match + WRAP + rtl8139_outl(RTL8139_RCR, RTL8139_RCR_AB | RTL8139_RCR_AM | RTL8139_RCR_APM | RTL8139_RCR_WRAP | (3 << 11)); // 32k rx buffer + + // Enable Transmitter and Receiver + rtl8139_outb(RTL8139_CR, RTL8139_CR_RE | RTL8139_CR_TE); + + // Config TCR + rtl8139_outl(RTL8139_TCR, (0x03 << 24)); // IFG + + rtl8139_initialized = 1; + return 0; +} + +int rtl8139_send_packet(const void* data, size_t length) { + if (!rtl8139_initialized) return -1; + if (length > 1792) return -1; + + // Use current tx buffer + uint8_t* tx_buf = tx_buffers[tx_curr]; + uint8_t* src = (uint8_t*)data; + for (size_t i = 0; i < length; i++) tx_buf[i] = src[i]; + + uint32_t phys = v2p((uint64_t)(uintptr_t)tx_buf); + rtl8139_outl(RTL8139_TSAD0 + (tx_curr * 4), phys); + + // Status is length | clear bit 13 to send + rtl8139_outl(RTL8139_TSD0 + (tx_curr * 4), length); + + tx_curr = (tx_curr + 1) % 4; + return 0; +} + +int rtl8139_receive_packet(void* buffer, size_t buffer_size) { + if (!rtl8139_initialized) return 0; + + uint8_t cmd = rtl8139_inb(RTL8139_CR); + if ((cmd & 1) == 1) { // Buffer empty + return 0; + } + + uint16_t* rx_head = (uint16_t*)(rx_buffer + rx_ptr); + uint16_t rx_status = rx_head[0]; + uint16_t rx_length = rx_head[1]; // includes 4 bytes CRC at end + + if (rx_status & (1 << 5)) { // Bad packet + // Bad packet received. We need to skip it or reset. + } + + if (rx_length > 0 && rx_length <= buffer_size + 4) { + uint8_t* pkt = (uint8_t*)(rx_head) + 4; + uint16_t net_len = rx_length - 4; // Strip CRC + + uint8_t* dest = (uint8_t*)buffer; + for (int i = 0; i < net_len; i++) { + dest[i] = pkt[i]; + } + + // Update rx_ptr + rx_ptr = (rx_ptr + rx_length + 4 + 3) & ~3; // Align up to 4 bytes + if (rx_ptr > 32768) { + rx_ptr -= 32768; // Wrap around + } + + rtl8139_outw(RTL8139_CAPR, rx_ptr - 16); + return net_len; + } + + // Default error handling, skip it + rx_ptr = (rx_ptr + rx_length + 4 + 3) & ~3; + if (rx_ptr > 32768) rx_ptr -= 32768; + rtl8139_outw(RTL8139_CAPR, rx_ptr - 16); + return 0; +} + +int rtl8139_get_mac(uint8_t* mac_out) { + if (!rtl8139_initialized) return -1; + for (int i = 0; i < 6; i++) { + mac_out[i] = mac_addr[i]; + } + return 0; +} diff --git a/src/kernel/rtl8139.h b/src/kernel/rtl8139.h new file mode 100644 index 0000000..951a520 --- /dev/null +++ b/src/kernel/rtl8139.h @@ -0,0 +1,16 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#ifndef RTL8139_H +#define RTL8139_H + +#include +#include +#include "pci.h" + +int rtl8139_init(pci_device_t* pci_dev); +int rtl8139_send_packet(const void* data, size_t length); +int rtl8139_receive_packet(void* buffer, size_t buffer_size); +int rtl8139_get_mac(uint8_t* mac_out); + +#endif diff --git a/src/kernel/virtio_net.c b/src/kernel/virtio_net.c new file mode 100644 index 0000000..fb948b2 --- /dev/null +++ b/src/kernel/virtio_net.c @@ -0,0 +1,258 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#include "virtio_net.h" +#include "io.h" +#include "kutils.h" +#include "platform.h" + +#define VIRTIO_PCI_HOST_FEATURES 0x00 +#define VIRTIO_PCI_GUEST_FEATURES 0x04 +#define VIRTIO_PCI_QUEUE_PFN 0x08 +#define VIRTIO_PCI_QUEUE_SIZE 0x0C +#define VIRTIO_PCI_QUEUE_SEL 0x0E +#define VIRTIO_PCI_QUEUE_NOTIFY 0x10 +#define VIRTIO_PCI_STATUS 0x12 +#define VIRTIO_PCI_ISR 0x13 +#define VIRTIO_PCI_CONFIG 0x14 + +#define VRING_DESC_F_NEXT 1 +#define VRING_DESC_F_WRITE 2 + +struct vq_desc { + uint64_t addr; + uint32_t len; + uint16_t flags; + uint16_t next; +} __attribute__((packed)); + +struct vq_avail { + uint16_t flags; + uint16_t idx; + uint16_t ring[256]; +} __attribute__((packed)); + +struct vq_used_elem { + uint32_t id; + uint32_t len; +} __attribute__((packed)); + +struct vq_used { + uint16_t flags; + uint16_t idx; + struct vq_used_elem ring[256]; +} __attribute__((packed)); + +struct virtqueue { + struct vq_desc* desc; + struct vq_avail* avail; + struct vq_used* used; + uint16_t q_size; + uint16_t last_used_idx; + uint16_t last_avail_idx; // Software tracking of idx +}; + +// Virtio Network header (typically 12 bytes if VIRTIO_NET_F_MRG_RXBUF is NOT set, 10 bytes legacy without MRG) +struct virtio_net_hdr { + uint8_t flags; + uint8_t gso_type; + uint16_t hdr_len; + uint16_t gso_size; + uint16_t csum_start; + uint16_t csum_offset; + // uint16_t num_buffers; // if MRG_RXBUF enabled +} __attribute__((packed)); + +static uint16_t io_base = 0; +static int virtio_initialized = 0; +static uint8_t mac_addr[6]; + +static struct virtqueue rx_vq; +static struct virtqueue tx_vq; + +// Memory buffers for queues +static uint8_t rx_ring_mem[16384] __attribute__((aligned(4096))); +static uint8_t tx_ring_mem[16384] __attribute__((aligned(4096))); + +static uint8_t rx_buffers[256][2048]; +static struct virtio_net_hdr tx_hdr[256]; +static uint8_t tx_buffers[256][2048]; + +static void virtqueue_init(struct virtqueue* vq, uint8_t* mem, uint16_t qsize) { + vq->q_size = qsize; + vq->last_used_idx = 0; + vq->last_avail_idx = 0; + + vq->desc = (struct vq_desc*)mem; + vq->avail = (struct vq_avail*)(mem + qsize * sizeof(struct vq_desc)); + + // used ring is aligned to 4096 after avail + uintptr_t avail_end = (uintptr_t)vq->avail + sizeof(struct vq_avail) + sizeof(uint16_t); // avail_event + uintptr_t used_start = (avail_end + 4095) & ~4095; + vq->used = (struct vq_used*)used_start; + + k_memset(mem, 0, 16384); +} + +int virtio_net_init(pci_device_t* pci_dev) { + if (virtio_initialized) return 0; + + uint32_t bar0 = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x10); + if (!(bar0 & 1)) { + // We only support legacy I/O virtio + return -1; + } + + // Enable Bus Master + I/O Space + uint32_t command = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x04); + pci_write_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x04, command | (1 << 2) | (1 << 0)); + + io_base = bar0 & ~3; + + extern void serial_write(const char *str); + serial_write("[VIRTIO-NET] I/O Base: 0x"); + char hex_buf[32]; k_itoa_hex(io_base, hex_buf); serial_write(hex_buf); serial_write("\n"); + + // Reset device + outb(io_base + VIRTIO_PCI_STATUS, 0); + + // Acknowledge + Driver + outb(io_base + VIRTIO_PCI_STATUS, 1 | 2); + + // Read features (negotiate MAC) + uint32_t features = inl(io_base + VIRTIO_PCI_HOST_FEATURES); + outl(io_base + VIRTIO_PCI_GUEST_FEATURES, features & 0x01000020); // MAC + STATUS + + // Setup RX Virtqueue (Index 0) + outw(io_base + VIRTIO_PCI_QUEUE_SEL, 0); + uint16_t rx_qsize = inw(io_base + VIRTIO_PCI_QUEUE_SIZE); + if (rx_qsize == 0) return -1; + if (rx_qsize > 256) rx_qsize = 256; + virtqueue_init(&rx_vq, rx_ring_mem, rx_qsize); + outl(io_base + VIRTIO_PCI_QUEUE_PFN, v2p((uint64_t)(uintptr_t)rx_ring_mem) / 4096); + + // Pre-fill RX ring with buffers + for (int i = 0; i < rx_qsize; i++) { + rx_vq.desc[i].addr = v2p((uint64_t)(uintptr_t)rx_buffers[i]); + rx_vq.desc[i].len = 2048; + rx_vq.desc[i].flags = VRING_DESC_F_WRITE; + rx_vq.desc[i].next = 0; + + rx_vq.avail->ring[i] = i; + } + rx_vq.avail->idx = rx_qsize; + rx_vq.last_avail_idx = rx_qsize; + + // Setup TX Virtqueue (Index 1) + outw(io_base + VIRTIO_PCI_QUEUE_SEL, 1); + uint16_t tx_qsize = inw(io_base + VIRTIO_PCI_QUEUE_SIZE); + if (tx_qsize > 256) tx_qsize = 256; + virtqueue_init(&tx_vq, tx_ring_mem, tx_qsize); + outl(io_base + VIRTIO_PCI_QUEUE_PFN, v2p((uint64_t)(uintptr_t)tx_ring_mem) / 4096); + + // Read MAC + for (int i = 0; i < 6; i++) { + mac_addr[i] = inb(io_base + VIRTIO_PCI_CONFIG + i); + } + + serial_write("[VIRTIO-NET] MAC: "); + for(int i=0; i<6; i++) { + char buf[4]; + k_itoa_hex(mac_addr[i], buf); + serial_write(buf); + if(i<5) serial_write(":"); + } + serial_write("\n"); + + // Device OK + outb(io_base + VIRTIO_PCI_STATUS, 1 | 2 | 4); + + // Kick RX Queue + outw(io_base + VIRTIO_PCI_QUEUE_NOTIFY, 0); + + virtio_initialized = 1; + return 0; +} + +int virtio_net_send_packet(const void* data, size_t length) { + if (!virtio_initialized) return -1; + if (length > 1514) return -1; + + uint16_t head = tx_vq.last_avail_idx % tx_vq.q_size; + uint16_t d_idx = head * 2; // We use 2 descriptors per packet (Header, Data) + + // Setup header + k_memset(&tx_hdr[head], 0, sizeof(struct virtio_net_hdr)); + tx_vq.desc[d_idx].addr = v2p((uint64_t)(uintptr_t)&tx_hdr[head]); + tx_vq.desc[d_idx].len = sizeof(struct virtio_net_hdr); + tx_vq.desc[d_idx].flags = VRING_DESC_F_NEXT; + tx_vq.desc[d_idx].next = d_idx + 1; + + // Setup data + uint8_t* src = (uint8_t*)data; + for (size_t i = 0; i < length; i++) tx_buffers[head][i] = src[i]; + + tx_vq.desc[d_idx + 1].addr = v2p((uint64_t)(uintptr_t)tx_buffers[head]); + tx_vq.desc[d_idx + 1].len = length; + tx_vq.desc[d_idx + 1].flags = 0; // Not NEXT, Not WRITE + + tx_vq.avail->ring[tx_vq.avail->idx % tx_vq.q_size] = d_idx; + + __asm__ __volatile__ ("mfence"); // Memory barrier + tx_vq.avail->idx++; + __asm__ __volatile__ ("mfence"); + + tx_vq.last_avail_idx++; + + // Notify device + outw(io_base + VIRTIO_PCI_QUEUE_NOTIFY, 1); + + return 0; +} + +int virtio_net_receive_packet(void* buffer, size_t buffer_size) { + if (!virtio_initialized) return 0; + + if (rx_vq.last_used_idx == rx_vq.used->idx) { + return 0; // No new packets + } + + uint16_t u_idx = rx_vq.last_used_idx % rx_vq.q_size; + uint32_t d_idx = rx_vq.used->ring[u_idx].id; + uint32_t len = rx_vq.used->ring[u_idx].len; + + // The length includes the virtio_net_hdr (10 bytes) + // We must strip it for the caller + uint16_t net_len = 0; + if (len > sizeof(struct virtio_net_hdr)) { + net_len = len - sizeof(struct virtio_net_hdr); + if (net_len > buffer_size) net_len = buffer_size; + + uint8_t* pkt = rx_buffers[d_idx] + sizeof(struct virtio_net_hdr); + uint8_t* dest = (uint8_t*)buffer; + + for (int i = 0; i < net_len; i++) { + dest[i] = pkt[i]; + } + } + + // Put buffer back directly in avail ring + rx_vq.avail->ring[rx_vq.avail->idx % rx_vq.q_size] = d_idx; + + __asm__ __volatile__ ("mfence"); + rx_vq.avail->idx++; + rx_vq.last_used_idx++; + + __asm__ __volatile__ ("mfence"); + outw(io_base + VIRTIO_PCI_QUEUE_NOTIFY, 0); + + return net_len; +} + +int virtio_net_get_mac(uint8_t* mac_out) { + if (!virtio_initialized) return -1; + for (int i = 0; i < 6; i++) { + mac_out[i] = mac_addr[i]; + } + return 0; +} diff --git a/src/kernel/virtio_net.h b/src/kernel/virtio_net.h new file mode 100644 index 0000000..3c04e08 --- /dev/null +++ b/src/kernel/virtio_net.h @@ -0,0 +1,16 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#ifndef VIRTIO_NET_H +#define VIRTIO_NET_H + +#include +#include +#include "pci.h" + +int virtio_net_init(pci_device_t* pci_dev); +int virtio_net_send_packet(const void* data, size_t length); +int virtio_net_receive_packet(void* buffer, size_t buffer_size); +int virtio_net_get_mac(uint8_t* mac_out); + +#endif