mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
src/kernel --> src/
This commit is contained in:
186
src/net/nic/e1000.c
Normal file
186
src/net/nic/e1000.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 <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
102
src/net/nic/e1000.h
Normal 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
147
src/net/nic/nic.c
Normal 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
25
src/net/nic/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
|
||||
77
src/net/nic/nic_netif.c
Normal file
77
src/net/nic/nic_netif.c
Normal 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
9
src/net/nic/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
|
||||
229
src/net/nic/rtl8111.c
Normal file
229
src/net/nic/rtl8111.c
Normal 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
16
src/net/nic/rtl8111.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 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
186
src/net/nic/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/net/nic/rtl8139.h
Normal file
16
src/net/nic/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/net/nic/virtio_net.c
Normal file
258
src/net/nic/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/net/nic/virtio_net.h
Normal file
16
src/net/nic/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
|
||||
Reference in New Issue
Block a user