src/kernel --> src/

This commit is contained in:
boreddevnl
2026-03-16 00:30:47 +01:00
parent 3da1496e4f
commit fc83d7941b
630 changed files with 2 additions and 2 deletions

186
src/net/nic/e1000.c Normal file
View File

@@ -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 <stdint.h>
#include <stddef.h>
#include "e1000.h"
#include "pci.h"
#include "io.h"
#include "platform.h"
#include "kutils.h"
static e1000_device_t e1000_dev;
static int e1000_initialized = 0;
static e1000_tx_desc_t tx_descriptors[E1000_TX_RING_SIZE] __attribute__((aligned(16)));
static e1000_rx_desc_t rx_descriptors[E1000_RX_RING_SIZE] __attribute__((aligned(16)));
static uint8_t tx_buffers[E1000_TX_RING_SIZE][2048] __attribute__((aligned(16)));
static uint8_t rx_buffers[E1000_RX_RING_SIZE][2048] __attribute__((aligned(16)));
static void* kmemcpy(void* dest, const void* src, size_t n) {
uint8_t* d = (uint8_t*)dest;
const uint8_t* s = (const uint8_t*)src;
for (size_t i = 0; i < n; i++) d[i] = s[i];
return dest;
}
int e1000_init(pci_device_t* pci_dev) {
if (e1000_initialized) return 0;
uint32_t bar0 = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x10);
if (bar0 == 0 || bar0 == 0xFFFFFFFF) return -1;
if (bar0 & 1) return -1;
uint64_t mmio_base_phys = (uint64_t)(bar0 & ~0xF);
volatile uint32_t* mmio_base = (volatile uint32_t*)(uintptr_t)p2v(mmio_base_phys);
e1000_dev.mmio_base = mmio_base;
e1000_dev.pci_dev = *pci_dev;
e1000_dev.initialized = 0;
extern void serial_write(const char *str);
serial_write("[E1000] MMIO Base (virt): 0x");
char hex_buf[32];
k_itoa_hex((uint64_t)mmio_base, hex_buf);
serial_write(hex_buf);
serial_write("\n");
uint32_t status_reg = e1000_read_reg(mmio_base, E1000_REG_STATUS);
serial_write("[E1000] Status: 0x");
k_itoa_hex(status_reg, hex_buf);
serial_write(hex_buf);
serial_write("\n");
uint32_t command = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x04);
command |= (1 << 2);
command |= (1 << 1);
pci_write_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x04, command);
uint32_t ctrl = e1000_read_reg(mmio_base, E1000_REG_CTRL);
e1000_write_reg(mmio_base, E1000_REG_CTRL, ctrl | E1000_CTRL_RST);
for (int i = 0; i < 100000; i++) {
ctrl = e1000_read_reg(mmio_base, E1000_REG_CTRL);
if (!(ctrl & E1000_CTRL_RST)) break;
}
uint32_t ral = e1000_read_reg(mmio_base, E1000_REG_RAL);
uint32_t rah = e1000_read_reg(mmio_base, E1000_REG_RAH);
uint16_t* mac_16 = (uint16_t*)&e1000_dev.mac_address;
mac_16[0] = (uint16_t)(ral & 0xFFFF);
mac_16[1] = (uint16_t)(ral >> 16);
mac_16[2] = (uint16_t)(rah & 0xFFFF);
serial_write("[E1000] MAC: ");
for(int i=0; i<6; i++) {
char buf[4];
k_itoa_hex(e1000_dev.mac_address.bytes[i], buf);
serial_write(buf);
if(i<5) serial_write(":");
}
serial_write("\n");
e1000_write_reg(mmio_base, E1000_REG_RAL, ral);
e1000_write_reg(mmio_base, E1000_REG_RAH, rah | (1u << 31));
e1000_dev.tx_descriptors = tx_descriptors;
e1000_dev.tx_head = 0;
e1000_dev.tx_tail = 0;
k_memset(tx_descriptors, 0, sizeof(tx_descriptors));
k_memset(tx_buffers, 0, sizeof(tx_buffers));
for (int i = 0; i < E1000_TX_RING_SIZE; i++) {
e1000_dev.tx_buffers[i] = tx_buffers[i];
e1000_dev.tx_descriptors[i].buffer_addr = v2p((uint64_t)(uintptr_t)tx_buffers[i]);
}
uint64_t tx_desc_phys = v2p((uint64_t)(uintptr_t)tx_descriptors);
e1000_write_reg(mmio_base, E1000_REG_TDBAL, (uint32_t)(tx_desc_phys & 0xFFFFFFFF));
e1000_write_reg(mmio_base, E1000_REG_TDBAH, (uint32_t)(tx_desc_phys >> 32));
e1000_write_reg(mmio_base, E1000_REG_TDLEN, E1000_TX_RING_SIZE * sizeof(e1000_tx_desc_t));
e1000_write_reg(mmio_base, E1000_REG_TDH, 0);
e1000_write_reg(mmio_base, E1000_REG_TDT, 0);
uint32_t tctl = E1000_TCTL_EN | E1000_TCTL_PSP | (0x10 << 4) | (0x40 << 12);
e1000_write_reg(mmio_base, E1000_REG_TCTL, tctl);
e1000_write_reg(mmio_base, E1000_REG_TIPG, 0x0060200A);
e1000_dev.rx_descriptors = rx_descriptors;
e1000_dev.rx_head = 0;
e1000_dev.rx_tail = E1000_RX_RING_SIZE - 1;
k_memset(rx_descriptors, 0, sizeof(rx_descriptors));
k_memset(rx_buffers, 0, sizeof(rx_buffers));
for (int i = 0; i < E1000_RX_RING_SIZE; i++) {
e1000_dev.rx_buffers[i] = rx_buffers[i];
e1000_dev.rx_descriptors[i].buffer_addr = v2p((uint64_t)(uintptr_t)rx_buffers[i]);
}
uint64_t rx_desc_phys = v2p((uint64_t)(uintptr_t)rx_descriptors);
e1000_write_reg(mmio_base, E1000_REG_RDBAL, (uint32_t)(rx_desc_phys & 0xFFFFFFFF));
e1000_write_reg(mmio_base, E1000_REG_RDBAH, (uint32_t)(rx_desc_phys >> 32));
e1000_write_reg(mmio_base, E1000_REG_RDLEN, E1000_RX_RING_SIZE * sizeof(e1000_rx_desc_t));
e1000_write_reg(mmio_base, E1000_REG_RDH, 0);
e1000_write_reg(mmio_base, E1000_REG_RDT, E1000_RX_RING_SIZE - 1);
uint32_t rctl = E1000_RCTL_EN | E1000_RCTL_SBP | E1000_RCTL_UPE | E1000_RCTL_MPE |
E1000_RCTL_LPE | E1000_RCTL_LBM_NONE | E1000_RCTL_RDMTS_HALF |
E1000_RCTL_MO_36 | E1000_RCTL_BAM | E1000_RCTL_BSIZE_2048 | E1000_RCTL_SECRC;
e1000_write_reg(mmio_base, E1000_REG_RCTL, rctl);
ctrl = e1000_read_reg(mmio_base, E1000_REG_CTRL);
e1000_write_reg(mmio_base, E1000_REG_CTRL, ctrl | E1000_CTRL_SLU);
e1000_dev.initialized = 1;
e1000_initialized = 1;
return 0;
}
e1000_device_t* e1000_get_device(void) {
if (!e1000_initialized) return NULL;
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;
volatile uint32_t* mmio = e1000_dev.mmio_base;
uint16_t tail = e1000_dev.tx_tail;
uint16_t next_tail = (tail + 1) % E1000_TX_RING_SIZE;
if (e1000_dev.tx_descriptors[tail].cmd != 0) {
for(int i=0; i<1000000; i++) {
if (e1000_dev.tx_descriptors[tail].status & 0x01) break;
__asm__ __volatile__("pause");
}
}
kmemcpy(e1000_dev.tx_buffers[tail], data, length);
e1000_dev.tx_descriptors[tail].length = (uint16_t)length;
e1000_dev.tx_descriptors[tail].cmd = 0x0B;
e1000_dev.tx_descriptors[tail].status = 0;
e1000_dev.tx_tail = next_tail;
e1000_write_reg(mmio, E1000_REG_TDT, e1000_dev.tx_tail);
return 0;
}
int e1000_receive_packet(void* buffer, size_t buffer_size) {
if (!e1000_initialized || !e1000_dev.initialized) return 0;
uint16_t next_idx = (e1000_dev.rx_tail + 1) % E1000_RX_RING_SIZE;
if (!(e1000_dev.rx_descriptors[next_idx].status & 1)) return 0;
uint16_t length = e1000_dev.rx_descriptors[next_idx].length;
// Do NOT subtract 4. SECRC strips the CRC and the length already reflects this.
if (length > buffer_size) length = (uint16_t)buffer_size;
kmemcpy(buffer, e1000_dev.rx_buffers[next_idx], length);
e1000_dev.rx_descriptors[next_idx].status = 0;
e1000_dev.rx_descriptors[next_idx].length = 0;
e1000_dev.rx_tail = next_idx;
e1000_write_reg(e1000_dev.mmio_base, E1000_REG_RDT, e1000_dev.rx_tail);
return (int)length;
}

102
src/net/nic/e1000.h Normal file
View File

@@ -0,0 +1,102 @@
// 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 E1000_H
#define E1000_H
#include <stdint.h>
#include <stddef.h>
#include "pci.h"
#define E1000_VENDOR_ID 0x8086
#define E1000_DEVICE_ID_82540EM 0x100E
#define E1000_REG_CTRL 0x0000
#define E1000_REG_STATUS 0x0008
#define E1000_REG_EERD 0x0014
#define E1000_REG_ICR 0x00C0
#define E1000_REG_IMS 0x00D0
#define E1000_REG_RCTL 0x0100
#define E1000_REG_TCTL 0x0400
#define E1000_REG_TIPG 0x0410
#define E1000_REG_RDBAL 0x2800
#define E1000_REG_RDBAH 0x2804
#define E1000_REG_RDLEN 0x2808
#define E1000_REG_RDH 0x2810
#define E1000_REG_RDT 0x2818
#define E1000_REG_TDBAL 0x3800
#define E1000_REG_TDBAH 0x3804
#define E1000_REG_TDLEN 0x3808
#define E1000_REG_TDH 0x3810
#define E1000_REG_TDT 0x3818
#define E1000_REG_RAL 0x5400
#define E1000_REG_RAH 0x5404
#define E1000_CTRL_RST (1 << 26)
#define E1000_CTRL_SLU (1 << 6)
#define E1000_RCTL_EN (1 << 1)
#define E1000_RCTL_SBP (1 << 2)
#define E1000_RCTL_UPE (1 << 3)
#define E1000_RCTL_MPE (1 << 4)
#define E1000_RCTL_LPE (1 << 5)
#define E1000_RCTL_LBM_NONE (0 << 6)
#define E1000_RCTL_RDMTS_HALF (0 << 8)
#define E1000_RCTL_MO_36 (0 << 12)
#define E1000_RCTL_BAM (1 << 15)
#define E1000_RCTL_BSIZE_2048 (0 << 16)
#define E1000_RCTL_SECRC (1 << 26)
#define E1000_TCTL_EN (1 << 1)
#define E1000_TCTL_PSP (1 << 3)
#define E1000_TCTL_CT (0xFF << 4)
#define E1000_TCTL_COLD (0x3FF << 12)
#define E1000_ICR_TXDW (1 << 0)
#define E1000_ICR_RXT0 (1 << 7)
#define E1000_TX_RING_SIZE 32
#define E1000_RX_RING_SIZE 32
typedef struct {
uint64_t buffer_addr;
uint16_t length;
uint8_t cso;
uint8_t cmd;
uint8_t status;
uint8_t css;
uint16_t special;
} __attribute__((packed)) e1000_tx_desc_t;
typedef struct {
uint64_t buffer_addr;
uint16_t length;
uint16_t checksum;
uint8_t status;
uint8_t errors;
uint16_t special;
} __attribute__((packed)) e1000_rx_desc_t;
typedef struct {
volatile uint32_t* mmio_base;
pci_device_t pci_dev;
int initialized;
struct { uint8_t bytes[6]; } mac_address;
e1000_tx_desc_t* tx_descriptors;
void* tx_buffers[E1000_TX_RING_SIZE];
uint16_t tx_head;
uint16_t tx_tail;
e1000_rx_desc_t* rx_descriptors;
void* rx_buffers[E1000_RX_RING_SIZE];
uint16_t rx_head;
uint16_t rx_tail;
} e1000_device_t;
int e1000_init(pci_device_t* pci_dev);
static inline uint32_t e1000_read_reg(volatile uint32_t* mmio_base, uint16_t offset) { return mmio_base[offset / 4]; }
static inline void e1000_write_reg(volatile uint32_t* mmio_base, uint16_t offset, uint32_t value) { mmio_base[offset / 4] = value; }
e1000_device_t* e1000_get_device(void);
int e1000_send_packet(const void* data, size_t length);
int e1000_receive_packet(void* buffer, size_t buffer_size);
#endif

147
src/net/nic/nic.c Normal file
View File

@@ -0,0 +1,147 @@
// 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"
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);
extern int rtl8111_init(pci_device_t* pci_dev);
extern int rtl8111_send_packet(const void* data, size_t length);
extern int rtl8111_receive_packet(void* buffer, size_t buffer_size);
extern int rtl8111_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;
}
static int register_rtl8111(pci_device_t* dev) {
if (rtl8111_init(dev) == 0) {
active_nic_driver.name = "rtl8111";
active_nic_driver.init = rtl8111_init;
active_nic_driver.send_packet = rtl8111_send_packet;
active_nic_driver.receive_packet = rtl8111_receive_packet;
active_nic_driver.get_mac_address = rtl8111_get_mac;
return 0;
}
return -1;
}
int nic_init(void) {
if (nic_initialized) return 0;
pci_device_t pci_dev;
if (pci_find_device(0x10EC, 0x8168, &pci_dev)) {
if (register_rtl8111(&pci_dev) == 0) {
nic_initialized = 1;
return 0;
}
}
if (pci_find_device(0x10EC, 0x8139, &pci_dev)) {
if (register_rtl8139(&pci_dev) == 0) {
nic_initialized = 1;
return 0;
}
}
if (pci_find_device(0x1AF4, 0x1000, &pci_dev)) {
if (register_virtio_net(&pci_dev) == 0) {
nic_initialized = 1;
return 0;
}
}
if (pci_find_device(0x1AF4, 0x1041, &pci_dev)) {
if (register_virtio_net(&pci_dev) == 0) {
nic_initialized = 1;
return 0;
}
}
if (pci_find_device(0x8086, 0x100E, &pci_dev)) {
if (register_e1000(&pci_dev) == 0) {
nic_initialized = 1;
return 0;
}
}
return -1;
}
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);
}
const char* nic_get_active_name(void) {
if (!nic_initialized || !active_nic_driver.name) return NULL;
return active_nic_driver.name;
}

25
src/net/nic/nic.h Normal file
View File

@@ -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 <stdint.h>
#include <stddef.h>
#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

77
src/net/nic/nic_netif.c Normal file
View File

@@ -0,0 +1,77 @@
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/ethip6.h"
#include "lwip/etharp.h"
#include "netif/etharp.h"
#include "nic.h"
#include "network.h"
#include "kutils.h"
#define IFNAME0 'e'
#define IFNAME1 'n'
static err_t nic_low_level_output(struct netif *netif, struct pbuf *p) {
(void)netif;
if (p->next == NULL) {
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 (nic_send_packet(buffer, copied) != 0) {
return ERR_IF;
}
}
LINK_STATS_INC(link.xmit);
return ERR_OK;
}
static void nic_low_level_input(struct netif *netif) {
u8_t buffer[2048];
int len;
while ((len = nic_receive_packet(buffer, sizeof(buffer))) > 0) {
struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL) {
pbuf_take(p, buffer, len);
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
} else {
LINK_STATS_INC(link.recv);
}
}
}
}
err_t nic_netif_init(struct netif *netif) {
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
netif->output = etharp_output;
netif->linkoutput = nic_low_level_output;
nic_driver_t* dev = nic_get_driver();
if (!dev) return ERR_IF;
netif->hwaddr_len = 6;
nic_get_mac_address(netif->hwaddr);
netif->mtu = 1500;
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
// Explicitly set link to up to trigger lwIP state changes
netif_set_link_up(netif);
return ERR_OK;
}
void nic_netif_poll(struct netif *netif) {
nic_low_level_input(netif);
}

9
src/net/nic/nic_netif.h Normal file
View File

@@ -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

229
src/net/nic/rtl8111.c Normal file
View File

@@ -0,0 +1,229 @@
// 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 "rtl8111.h"
#include "io.h"
#include "kutils.h"
#include "platform.h"
#define RTL8111_MAC0 0x00
#define RTL8111_TDSAR 0x20
#define RTL8111_CR 0x37
#define RTL8111_TPPOLL 0x38
#define RTL8111_IMR 0x3C
#define RTL8111_ISR 0x3E
#define RTL8111_TCR 0x40
#define RTL8111_RCR 0x44
#define RTL8111_MULINT 0x5C
#define RTL8111_RMS 0xDA
#define RTL8111_MTPS 0xEC
#define RTL8111_RDSAR 0xE4
#define RTL8111_CR_TE (1 << 2)
#define RTL8111_CR_RE (1 << 3)
#define RTL8111_CR_RST (1 << 4)
#define RTL8111_DESC_OWN (1u << 31)
#define RTL8111_DESC_EOR (1u << 30)
#define RTL8111_DESC_FS (1u << 29)
#define RTL8111_DESC_LS (1u << 28)
struct rtl8111_desc {
uint32_t opts1;
uint32_t opts2;
uint64_t buf_addr;
} __attribute__((packed, aligned(16)));
#define RTL8111_NUM_RX_DESC 128
#define RTL8111_NUM_TX_DESC 128
static int rtl8111_initialized = 0;
static uint64_t mmio_base_addr = 0;
static uint8_t mac_addr[6];
static struct rtl8111_desc rx_desc[RTL8111_NUM_RX_DESC] __attribute__((aligned(256)));
static struct rtl8111_desc tx_desc[RTL8111_NUM_TX_DESC] __attribute__((aligned(256)));
static uint8_t rx_buffers[RTL8111_NUM_RX_DESC][2048] __attribute__((aligned(8)));
static uint8_t tx_buffers[RTL8111_NUM_TX_DESC][2048] __attribute__((aligned(8)));
static uint16_t rx_idx = 0;
static uint16_t tx_idx = 0;
static inline uint8_t rtl8111_inb(uint16_t offset) { return *(volatile uint8_t*)(uintptr_t)(mmio_base_addr + offset); }
static inline uint16_t rtl8111_inw(uint16_t offset) { return *(volatile uint16_t*)(uintptr_t)(mmio_base_addr + offset); }
static inline uint32_t rtl8111_inl(uint16_t offset) { return *(volatile uint32_t*)(uintptr_t)(mmio_base_addr + offset); }
static inline void rtl8111_outb(uint16_t offset, uint8_t value) { *(volatile uint8_t*)(uintptr_t)(mmio_base_addr + offset) = value; }
static inline void rtl8111_outw(uint16_t offset, uint16_t value) { *(volatile uint16_t*)(uintptr_t)(mmio_base_addr + offset) = value; }
static inline void rtl8111_outl(uint16_t offset, uint32_t value) { *(volatile uint32_t*)(uintptr_t)(mmio_base_addr + offset) = value; }
int rtl8111_init(pci_device_t* pci_dev) {
if (rtl8111_initialized) return 0;
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 bar2 = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, 0x18);
uint64_t mmio_phys = 0;
for (int bar_off = 0x10; bar_off <= 0x24; bar_off += 4) {
uint32_t bar = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, bar_off);
if (bar != 0 && bar != 0xFFFFFFFF && !(bar & 1)) {
mmio_phys = (bar & ~0xF);
if ((bar & 0x6) == 0x4) {
uint32_t bar_upper = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->function, bar_off + 4);
mmio_phys |= ((uint64_t)bar_upper << 32);
}
break;
}
}
if (mmio_phys == 0) return -1;
mmio_base_addr = p2v(mmio_phys);
extern void serial_write(const char *str);
serial_write("[RTL8111] MMIO Base: 0x");
char hex_buf[32]; k_itoa_hex(mmio_base_addr, hex_buf); serial_write(hex_buf); serial_write("\n");
rtl8111_outb(RTL8111_CR, RTL8111_CR_RST);
for (int i = 0; i < 100000; i++) {
if (!(rtl8111_inb(RTL8111_CR) & RTL8111_CR_RST)) break;
}
uint32_t mac_low = rtl8111_inl(RTL8111_MAC0);
uint32_t mac_high = rtl8111_inl(RTL8111_MAC0 + 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("[RTL8111] 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");
rtl8111_outb(0x50, 0xC0);
k_memset(rx_desc, 0, sizeof(rx_desc));
for (int i = 0; i < RTL8111_NUM_RX_DESC; i++) {
uint64_t buf_phys = v2p((uint64_t)(uintptr_t)rx_buffers[i]);
rx_desc[i].buf_addr = buf_phys;
uint32_t cmd = 2048 | RTL8111_DESC_OWN;
if (i == RTL8111_NUM_RX_DESC - 1) {
cmd |= RTL8111_DESC_EOR;
}
rx_desc[i].opts1 = cmd;
rx_desc[i].opts2 = 0;
}
uint64_t rx_ring_phys = v2p((uint64_t)(uintptr_t)rx_desc);
rtl8111_outl(RTL8111_RDSAR, (uint32_t)rx_ring_phys);
rtl8111_outl(RTL8111_RDSAR + 4, (uint32_t)(rx_ring_phys >> 32));
k_memset(tx_desc, 0, sizeof(tx_desc));
uint64_t tx_ring_phys = v2p((uint64_t)(uintptr_t)tx_desc);
rtl8111_outl(RTL8111_TDSAR, (uint32_t)tx_ring_phys);
rtl8111_outl(RTL8111_TDSAR + 4, (uint32_t)(tx_ring_phys >> 32));
rtl8111_outl(RTL8111_RCR, 0x0F | (0x07 << 8) | (0x07 << 13));
rtl8111_outl(RTL8111_TCR, (0x03 << 24));
rtl8111_outw(RTL8111_RMS, 2048);
rtl8111_outb(RTL8111_MTPS, 0x3F);
rtl8111_outb(0x50, 0x00);
rtl8111_outb(RTL8111_CR, RTL8111_CR_RE | RTL8111_CR_TE);
rtl8111_outw(RTL8111_MULINT, 0);
rtl8111_outw(RTL8111_IMR, 0x0005);
rx_idx = 0;
tx_idx = 0;
rtl8111_initialized = 1;
return 0;
}
int rtl8111_send_packet(const void* data, size_t length) {
if (!rtl8111_initialized) return -1;
if (length > 2048) return -1;
struct rtl8111_desc* desc = &tx_desc[tx_idx];
if (desc->opts1 & RTL8111_DESC_OWN) {
return -1;
}
uint8_t* tx_buf = tx_buffers[tx_idx];
uint8_t* src = (uint8_t*)data;
for (size_t i = 0; i < length; i++) tx_buf[i] = src[i];
desc->buf_addr = v2p((uint64_t)(uintptr_t)tx_buf);
uint32_t cmd = length | RTL8111_DESC_OWN | RTL8111_DESC_FS | RTL8111_DESC_LS;
if (tx_idx == RTL8111_NUM_TX_DESC - 1) {
cmd |= RTL8111_DESC_EOR;
}
desc->opts2 = 0;
__asm__ __volatile__ ("mfence");
desc->opts1 = cmd;
rtl8111_outb(RTL8111_TPPOLL, 0x40);
tx_idx = (tx_idx + 1) % RTL8111_NUM_TX_DESC;
return 0;
}
int rtl8111_receive_packet(void* buffer, size_t buffer_size) {
if (!rtl8111_initialized) return 0;
uint16_t isr = rtl8111_inw(RTL8111_ISR);
if (isr) {
rtl8111_outw(RTL8111_ISR, isr);
}
struct rtl8111_desc* desc = &rx_desc[rx_idx];
if (desc->opts1 & RTL8111_DESC_OWN) {
return 0;
}
uint32_t len = desc->opts1 & 0x3FFF;
if (desc->opts1 & (1 << 21)) {
len = 0;
} else if (len > 0) {
if (len > 4) len -= 4;
if (len > buffer_size) len = buffer_size;
uint8_t* dest = (uint8_t*)buffer;
uint8_t* pkt = rx_buffers[rx_idx];
for (uint32_t i = 0; i < len; i++) {
dest[i] = pkt[i];
}
}
uint32_t cmd = 2048 | RTL8111_DESC_OWN;
if (rx_idx == RTL8111_NUM_RX_DESC - 1) {
cmd |= RTL8111_DESC_EOR;
}
desc->buf_addr = v2p((uint64_t)(uintptr_t)rx_buffers[rx_idx]);
desc->opts2 = 0;
__asm__ __volatile__ ("mfence");
desc->opts1 = cmd;
rx_idx = (rx_idx + 1) % RTL8111_NUM_RX_DESC;
return len;
}
int rtl8111_get_mac(uint8_t* mac_out) {
if (!rtl8111_initialized) return -1;
for (int i = 0; i < 6; i++) {
mac_out[i] = mac_addr[i];
}
return 0;
}

16
src/net/nic/rtl8111.h Normal file
View File

@@ -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 RTL8111_H
#define RTL8111_H
#include <stdint.h>
#include <stddef.h>
#include "pci.h"
int rtl8111_init(pci_device_t* pci_dev);
int rtl8111_send_packet(const void* data, size_t length);
int rtl8111_receive_packet(void* buffer, size_t buffer_size);
int rtl8111_get_mac(uint8_t* mac_out);
#endif

186
src/net/nic/rtl8139.c Normal file
View File

@@ -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;
}

16
src/net/nic/rtl8139.h Normal file
View File

@@ -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 <stdint.h>
#include <stddef.h>
#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

258
src/net/nic/virtio_net.c Normal file
View File

@@ -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;
}

16
src/net/nic/virtio_net.h Normal file
View File

@@ -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 <stdint.h>
#include <stddef.h>
#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