mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
Network Virtio and RTL8139 NIC support
This commit is contained in:
parent
95c465ca39
commit
b05b221c41
16 changed files with 674 additions and 52 deletions
2
Makefile
2
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 \
|
||||
|
|
|
|||
BIN
boredos.iso
BIN
boredos.iso
Binary file not shown.
BIN
build/cmd.o
BIN
build/cmd.o
Binary file not shown.
BIN
build/e1000.o
BIN
build/e1000.o
Binary file not shown.
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "e1000.h"
|
||||
#include "nic.h"
|
||||
|
||||
#define ETH_FRAME_MAX_SIZE 1518
|
||||
#define ETH_HEADER_SIZE 14
|
||||
|
|
|
|||
123
src/kernel/nic.c
Normal file
123
src/kernel/nic.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
25
src/kernel/nic.h
Normal file
25
src/kernel/nic.h
Normal 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
|
||||
|
|
@ -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);
|
||||
}
|
||||
9
src/kernel/nic_netif.h
Normal file
9
src/kernel/nic_netif.h
Normal 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
|
||||
186
src/kernel/rtl8139.c
Normal file
186
src/kernel/rtl8139.c
Normal 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/kernel/rtl8139.h
Normal file
16
src/kernel/rtl8139.h
Normal 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/kernel/virtio_net.c
Normal file
258
src/kernel/virtio_net.c
Normal 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/kernel/virtio_net.h
Normal file
16
src/kernel/virtio_net.h
Normal 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
|
||||
Loading…
Reference in a new issue