add CatchAllDebugBucket as a syscall for debugging, add a basic network card plus ethernet driver (wip)
This commit is contained in:
1 parent
bf34d63ea3
commit
0d7f71b693
22 files changed
+2248
-25
No files matched your search
Binary file not shown.
@@ -117,6 +117,14 @@ cl %CFLAGS% /I"%SRC%\usb" /c /Fo"obj\drivers\usb.obj" "%SRC%\drivers\usb\usb.c"
|
|||||||
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:"bin\usb.sys" /ENTRY:DriverEntry ^
|
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:"bin\usb.sys" /ENTRY:DriverEntry ^
|
||||||
bin\hal.lib bin\kernel.lib obj\libc.obj obj\drivers\usb.obj || exit /b 1
|
bin\hal.lib bin\kernel.lib obj\libc.obj obj\drivers\usb.obj || exit /b 1
|
||||||
|
|
||||||
|
cl %CFLAGS% /I"%SRC%\rtl816x" /c /Fo"obj\drivers\rtl816x.obj" "%SRC%\drivers\rtl816x\rtl816x.c" || exit /b 1
|
||||||
|
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:"bin\rtl816x.sys" /ENTRY:DriverEntry ^
|
||||||
|
bin\hal.lib bin\kernel.lib obj\libc.obj obj\drivers\rtl816x.obj || exit /b 1
|
||||||
|
|
||||||
|
cl %CFLAGS% /I"%SRC%\rtl8139" /c /Fo"obj\drivers\rtl8139.obj" "%SRC%\drivers\rtl8139\rtl8139.c" || exit /b 1
|
||||||
|
link /nologo /SUBSYSTEM:NATIVE /NODEFAULTLIB /DLL /OUT:"bin\rtl8139.sys" /ENTRY:DriverEntry ^
|
||||||
|
bin\hal.lib bin\kernel.lib obj\libc.obj obj\drivers\rtl8139.obj || exit /b 1
|
||||||
|
|
||||||
:: ---- Compile init ----
|
:: ---- Compile init ----
|
||||||
echo Compiling init...
|
echo Compiling init...
|
||||||
for %%f in (%SRC%\init\*.c) do (
|
for %%f in (%SRC%\init\*.c) do (
|
||||||
@@ -157,7 +165,6 @@ for %%f in (%SRC%\async_apc\*.c) do (
|
|||||||
link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^
|
link %LFLAGS% /SUBSYSTEM:NATIVE /ENTRY:main ^
|
||||||
/OUT:"bin\async_apc.exe" /PDB:"bin\async_apc.pdb" bin\syscall.lib obj\libc.obj obj\async_apc\*.obj || exit /b 1
|
/OUT:"bin\async_apc.exe" /PDB:"bin\async_apc.pdb" bin\syscall.lib obj\libc.obj obj\async_apc\*.obj || exit /b 1
|
||||||
|
|
||||||
|
|
||||||
:: ---- Compile async_cancel ----
|
:: ---- Compile async_cancel ----
|
||||||
echo Compiling async_cancel...
|
echo Compiling async_cancel...
|
||||||
for %%f in (%SRC%\async_cancel\*.c) do (
|
for %%f in (%SRC%\async_cancel\*.c) do (
|
||||||
@@ -224,6 +231,8 @@ copy /y "bin\ide.sys" "%DEST%\drivers\"
|
|||||||
copy /y "bin\ahci.sys" "%DEST%\drivers\"
|
copy /y "bin\ahci.sys" "%DEST%\drivers\"
|
||||||
copy /y "bin\fat32.sys" "%DEST%\drivers\"
|
copy /y "bin\fat32.sys" "%DEST%\drivers\"
|
||||||
copy /y "bin\usb.sys" "%DEST%\drivers\"
|
copy /y "bin\usb.sys" "%DEST%\drivers\"
|
||||||
|
copy /y "bin\rtl816x.sys" "%DEST%\drivers\"
|
||||||
|
copy /y "bin\rtl8139.sys" "%DEST%\drivers\"
|
||||||
copy /y "bin\init.exe" "%DEST%\system\"
|
copy /y "bin\init.exe" "%DEST%\system\"
|
||||||
copy /y "bin\shell_win32.exe" "%DEST%\apps\"
|
copy /y "bin\shell_win32.exe" "%DEST%\apps\"
|
||||||
copy /y "bin\shell_posix.exe" "%DEST%\apps\"
|
copy /y "bin\shell_posix.exe" "%DEST%\apps\"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
[boot]
|
[boot]
|
||||||
ide.sys
|
ide.sys
|
||||||
ahci.sys
|
ahci.sys
|
||||||
fat32.sys
|
fat32.sys
|
||||||
|
|
||||||
[runtime]
|
[runtime]
|
||||||
usb.sys
|
usb.sys
|
||||||
|
rtl8139.sys
|
||||||
@@ -0,0 +1,571 @@
|
|||||||
|
// rtl8139.c - Realtek RTL8139 / QEMU rtl8139 PCI fast Ethernet driver.
|
||||||
|
//
|
||||||
|
// Scope for this first version:
|
||||||
|
// * PCI ID 10ec:8139, legacy INTx interrupts.
|
||||||
|
// * MMIO preferred, I/O BAR fallback.
|
||||||
|
// * Classic RTL8139 register interface: one RX ring buffer and four TX slots.
|
||||||
|
// * No checksum offload, C+ mode, VLAN, multicast hash programming, or power mgmt.
|
||||||
|
//
|
||||||
|
// This is intentionally a separate mini-driver from rtl816x.sys. RTL8139 and
|
||||||
|
// RTL8111/8168 share a vendor but not a useful packet-DMA programming model.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
|
||||||
|
#include <hal_api.h>
|
||||||
|
#include <ke_status.h>
|
||||||
|
#include <ke_debug.h>
|
||||||
|
#include <ke_irq.h>
|
||||||
|
#include <ke_dpc.h>
|
||||||
|
#include <ke_spinlock.h>
|
||||||
|
#include <mm_phys.h>
|
||||||
|
#include <mm_virt.h>
|
||||||
|
#include <net_device.h>
|
||||||
|
|
||||||
|
#define RTL8139_VENDOR_REALTEK 0x10ECu
|
||||||
|
#define RTL8139_DEVICE_ID 0x8139u
|
||||||
|
|
||||||
|
#define PCI_REG_COMMAND 0x04u
|
||||||
|
#define PCI_REG_BAR0 0x10u
|
||||||
|
#define PCI_COMMAND_IO (1u << 0)
|
||||||
|
#define PCI_COMMAND_MEMORY (1u << 1)
|
||||||
|
#define PCI_COMMAND_MASTER (1u << 2)
|
||||||
|
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
|
||||||
|
|
||||||
|
#define RTL8139_REG_IDR0 0x00u
|
||||||
|
#define RTL8139_REG_MAR0 0x08u
|
||||||
|
#define RTL8139_REG_TSD0 0x10u
|
||||||
|
#define RTL8139_REG_TSAD0 0x20u
|
||||||
|
#define RTL8139_REG_RBSTART 0x30u
|
||||||
|
#define RTL8139_REG_CMD 0x37u
|
||||||
|
#define RTL8139_REG_CAPR 0x38u
|
||||||
|
#define RTL8139_REG_CBR 0x3Au
|
||||||
|
#define RTL8139_REG_IMR 0x3Cu
|
||||||
|
#define RTL8139_REG_ISR 0x3Eu
|
||||||
|
#define RTL8139_REG_TCR 0x40u
|
||||||
|
#define RTL8139_REG_RCR 0x44u
|
||||||
|
#define RTL8139_REG_CONFIG1 0x52u
|
||||||
|
|
||||||
|
#define RTL8139_CMD_BUFE 0x01u
|
||||||
|
#define RTL8139_CMD_TE 0x04u
|
||||||
|
#define RTL8139_CMD_RE 0x08u
|
||||||
|
#define RTL8139_CMD_RST 0x10u
|
||||||
|
|
||||||
|
#define RTL8139_ISR_ROK 0x0001u
|
||||||
|
#define RTL8139_ISR_RER 0x0002u
|
||||||
|
#define RTL8139_ISR_TOK 0x0004u
|
||||||
|
#define RTL8139_ISR_TER 0x0008u
|
||||||
|
#define RTL8139_ISR_RXOVW 0x0010u
|
||||||
|
#define RTL8139_ISR_PUN 0x0020u
|
||||||
|
#define RTL8139_ISR_FOVW 0x0040u
|
||||||
|
#define RTL8139_ISR_LENCHG 0x2000u
|
||||||
|
#define RTL8139_ISR_TIMEOUT 0x4000u
|
||||||
|
#define RTL8139_ISR_SYSERR 0x8000u
|
||||||
|
|
||||||
|
#define RTL8139_INTR_ENABLES (RTL8139_ISR_ROK | RTL8139_ISR_RER | RTL8139_ISR_TOK | \
|
||||||
|
RTL8139_ISR_TER | RTL8139_ISR_RXOVW | RTL8139_ISR_PUN | \
|
||||||
|
RTL8139_ISR_FOVW | RTL8139_ISR_LENCHG | RTL8139_ISR_SYSERR)
|
||||||
|
|
||||||
|
#define RTL8139_RCR_AAP (1u << 0)
|
||||||
|
#define RTL8139_RCR_APM (1u << 1)
|
||||||
|
#define RTL8139_RCR_AM (1u << 2)
|
||||||
|
#define RTL8139_RCR_AB (1u << 3)
|
||||||
|
#define RTL8139_RCR_AR (1u << 4)
|
||||||
|
#define RTL8139_RCR_AER (1u << 5)
|
||||||
|
#define RTL8139_RCR_WRAP (1u << 7)
|
||||||
|
#define RTL8139_RCR_MXDMA_UNLIMITED (7u << 8)
|
||||||
|
#define RTL8139_RCR_RBLEN_8K (0u << 11)
|
||||||
|
|
||||||
|
#define RTL8139_TCR_MXDMA_UNLIMITED (7u << 8)
|
||||||
|
#define RTL8139_TCR_IFG_NORMAL (3u << 24)
|
||||||
|
|
||||||
|
#define RTL8139_TSD_SIZE_MASK 0x00001FFFu
|
||||||
|
#define RTL8139_TSD_OWN (1u << 13)
|
||||||
|
#define RTL8139_TSD_TUN (1u << 14)
|
||||||
|
#define RTL8139_TSD_TOK (1u << 15)
|
||||||
|
#define RTL8139_TSD_TABT (1u << 30)
|
||||||
|
|
||||||
|
#define RTL8139_RX_OK 0x0001u
|
||||||
|
#define RTL8139_RX_BAD_BITS 0x003Eu // FAE | CRC | LONG | RUNT | ISE; MAR/PAM/BAR are valid match flags.
|
||||||
|
|
||||||
|
#define RTL8139_TX_SLOTS 4u
|
||||||
|
#define RTL8139_TX_BUF_SIZE 2048u
|
||||||
|
#define RTL8139_RX_RING_SIZE 8192u
|
||||||
|
#define RTL8139_RX_PAD 16u
|
||||||
|
#define RTL8139_RX_GUARD 1500u
|
||||||
|
#define RTL8139_RX_ALLOC_SIZE (RTL8139_RX_RING_SIZE + RTL8139_RX_PAD + RTL8139_RX_GUARD)
|
||||||
|
#define RTL8139_RX_PAGES ((RTL8139_RX_ALLOC_SIZE + 4095u) / 4096u)
|
||||||
|
#define RTL8139_TX_PAGES (((RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE) + 4095u) / 4096u)
|
||||||
|
#define RTL8139_MMIO_PAGES 1u
|
||||||
|
#define RTL8139_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u)
|
||||||
|
|
||||||
|
#define RTL8139_REG_TSD(slot) (RTL8139_REG_TSD0 + ((slot) * 4u))
|
||||||
|
#define RTL8139_REG_TSAD(slot) (RTL8139_REG_TSAD0 + ((slot) * 4u))
|
||||||
|
|
||||||
|
typedef enum RTL8139_BAR_KIND {
|
||||||
|
Rtl8139BarNone = 0,
|
||||||
|
Rtl8139BarIo,
|
||||||
|
Rtl8139BarMmio,
|
||||||
|
} RTL8139_BAR_KIND;
|
||||||
|
|
||||||
|
typedef struct RTL8139_BAR {
|
||||||
|
RTL8139_BAR_KIND Kind;
|
||||||
|
uint64_t Base;
|
||||||
|
uint8_t Index;
|
||||||
|
} RTL8139_BAR;
|
||||||
|
|
||||||
|
typedef struct RTL8139_DEVICE {
|
||||||
|
uint8_t Bus;
|
||||||
|
uint8_t Dev;
|
||||||
|
uint8_t Fn;
|
||||||
|
uint8_t Irq;
|
||||||
|
|
||||||
|
bool UseMmio;
|
||||||
|
volatile uint8_t* Mmio;
|
||||||
|
uint16_t IoBase;
|
||||||
|
|
||||||
|
PHYSICAL_ADDRESS RxPhys;
|
||||||
|
PHYSICAL_ADDRESS TxPhys;
|
||||||
|
uint8_t* RxBuffer;
|
||||||
|
uint8_t* TxBuffers;
|
||||||
|
uint8_t RxScratch[RTL8139_TX_BUF_SIZE];
|
||||||
|
|
||||||
|
uint32_t RxOffset;
|
||||||
|
uint32_t TxSlot;
|
||||||
|
SPINLOCK TxLock;
|
||||||
|
KDPC Dpc;
|
||||||
|
volatile uint16_t PendingIsr;
|
||||||
|
|
||||||
|
NET_DEVICE NetDevice;
|
||||||
|
} RTL8139_DEVICE;
|
||||||
|
|
||||||
|
static const HAL_API* gHal;
|
||||||
|
static RTL8139_DEVICE gDevice;
|
||||||
|
static bool gDevicePresent = false;
|
||||||
|
|
||||||
|
static __forceinline void Rtl8139MemoryBarrier(void) {
|
||||||
|
_ReadWriteBarrier();
|
||||||
|
_mm_mfence();
|
||||||
|
_ReadWriteBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t Rtl8139Read8(RTL8139_DEVICE* r, uint32_t reg) {
|
||||||
|
if (r->UseMmio)
|
||||||
|
return *(volatile uint8_t*)(r->Mmio + reg);
|
||||||
|
return __inbyte((unsigned short)(r->IoBase + reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t Rtl8139Read16(RTL8139_DEVICE* r, uint32_t reg) {
|
||||||
|
if (r->UseMmio)
|
||||||
|
return *(volatile uint16_t*)(r->Mmio + reg);
|
||||||
|
return __inword((unsigned short)(r->IoBase + reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t Rtl8139Read32(RTL8139_DEVICE* r, uint32_t reg) {
|
||||||
|
if (r->UseMmio)
|
||||||
|
return *(volatile uint32_t*)(r->Mmio + reg);
|
||||||
|
return __indword((unsigned short)(r->IoBase + reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139Write8(RTL8139_DEVICE* r, uint32_t reg, uint8_t value) {
|
||||||
|
if (r->UseMmio) *(volatile uint8_t*)(r->Mmio + reg) = value;
|
||||||
|
else __outbyte((unsigned short)(r->IoBase + reg), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139Write16(RTL8139_DEVICE* r, uint32_t reg, uint16_t value) {
|
||||||
|
if (r->UseMmio) *(volatile uint16_t*)(r->Mmio + reg) = value;
|
||||||
|
else __outword((unsigned short)(r->IoBase + reg), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139Write32(RTL8139_DEVICE* r, uint32_t reg, uint32_t value) {
|
||||||
|
if (r->UseMmio) *(volatile uint32_t*)(r->Mmio + reg) = value;
|
||||||
|
else __outdword((unsigned short)(r->IoBase + reg), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Rtl8139ReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL8139_BAR* out) {
|
||||||
|
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
|
||||||
|
uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg);
|
||||||
|
|
||||||
|
out->Kind = Rtl8139BarNone;
|
||||||
|
out->Base = 0;
|
||||||
|
out->Index = index;
|
||||||
|
|
||||||
|
if (lo == 0 || lo == 0xFFFFFFFFu)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (lo & 1u) {
|
||||||
|
uint32_t base = lo & ~0x3u;
|
||||||
|
if (!base) return false;
|
||||||
|
out->Kind = Rtl8139BarIo;
|
||||||
|
out->Base = base;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t base = (uint64_t)(lo & ~0xFu);
|
||||||
|
uint32_t type = (lo >> 1) & 0x3u;
|
||||||
|
if (type == 0x2u && index < 5) {
|
||||||
|
uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
|
||||||
|
base |= ((uint64_t)hi << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!base) return false;
|
||||||
|
out->Kind = Rtl8139BarMmio;
|
||||||
|
out->Base = base;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS Rtl8139MapMmio(uint64_t physBase, volatile uint8_t** outMmio) {
|
||||||
|
uint64_t pageBase = physBase & PAGE_MASK;
|
||||||
|
uint64_t pageOff = physBase - pageBase;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < RTL8139_MMIO_PAGES; i++) {
|
||||||
|
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
|
||||||
|
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
|
||||||
|
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||||
|
if (st == STATUS_ALREADY_EXISTS)
|
||||||
|
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS Rtl8139SelectRegisterBar(RTL8139_DEVICE* r) {
|
||||||
|
RTL8139_BAR best = { Rtl8139BarNone, 0, 0 };
|
||||||
|
RTL8139_BAR fallback = { Rtl8139BarNone, 0, 0 };
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 6; i++) {
|
||||||
|
RTL8139_BAR bar;
|
||||||
|
if (!Rtl8139ReadBar(r->Bus, r->Dev, r->Fn, i, &bar))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bar.Kind == Rtl8139BarMmio) {
|
||||||
|
best = bar;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (bar.Kind == Rtl8139BarIo && fallback.Kind == Rtl8139BarNone)
|
||||||
|
fallback = bar;
|
||||||
|
|
||||||
|
if (bar.Kind == Rtl8139BarMmio) {
|
||||||
|
uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u));
|
||||||
|
if (((lo >> 1) & 0x3u) == 0x2u)
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best.Kind == Rtl8139BarNone)
|
||||||
|
best = fallback;
|
||||||
|
|
||||||
|
if (best.Kind == Rtl8139BarMmio) {
|
||||||
|
KSTATUS st = Rtl8139MapMmio(best.Base, &r->Mmio);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
return st;
|
||||||
|
r->UseMmio = true;
|
||||||
|
DbgPrint("RTL8139: BAR%u MMIO base=0x%llx virt=%p\n",
|
||||||
|
best.Index, (unsigned long long)best.Base, r->Mmio);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best.Kind == Rtl8139BarIo) {
|
||||||
|
if (best.Base > 0xFFFFu)
|
||||||
|
return STATUS_NOT_SUPPORTED;
|
||||||
|
r->IoBase = (uint16_t)best.Base;
|
||||||
|
r->UseMmio = false;
|
||||||
|
DbgPrint("RTL8139: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139EnablePci(RTL8139_DEVICE* r) {
|
||||||
|
uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND);
|
||||||
|
cmd |= PCI_COMMAND_MASTER;
|
||||||
|
if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY;
|
||||||
|
else cmd |= PCI_COMMAND_IO;
|
||||||
|
cmd &= ~PCI_COMMAND_INTX_DISABLE;
|
||||||
|
gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS Rtl8139Reset(RTL8139_DEVICE* r) {
|
||||||
|
Rtl8139Write8(r, RTL8139_REG_CMD, RTL8139_CMD_RST);
|
||||||
|
for (uint32_t i = 0; i < 1000; i++) {
|
||||||
|
if ((Rtl8139Read8(r, RTL8139_REG_CMD) & RTL8139_CMD_RST) == 0)
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
if (gHal->Timer.Stall)
|
||||||
|
gHal->Timer.Stall(1000);
|
||||||
|
}
|
||||||
|
return STATUS_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139ReadMac(RTL8139_DEVICE* r) {
|
||||||
|
for (uint32_t i = 0; i < 6; i++)
|
||||||
|
r->NetDevice.HardwareAddress[i] = Rtl8139Read8(r, RTL8139_REG_IDR0 + i);
|
||||||
|
r->NetDevice.HardwareAddressLength = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS Rtl8139AllocateDma(RTL8139_DEVICE* r) {
|
||||||
|
r->RxPhys = MmAllocateContiguous(RTL8139_RX_PAGES);
|
||||||
|
r->TxPhys = MmAllocateContiguous(RTL8139_TX_PAGES);
|
||||||
|
if (!r->RxPhys || !r->TxPhys)
|
||||||
|
return STATUS_NO_MEMORY;
|
||||||
|
|
||||||
|
// RTL8139 has 32-bit DMA address registers. This is fine for typical QEMU
|
||||||
|
// and low-memory hobby OS configs, but make failure explicit.
|
||||||
|
if ((r->RxPhys + RTL8139_RX_ALLOC_SIZE - 1) > 0xFFFFFFFFULL ||
|
||||||
|
(r->TxPhys + RTL8139_TX_SLOTS * RTL8139_TX_BUF_SIZE - 1) > 0xFFFFFFFFULL) {
|
||||||
|
DbgPrint("RTL8139: DMA allocation above 4GiB is not supported by this NIC\n");
|
||||||
|
return STATUS_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->RxBuffer = (uint8_t*)gHal->PhysToVirt(r->RxPhys);
|
||||||
|
r->TxBuffers = (uint8_t*)gHal->PhysToVirt(r->TxPhys);
|
||||||
|
memset(r->RxBuffer, 0, PAGE_SIZE * RTL8139_RX_PAGES);
|
||||||
|
memset(r->TxBuffers, 0, PAGE_SIZE * RTL8139_TX_PAGES);
|
||||||
|
|
||||||
|
DbgPrint("RTL8139: rx=0x%llx tx=0x%llx\n",
|
||||||
|
(unsigned long long)r->RxPhys,
|
||||||
|
(unsigned long long)r->TxPhys);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t Rtl8139RxRead16(RTL8139_DEVICE* r, uint32_t offset) {
|
||||||
|
offset %= RTL8139_RX_RING_SIZE;
|
||||||
|
uint16_t lo = r->RxBuffer[offset];
|
||||||
|
uint16_t hi = r->RxBuffer[(offset + 1u) % RTL8139_RX_RING_SIZE];
|
||||||
|
return (uint16_t)(lo | (hi << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139RxCopy(RTL8139_DEVICE* r, uint32_t offset, void* dst, uint32_t length) {
|
||||||
|
uint8_t* out = (uint8_t*)dst;
|
||||||
|
offset %= RTL8139_RX_RING_SIZE;
|
||||||
|
|
||||||
|
uint32_t first = RTL8139_RX_RING_SIZE - offset;
|
||||||
|
if (first > length)
|
||||||
|
first = length;
|
||||||
|
memcpy(out, r->RxBuffer + offset, first);
|
||||||
|
if (first < length)
|
||||||
|
memcpy(out + first, r->RxBuffer, length - first);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139ProgramHardware(RTL8139_DEVICE* r) {
|
||||||
|
// Wake the device and clear stale interrupts before installing buffers.
|
||||||
|
Rtl8139Write8(r, RTL8139_REG_CONFIG1, 0x00);
|
||||||
|
Rtl8139Write16(r, RTL8139_REG_IMR, 0);
|
||||||
|
Rtl8139Write16(r, RTL8139_REG_ISR, 0xFFFFu);
|
||||||
|
|
||||||
|
Rtl8139Write32(r, RTL8139_REG_RBSTART, (uint32_t)r->RxPhys);
|
||||||
|
r->RxOffset = 0;
|
||||||
|
Rtl8139Write16(r, RTL8139_REG_CAPR, 0xFFF0u); // CAPR is rxOffset - 16.
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < RTL8139_TX_SLOTS; i++) {
|
||||||
|
PHYSICAL_ADDRESS p = r->TxPhys + (PHYSICAL_ADDRESS)i * RTL8139_TX_BUF_SIZE;
|
||||||
|
Rtl8139Write32(r, RTL8139_REG_TSAD(i), (uint32_t)p);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rtl8139Write32(r, RTL8139_REG_TCR,
|
||||||
|
RTL8139_TCR_IFG_NORMAL | RTL8139_TCR_MXDMA_UNLIMITED);
|
||||||
|
Rtl8139Write32(r, RTL8139_REG_RCR,
|
||||||
|
RTL8139_RCR_APM | RTL8139_RCR_AB | RTL8139_RCR_AM |
|
||||||
|
RTL8139_RCR_WRAP | RTL8139_RCR_RBLEN_8K |
|
||||||
|
RTL8139_RCR_MXDMA_UNLIMITED);
|
||||||
|
|
||||||
|
Rtl8139MemoryBarrier();
|
||||||
|
Rtl8139Write8(r, RTL8139_REG_CMD, RTL8139_CMD_RE | RTL8139_CMD_TE);
|
||||||
|
Rtl8139Write16(r, RTL8139_REG_ISR, 0xFFFFu);
|
||||||
|
Rtl8139Write16(r, RTL8139_REG_IMR, RTL8139_INTR_ENABLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139HandleRx(RTL8139_DEVICE* r) {
|
||||||
|
for (uint32_t budget = 0; budget < 64; budget++) {
|
||||||
|
uint8_t cmd = Rtl8139Read8(r, RTL8139_REG_CMD);
|
||||||
|
if (cmd & RTL8139_CMD_BUFE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint32_t off = r->RxOffset % RTL8139_RX_RING_SIZE;
|
||||||
|
uint16_t status = Rtl8139RxRead16(r, off);
|
||||||
|
uint16_t packetLength = Rtl8139RxRead16(r, off + 2u);
|
||||||
|
|
||||||
|
if (packetLength < 4 || packetLength > RTL8139_RX_WIRE_MAX) {
|
||||||
|
DbgPrint("RTL8139: bad rx length %u status=0x%04x, resetting rx pointer\n",
|
||||||
|
packetLength, status);
|
||||||
|
r->RxOffset = Rtl8139Read16(r, RTL8139_REG_CBR) % RTL8139_RX_RING_SIZE;
|
||||||
|
Rtl8139Write16(r, RTL8139_REG_CAPR, (uint16_t)(r->RxOffset - 16u));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t frameLength = packetLength;
|
||||||
|
if (frameLength > 4)
|
||||||
|
frameLength -= 4; // Strip Ethernet FCS before upper-layer handoff.
|
||||||
|
|
||||||
|
bool goodFrame = (status & RTL8139_RX_OK) &&
|
||||||
|
!(status & RTL8139_RX_BAD_BITS) &&
|
||||||
|
frameLength >= NET_ETHERNET_HEADER_LENGTH &&
|
||||||
|
frameLength <= NET_ETHERNET_MAX_FRAME;
|
||||||
|
if (goodFrame) {
|
||||||
|
if ((off + 4u + frameLength) <= RTL8139_RX_RING_SIZE) {
|
||||||
|
NetReceiveFrame(&r->NetDevice, r->RxBuffer + off + 4u, frameLength);
|
||||||
|
} else {
|
||||||
|
Rtl8139RxCopy(r, off + 4u, r->RxScratch, frameLength);
|
||||||
|
NetReceiveFrame(&r->NetDevice, r->RxScratch, frameLength);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DbgPrint("RTL8139: rx error status=0x%04x len=%u\n", status, packetLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->RxOffset = (r->RxOffset + 4u + packetLength + 3u) & ~3u;
|
||||||
|
r->RxOffset %= RTL8139_RX_RING_SIZE;
|
||||||
|
Rtl8139MemoryBarrier();
|
||||||
|
Rtl8139Write16(r, RTL8139_REG_CAPR, (uint16_t)(r->RxOffset - 16u));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139DpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
|
||||||
|
(void)dpc;
|
||||||
|
(void)systemArgument1;
|
||||||
|
(void)systemArgument2;
|
||||||
|
|
||||||
|
RTL8139_DEVICE* r = (RTL8139_DEVICE*)context;
|
||||||
|
uint16_t status = (uint16_t)r->PendingIsr;
|
||||||
|
r->PendingIsr = 0;
|
||||||
|
|
||||||
|
if (status & (RTL8139_ISR_ROK | RTL8139_ISR_RER | RTL8139_ISR_RXOVW | RTL8139_ISR_FOVW))
|
||||||
|
Rtl8139HandleRx(r);
|
||||||
|
|
||||||
|
if (status & (RTL8139_ISR_PUN | RTL8139_ISR_LENCHG))
|
||||||
|
DbgPrint("RTL8139: link/status change isr=0x%04x\n", status);
|
||||||
|
|
||||||
|
if (status & (RTL8139_ISR_RER | RTL8139_ISR_TER | RTL8139_ISR_RXOVW |
|
||||||
|
RTL8139_ISR_FOVW | RTL8139_ISR_SYSERR))
|
||||||
|
DbgPrint("RTL8139: interrupt error bits=0x%04x\n", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Rtl8139Isr(void) {
|
||||||
|
if (!gDevicePresent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RTL8139_DEVICE* r = &gDevice;
|
||||||
|
uint16_t status = Rtl8139Read16(r, RTL8139_REG_ISR);
|
||||||
|
status &= RTL8139_INTR_ENABLES;
|
||||||
|
if (!status || status == 0xFFFFu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rtl8139Write16(r, RTL8139_REG_ISR, status);
|
||||||
|
r->PendingIsr |= status;
|
||||||
|
KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS Rtl8139SendFrame(NET_DEVICE* device, const void* frame, size_t length) {
|
||||||
|
if (!device || !frame)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (length > NET_ETHERNET_MAX_FRAME)
|
||||||
|
return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
if (length < NET_ETHERNET_HEADER_LENGTH)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
RTL8139_DEVICE* r = (RTL8139_DEVICE*)device->DriverContext;
|
||||||
|
if (!r)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&r->TxLock);
|
||||||
|
uint32_t slot = r->TxSlot;
|
||||||
|
uint32_t tsd = Rtl8139Read32(r, RTL8139_REG_TSD(slot));
|
||||||
|
// RTL8139 TSD.OWN is 1 when the descriptor is idle/free.
|
||||||
|
// Writing a command with OWN cleared hands the slot to the NIC.
|
||||||
|
if (!(tsd & RTL8139_TSD_OWN)) {
|
||||||
|
KeReleaseSpinlock(&r->TxLock, flags);
|
||||||
|
return STATUS_QUEUE_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t wireLength = length < NET_ETHERNET_MIN_FRAME ? NET_ETHERNET_MIN_FRAME : length;
|
||||||
|
uint8_t* buf = r->TxBuffers + slot * RTL8139_TX_BUF_SIZE;
|
||||||
|
memcpy(buf, frame, length);
|
||||||
|
if (wireLength > length)
|
||||||
|
memset(buf + length, 0, wireLength - length);
|
||||||
|
|
||||||
|
Rtl8139MemoryBarrier();
|
||||||
|
Rtl8139Write32(r, RTL8139_REG_TSD(slot), (uint32_t)wireLength & RTL8139_TSD_SIZE_MASK);
|
||||||
|
|
||||||
|
r->TxSlot = (slot + 1u) % RTL8139_TX_SLOTS;
|
||||||
|
KeReleaseSpinlock(&r->TxLock, flags);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS Rtl8139Initialize(const HAL_API* hal) {
|
||||||
|
gHal = hal;
|
||||||
|
memset(&gDevice, 0, sizeof(gDevice));
|
||||||
|
gDevice.TxLock = (SPINLOCK)SPINLOCK_INIT;
|
||||||
|
|
||||||
|
if (!gHal->Pci.FindDevice(RTL8139_VENDOR_REALTEK, RTL8139_DEVICE_ID,
|
||||||
|
&gDevice.Bus, &gDevice.Dev, &gDevice.Fn)) {
|
||||||
|
DbgPrint("RTL8139: no PCI device 10ec:8139 found; skipping\n");
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
DbgPrint("RTL8139: found Realtek PCI device 10ec:8139 at %u:%u.%u\n",
|
||||||
|
gDevice.Bus, gDevice.Dev, gDevice.Fn);
|
||||||
|
|
||||||
|
KSTATUS st = Rtl8139SelectRegisterBar(&gDevice);
|
||||||
|
if (!KSUCCESS(st)) {
|
||||||
|
DbgPrint("RTL8139: no usable register BAR (0x%x)\n", st);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rtl8139EnablePci(&gDevice);
|
||||||
|
|
||||||
|
gDevice.Irq = hal->Pci.GetIrqLine(gDevice.Bus, gDevice.Dev, gDevice.Fn);
|
||||||
|
if (gDevice.Irq == 0xFFu) {
|
||||||
|
DbgPrint("RTL8139: no legacy PCI IRQ line wired\n");
|
||||||
|
return STATUS_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake, reset, then read the station address.
|
||||||
|
Rtl8139Write8(&gDevice, RTL8139_REG_CONFIG1, 0x00);
|
||||||
|
st = Rtl8139Reset(&gDevice);
|
||||||
|
if (!KSUCCESS(st)) {
|
||||||
|
DbgPrint("RTL8139: reset timed out\n");
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rtl8139ReadMac(&gDevice);
|
||||||
|
DbgPrint("RTL8139: MAC %02x:%02x:%02x:%02x:%02x:%02x irq=%u\n",
|
||||||
|
gDevice.NetDevice.HardwareAddress[0], gDevice.NetDevice.HardwareAddress[1],
|
||||||
|
gDevice.NetDevice.HardwareAddress[2], gDevice.NetDevice.HardwareAddress[3],
|
||||||
|
gDevice.NetDevice.HardwareAddress[4], gDevice.NetDevice.HardwareAddress[5],
|
||||||
|
gDevice.Irq);
|
||||||
|
|
||||||
|
st = Rtl8139AllocateDma(&gDevice);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
return st;
|
||||||
|
|
||||||
|
KeInitializeDpc(&gDevice.Dpc, Rtl8139DpcRoutine, &gDevice);
|
||||||
|
|
||||||
|
gDevice.NetDevice.Mtu = NET_MTU_ETHERNET;
|
||||||
|
gDevice.NetDevice.DriverContext = &gDevice;
|
||||||
|
gDevice.NetDevice.SendFrame = Rtl8139SendFrame;
|
||||||
|
|
||||||
|
Rtl8139ProgramHardware(&gDevice);
|
||||||
|
|
||||||
|
gDevicePresent = true;
|
||||||
|
KeIrqRegister(gDevice.Irq, Rtl8139Isr);
|
||||||
|
|
||||||
|
st = NetRegisterDevice(&gDevice.NetDevice);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
return st;
|
||||||
|
|
||||||
|
DbgPrint("RTL8139: initialized; RX/TX enabled on IRQ %u\n", gDevice.Irq);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
KSTATUS DriverEntry(const HAL_API* hal) {
|
||||||
|
DbgPrint("RTL8139: driver loading...\n");
|
||||||
|
KSTATUS st = Rtl8139Initialize(hal);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
DbgPrint("RTL8139: init failed: 0x%x\n", st);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
@@ -0,0 +1,611 @@
|
|||||||
|
// rtl816x.c - first-pass Realtek RTL8111/8168/8411 PCIe Gigabit NIC driver.
|
||||||
|
//
|
||||||
|
// Scope for this version:
|
||||||
|
// * RTL8168/RTL8169-compatible PCI IDs, legacy INTx interrupt delivery.
|
||||||
|
// * MMIO preferred, I/O BAR fallback.
|
||||||
|
// * Descriptor-mode RX/TX with plain Ethernet frames.
|
||||||
|
// * No checksum offload, TSO, VLAN, jumbo frames, MSI/MSI-X, or EEE.
|
||||||
|
//
|
||||||
|
// The driver registers a NET_DEVICE so a later ARP/IPv4/ICMP stack can attach
|
||||||
|
// without knowing Realtek register details.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
|
||||||
|
#include <hal_api.h>
|
||||||
|
#include <ke_status.h>
|
||||||
|
#include <ke_debug.h>
|
||||||
|
#include <ke_irq.h>
|
||||||
|
#include <ke_dpc.h>
|
||||||
|
#include <ke_spinlock.h>
|
||||||
|
#include <mm_phys.h>
|
||||||
|
#include <mm_virt.h>
|
||||||
|
#include <net_device.h>
|
||||||
|
|
||||||
|
#define RTL_VENDOR_REALTEK 0x10ECu
|
||||||
|
#define RTL_DEVICE_8168 0x8168u
|
||||||
|
#define RTL_DEVICE_8169 0x8169u
|
||||||
|
|
||||||
|
#define PCI_REG_COMMAND 0x04u
|
||||||
|
#define PCI_REG_BAR0 0x10u
|
||||||
|
#define PCI_REG_IRQ 0x3Cu
|
||||||
|
#define PCI_COMMAND_IO (1u << 0)
|
||||||
|
#define PCI_COMMAND_MEMORY (1u << 1)
|
||||||
|
#define PCI_COMMAND_MASTER (1u << 2)
|
||||||
|
#define PCI_COMMAND_INTX_DISABLE (1u << 10)
|
||||||
|
|
||||||
|
#define RTL_REG_MAC0 0x00u
|
||||||
|
#define RTL_REG_MAR0 0x08u
|
||||||
|
#define RTL_REG_TNPDS_LO 0x20u
|
||||||
|
#define RTL_REG_TNPDS_HI 0x24u
|
||||||
|
#define RTL_REG_THPDS_LO 0x28u
|
||||||
|
#define RTL_REG_THPDS_HI 0x2Cu
|
||||||
|
#define RTL_REG_CHIP_CMD 0x37u
|
||||||
|
#define RTL_REG_TX_POLL 0x38u
|
||||||
|
#define RTL_REG_INTR_MASK 0x3Cu
|
||||||
|
#define RTL_REG_INTR_STAT 0x3Eu
|
||||||
|
#define RTL_REG_TX_CONFIG 0x40u
|
||||||
|
#define RTL_REG_RX_CONFIG 0x44u
|
||||||
|
#define RTL_REG_CFG9346 0x50u
|
||||||
|
#define RTL_REG_CONFIG1 0x52u
|
||||||
|
#define RTL_REG_PHY_STATUS 0x6Cu
|
||||||
|
#define RTL_REG_RX_MAXSIZE 0xDAu
|
||||||
|
#define RTL_REG_CPLUS_CMD 0xE0u
|
||||||
|
#define RTL_REG_RDSAR_LO 0xE4u
|
||||||
|
#define RTL_REG_RDSAR_HI 0xE8u
|
||||||
|
|
||||||
|
#define RTL_CMD_RESET 0x10u
|
||||||
|
#define RTL_CMD_RX_ENABLE 0x08u
|
||||||
|
#define RTL_CMD_TX_ENABLE 0x04u
|
||||||
|
|
||||||
|
#define RTL_TXPOLL_NPQ 0x40u
|
||||||
|
|
||||||
|
#define RTL_ISR_ROK 0x0001u
|
||||||
|
#define RTL_ISR_RER 0x0002u
|
||||||
|
#define RTL_ISR_TOK 0x0004u
|
||||||
|
#define RTL_ISR_TER 0x0008u
|
||||||
|
#define RTL_ISR_RXOVW 0x0010u
|
||||||
|
#define RTL_ISR_LINKCHG 0x0020u
|
||||||
|
#define RTL_ISR_FOVW 0x0040u
|
||||||
|
#define RTL_ISR_TDU 0x0080u
|
||||||
|
#define RTL_ISR_SWINT 0x0100u
|
||||||
|
#define RTL_ISR_TIMEOUT 0x4000u
|
||||||
|
#define RTL_ISR_SYSERR 0x8000u
|
||||||
|
|
||||||
|
#define RTL_INTR_ENABLES (RTL_ISR_ROK | RTL_ISR_RER | RTL_ISR_TOK | RTL_ISR_TER | \
|
||||||
|
RTL_ISR_RXOVW | RTL_ISR_LINKCHG | RTL_ISR_FOVW | \
|
||||||
|
RTL_ISR_TDU | RTL_ISR_SYSERR)
|
||||||
|
|
||||||
|
#define RTL_RXCFG_AAP (1u << 0) // accept all physical packets/promiscuous
|
||||||
|
#define RTL_RXCFG_APM (1u << 1) // accept physical match
|
||||||
|
#define RTL_RXCFG_AM (1u << 2) // accept multicast
|
||||||
|
#define RTL_RXCFG_AB (1u << 3) // accept broadcast
|
||||||
|
#define RTL_RXCFG_AR (1u << 4) // accept runt
|
||||||
|
#define RTL_RXCFG_AER (1u << 5) // accept errored
|
||||||
|
#define RTL_RXCFG_DMA_UNLIMITED (7u << 8)
|
||||||
|
|
||||||
|
#define RTL_TXCFG_DMA_UNLIMITED (7u << 8)
|
||||||
|
#define RTL_TXCFG_IFG (3u << 24)
|
||||||
|
|
||||||
|
#define RTL_DESC_OWN (1u << 31)
|
||||||
|
#define RTL_DESC_EOR (1u << 30)
|
||||||
|
#define RTL_DESC_FS (1u << 29)
|
||||||
|
#define RTL_DESC_LS (1u << 28)
|
||||||
|
#define RTL_RXDESC_RES (1u << 21)
|
||||||
|
#define RTL_RXDESC_RWT (1u << 22)
|
||||||
|
#define RTL_RXDESC_CRC (1u << 19)
|
||||||
|
#define RTL_RXDESC_RUNT (1u << 20)
|
||||||
|
#define RTL_DESC_LENGTH_MASK 0x00003FFFu
|
||||||
|
|
||||||
|
#define RTL_RX_COUNT 64u
|
||||||
|
#define RTL_TX_COUNT 64u
|
||||||
|
#define RTL_BUF_SIZE 2048u
|
||||||
|
#define RTL_RX_WIRE_MAX (NET_ETHERNET_MAX_FRAME + 4u)
|
||||||
|
#define RTL_RING_PAGES 1u
|
||||||
|
#define RTL_RXBUF_PAGES ((RTL_RX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u)
|
||||||
|
#define RTL_TXBUF_PAGES ((RTL_TX_COUNT * RTL_BUF_SIZE + 4095u) / 4096u)
|
||||||
|
#define RTL_MMIO_PAGES 1u
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct RTL_DESC {
|
||||||
|
volatile uint32_t opts1;
|
||||||
|
volatile uint32_t opts2;
|
||||||
|
volatile uint32_t addrLo;
|
||||||
|
volatile uint32_t addrHi;
|
||||||
|
} RTL_DESC;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
typedef enum RTL_BAR_KIND {
|
||||||
|
RtlBarNone = 0,
|
||||||
|
RtlBarIo,
|
||||||
|
RtlBarMmio,
|
||||||
|
} RTL_BAR_KIND;
|
||||||
|
|
||||||
|
typedef struct RTL_BAR {
|
||||||
|
RTL_BAR_KIND Kind;
|
||||||
|
uint64_t Base;
|
||||||
|
uint8_t Index;
|
||||||
|
} RTL_BAR;
|
||||||
|
|
||||||
|
typedef struct RTL816X_DEVICE {
|
||||||
|
uint8_t Bus;
|
||||||
|
uint8_t Dev;
|
||||||
|
uint8_t Fn;
|
||||||
|
uint8_t Irq;
|
||||||
|
uint16_t DeviceId;
|
||||||
|
|
||||||
|
bool UseMmio;
|
||||||
|
volatile uint8_t* Mmio;
|
||||||
|
uint16_t IoBase;
|
||||||
|
|
||||||
|
RTL_DESC* RxRing;
|
||||||
|
RTL_DESC* TxRing;
|
||||||
|
PHYSICAL_ADDRESS RxRingPhys;
|
||||||
|
PHYSICAL_ADDRESS TxRingPhys;
|
||||||
|
|
||||||
|
uint8_t* RxBuffers;
|
||||||
|
uint8_t* TxBuffers;
|
||||||
|
PHYSICAL_ADDRESS RxBuffersPhys;
|
||||||
|
PHYSICAL_ADDRESS TxBuffersPhys;
|
||||||
|
|
||||||
|
uint32_t RxIndex;
|
||||||
|
uint32_t TxIndex;
|
||||||
|
SPINLOCK TxLock;
|
||||||
|
KDPC Dpc;
|
||||||
|
volatile uint16_t PendingIsr;
|
||||||
|
|
||||||
|
NET_DEVICE NetDevice;
|
||||||
|
} RTL816X_DEVICE;
|
||||||
|
|
||||||
|
static const HAL_API* gHal;
|
||||||
|
static RTL816X_DEVICE gDevice;
|
||||||
|
static bool gDevicePresent = false;
|
||||||
|
|
||||||
|
static __forceinline void RtlMemoryBarrier(void) {
|
||||||
|
_ReadWriteBarrier();
|
||||||
|
_mm_mfence();
|
||||||
|
_ReadWriteBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t RtlRead8(RTL816X_DEVICE* r, uint32_t reg) {
|
||||||
|
if (r->UseMmio)
|
||||||
|
return *(volatile uint8_t*)(r->Mmio + reg);
|
||||||
|
return __inbyte((unsigned short)(r->IoBase + reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t RtlRead16(RTL816X_DEVICE* r, uint32_t reg) {
|
||||||
|
if (r->UseMmio)
|
||||||
|
return *(volatile uint16_t*)(r->Mmio + reg);
|
||||||
|
return __inword((unsigned short)(r->IoBase + reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t RtlRead32(RTL816X_DEVICE* r, uint32_t reg) {
|
||||||
|
if (r->UseMmio)
|
||||||
|
return *(volatile uint32_t*)(r->Mmio + reg);
|
||||||
|
return __indword((unsigned short)(r->IoBase + reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlWrite8(RTL816X_DEVICE* r, uint32_t reg, uint8_t value) {
|
||||||
|
if (r->UseMmio) *(volatile uint8_t*)(r->Mmio + reg) = value;
|
||||||
|
else __outbyte((unsigned short)(r->IoBase + reg), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlWrite16(RTL816X_DEVICE* r, uint32_t reg, uint16_t value) {
|
||||||
|
if (r->UseMmio) *(volatile uint16_t*)(r->Mmio + reg) = value;
|
||||||
|
else __outword((unsigned short)(r->IoBase + reg), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlWrite32(RTL816X_DEVICE* r, uint32_t reg, uint32_t value) {
|
||||||
|
if (r->UseMmio) *(volatile uint32_t*)(r->Mmio + reg) = value;
|
||||||
|
else __outdword((unsigned short)(r->IoBase + reg), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool RtlFindDevice(uint8_t* outBus, uint8_t* outDev, uint8_t* outFn, uint16_t* outDeviceId) {
|
||||||
|
if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8168, outBus, outDev, outFn)) {
|
||||||
|
*outDeviceId = RTL_DEVICE_8168;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (gHal->Pci.FindDevice(RTL_VENDOR_REALTEK, RTL_DEVICE_8169, outBus, outDev, outFn)) {
|
||||||
|
*outDeviceId = RTL_DEVICE_8169;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool RtlReadBar(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t index, RTL_BAR* out) {
|
||||||
|
uint8_t reg = (uint8_t)(PCI_REG_BAR0 + index * 4u);
|
||||||
|
uint32_t lo = gHal->Pci.Read32(bus, dev, fn, reg);
|
||||||
|
|
||||||
|
out->Kind = RtlBarNone;
|
||||||
|
out->Base = 0;
|
||||||
|
out->Index = index;
|
||||||
|
|
||||||
|
if (lo == 0 || lo == 0xFFFFFFFFu)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (lo & 1u) {
|
||||||
|
uint32_t base = lo & ~0x3u;
|
||||||
|
if (!base) return false;
|
||||||
|
out->Kind = RtlBarIo;
|
||||||
|
out->Base = base;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t base = (uint64_t)(lo & ~0xFu);
|
||||||
|
uint32_t type = (lo >> 1) & 0x3u;
|
||||||
|
if (type == 0x2u && index < 5) {
|
||||||
|
uint32_t hi = gHal->Pci.Read32(bus, dev, fn, (uint8_t)(reg + 4u));
|
||||||
|
base |= ((uint64_t)hi << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!base) return false;
|
||||||
|
out->Kind = RtlBarMmio;
|
||||||
|
out->Base = base;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS RtlMapMmio(uint64_t physBase, volatile uint8_t** outMmio) {
|
||||||
|
uint64_t pageBase = physBase & PAGE_MASK;
|
||||||
|
uint64_t pageOff = physBase - pageBase;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < RTL_MMIO_PAGES; i++) {
|
||||||
|
PHYSICAL_ADDRESS phys = pageBase + (uint64_t)i * PAGE_SIZE;
|
||||||
|
uintptr_t virt = (uintptr_t)gHal->PhysToVirt(phys);
|
||||||
|
KSTATUS st = MmMapPage(virt, phys, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||||
|
if (st == STATUS_ALREADY_EXISTS)
|
||||||
|
st = MmSetPageProtect(virt, PAGE_SIZE, PAGE_PRESENT | PAGE_WRITE | PAGE_NOCACHE);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outMmio = (volatile uint8_t*)((uintptr_t)gHal->PhysToVirt(pageBase) + pageOff);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS RtlSelectRegisterBar(RTL816X_DEVICE* r) {
|
||||||
|
RTL_BAR best = { RtlBarNone, 0, 0 };
|
||||||
|
RTL_BAR fallback = { RtlBarNone, 0, 0 };
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 6; i++) {
|
||||||
|
RTL_BAR bar;
|
||||||
|
if (!RtlReadBar(r->Bus, r->Dev, r->Fn, i, &bar))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bar.Kind == RtlBarMmio) {
|
||||||
|
best = bar;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (bar.Kind == RtlBarIo && fallback.Kind == RtlBarNone)
|
||||||
|
fallback = bar;
|
||||||
|
|
||||||
|
// Skip the high dword of 64-bit memory BARs.
|
||||||
|
if (bar.Kind == RtlBarMmio) {
|
||||||
|
uint32_t lo = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, (uint8_t)(PCI_REG_BAR0 + i * 4u));
|
||||||
|
if (((lo >> 1) & 0x3u) == 0x2u)
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best.Kind == RtlBarNone)
|
||||||
|
best = fallback;
|
||||||
|
|
||||||
|
if (best.Kind == RtlBarMmio) {
|
||||||
|
KSTATUS st = RtlMapMmio(best.Base, &r->Mmio);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
return st;
|
||||||
|
r->UseMmio = true;
|
||||||
|
DbgPrint("RTL816X: BAR%u MMIO base=0x%llx virt=%p\n",
|
||||||
|
best.Index, (unsigned long long)best.Base, r->Mmio);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best.Kind == RtlBarIo) {
|
||||||
|
if (best.Base > 0xFFFFu)
|
||||||
|
return STATUS_NOT_SUPPORTED;
|
||||||
|
r->IoBase = (uint16_t)best.Base;
|
||||||
|
r->UseMmio = false;
|
||||||
|
DbgPrint("RTL816X: BAR%u I/O base=0x%04x\n", best.Index, r->IoBase);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlEnablePci(RTL816X_DEVICE* r) {
|
||||||
|
uint32_t cmd = gHal->Pci.Read32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND);
|
||||||
|
cmd |= PCI_COMMAND_MASTER;
|
||||||
|
if (r->UseMmio) cmd |= PCI_COMMAND_MEMORY;
|
||||||
|
else cmd |= PCI_COMMAND_IO;
|
||||||
|
cmd &= ~PCI_COMMAND_INTX_DISABLE;
|
||||||
|
gHal->Pci.Write32(r->Bus, r->Dev, r->Fn, PCI_REG_COMMAND, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS RtlReset(RTL816X_DEVICE* r) {
|
||||||
|
RtlWrite8(r, RTL_REG_CHIP_CMD, RTL_CMD_RESET);
|
||||||
|
for (uint32_t i = 0; i < 1000; i++) {
|
||||||
|
if ((RtlRead8(r, RTL_REG_CHIP_CMD) & RTL_CMD_RESET) == 0)
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
if (gHal->Timer.Stall)
|
||||||
|
gHal->Timer.Stall(1000);
|
||||||
|
}
|
||||||
|
return STATUS_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlReadMac(RTL816X_DEVICE* r) {
|
||||||
|
for (uint32_t i = 0; i < 6; i++)
|
||||||
|
r->NetDevice.HardwareAddress[i] = RtlRead8(r, RTL_REG_MAC0 + i);
|
||||||
|
r->NetDevice.HardwareAddressLength = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS RtlAllocateDma(RTL816X_DEVICE* r) {
|
||||||
|
r->RxRingPhys = MmAllocateContiguous(RTL_RING_PAGES);
|
||||||
|
r->TxRingPhys = MmAllocateContiguous(RTL_RING_PAGES);
|
||||||
|
r->RxBuffersPhys = MmAllocateContiguous(RTL_RXBUF_PAGES);
|
||||||
|
r->TxBuffersPhys = MmAllocateContiguous(RTL_TXBUF_PAGES);
|
||||||
|
|
||||||
|
if (!r->RxRingPhys || !r->TxRingPhys || !r->RxBuffersPhys || !r->TxBuffersPhys)
|
||||||
|
return STATUS_NO_MEMORY;
|
||||||
|
|
||||||
|
r->RxRing = (RTL_DESC*)gHal->PhysToVirt(r->RxRingPhys);
|
||||||
|
r->TxRing = (RTL_DESC*)gHal->PhysToVirt(r->TxRingPhys);
|
||||||
|
r->RxBuffers = (uint8_t*)gHal->PhysToVirt(r->RxBuffersPhys);
|
||||||
|
r->TxBuffers = (uint8_t*)gHal->PhysToVirt(r->TxBuffersPhys);
|
||||||
|
|
||||||
|
memset(r->RxRing, 0, PAGE_SIZE * RTL_RING_PAGES);
|
||||||
|
memset(r->TxRing, 0, PAGE_SIZE * RTL_RING_PAGES);
|
||||||
|
memset(r->RxBuffers, 0, PAGE_SIZE * RTL_RXBUF_PAGES);
|
||||||
|
memset(r->TxBuffers, 0, PAGE_SIZE * RTL_TXBUF_PAGES);
|
||||||
|
|
||||||
|
DbgPrint("RTL816X: rxRing=0x%llx txRing=0x%llx rxBuf=0x%llx txBuf=0x%llx\n",
|
||||||
|
(unsigned long long)r->RxRingPhys,
|
||||||
|
(unsigned long long)r->TxRingPhys,
|
||||||
|
(unsigned long long)r->RxBuffersPhys,
|
||||||
|
(unsigned long long)r->TxBuffersPhys);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlInitDescriptors(RTL816X_DEVICE* r) {
|
||||||
|
for (uint32_t i = 0; i < RTL_RX_COUNT; i++) {
|
||||||
|
PHYSICAL_ADDRESS p = r->RxBuffersPhys + (PHYSICAL_ADDRESS)i * RTL_BUF_SIZE;
|
||||||
|
r->RxRing[i].addrLo = (uint32_t)p;
|
||||||
|
r->RxRing[i].addrHi = (uint32_t)(p >> 32);
|
||||||
|
r->RxRing[i].opts2 = 0;
|
||||||
|
r->RxRing[i].opts1 = RTL_DESC_OWN | RTL_BUF_SIZE | ((i == RTL_RX_COUNT - 1) ? RTL_DESC_EOR : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < RTL_TX_COUNT; i++) {
|
||||||
|
PHYSICAL_ADDRESS p = r->TxBuffersPhys + (PHYSICAL_ADDRESS)i * RTL_BUF_SIZE;
|
||||||
|
r->TxRing[i].addrLo = (uint32_t)p;
|
||||||
|
r->TxRing[i].addrHi = (uint32_t)(p >> 32);
|
||||||
|
r->TxRing[i].opts2 = 0;
|
||||||
|
r->TxRing[i].opts1 = (i == RTL_TX_COUNT - 1) ? RTL_DESC_EOR : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->RxIndex = 0;
|
||||||
|
r->TxIndex = 0;
|
||||||
|
RtlMemoryBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlProgramHardware(RTL816X_DEVICE* r) {
|
||||||
|
// Unlock the 9346-style config register window while programming.
|
||||||
|
RtlWrite8(r, RTL_REG_CFG9346, 0xC0);
|
||||||
|
|
||||||
|
// Disable interrupts while rings are being installed.
|
||||||
|
RtlWrite16(r, RTL_REG_INTR_MASK, 0);
|
||||||
|
RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu);
|
||||||
|
|
||||||
|
// DMA ring base addresses.
|
||||||
|
RtlWrite32(r, RTL_REG_TNPDS_LO, (uint32_t)r->TxRingPhys);
|
||||||
|
RtlWrite32(r, RTL_REG_TNPDS_HI, (uint32_t)(r->TxRingPhys >> 32));
|
||||||
|
RtlWrite32(r, RTL_REG_THPDS_LO, (uint32_t)r->TxRingPhys);
|
||||||
|
RtlWrite32(r, RTL_REG_THPDS_HI, (uint32_t)(r->TxRingPhys >> 32));
|
||||||
|
RtlWrite32(r, RTL_REG_RDSAR_LO, (uint32_t)r->RxRingPhys);
|
||||||
|
RtlWrite32(r, RTL_REG_RDSAR_HI, (uint32_t)(r->RxRingPhys >> 32));
|
||||||
|
|
||||||
|
// Plain packet mode. Keep C+ features mostly disabled for bring-up.
|
||||||
|
RtlWrite16(r, RTL_REG_CPLUS_CMD, RtlRead16(r, RTL_REG_CPLUS_CMD) & ~(uint16_t)0x00E0u);
|
||||||
|
RtlWrite16(r, RTL_REG_RX_MAXSIZE, RTL_BUF_SIZE);
|
||||||
|
|
||||||
|
RtlWrite32(r, RTL_REG_TX_CONFIG, RTL_TXCFG_IFG | RTL_TXCFG_DMA_UNLIMITED);
|
||||||
|
RtlWrite32(r, RTL_REG_RX_CONFIG,
|
||||||
|
RTL_RXCFG_APM | RTL_RXCFG_AB | RTL_RXCFG_AM | RTL_RXCFG_DMA_UNLIMITED);
|
||||||
|
|
||||||
|
RtlMemoryBarrier();
|
||||||
|
|
||||||
|
// Enable receiver and transmitter.
|
||||||
|
RtlWrite8(r, RTL_REG_CHIP_CMD, RTL_CMD_RX_ENABLE | RTL_CMD_TX_ENABLE);
|
||||||
|
|
||||||
|
// Lock config space again and enable selected interrupts.
|
||||||
|
RtlWrite8(r, RTL_REG_CFG9346, 0x00);
|
||||||
|
RtlWrite16(r, RTL_REG_INTR_STAT, 0xFFFFu);
|
||||||
|
RtlWrite16(r, RTL_REG_INTR_MASK, RTL_INTR_ENABLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlHandleRx(RTL816X_DEVICE* r) {
|
||||||
|
for (uint32_t budget = 0; budget < RTL_RX_COUNT; budget++) {
|
||||||
|
RTL_DESC* d = &r->RxRing[r->RxIndex];
|
||||||
|
uint32_t opts = d->opts1;
|
||||||
|
if (opts & RTL_DESC_OWN)
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint32_t keepEor = opts & RTL_DESC_EOR;
|
||||||
|
uint32_t len = opts & RTL_DESC_LENGTH_MASK;
|
||||||
|
bool good = ((opts & (RTL_RXDESC_RES | RTL_RXDESC_RWT | RTL_RXDESC_CRC | RTL_RXDESC_RUNT)) == 0) &&
|
||||||
|
((opts & (RTL_DESC_FS | RTL_DESC_LS)) == (RTL_DESC_FS | RTL_DESC_LS)) &&
|
||||||
|
len >= NET_ETHERNET_HEADER_LENGTH && len <= RTL_RX_WIRE_MAX;
|
||||||
|
|
||||||
|
if (good) {
|
||||||
|
// Realtek reports the Ethernet FCS in the descriptor length on many
|
||||||
|
// chips. Remove it when present so upper layers see a normal frame.
|
||||||
|
if (len > 4)
|
||||||
|
len -= 4;
|
||||||
|
if (len > NET_ETHERNET_MAX_FRAME) {
|
||||||
|
DbgPrint("RTL816X: dropping oversize frame len=%u\n", len);
|
||||||
|
} else {
|
||||||
|
NetReceiveFrame(&r->NetDevice,
|
||||||
|
r->RxBuffers + r->RxIndex * RTL_BUF_SIZE,
|
||||||
|
len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DbgPrint("RTL816X: rx descriptor %u error opts=0x%08x len=%u\n",
|
||||||
|
r->RxIndex, opts, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
d->opts2 = 0;
|
||||||
|
RtlMemoryBarrier();
|
||||||
|
d->opts1 = RTL_DESC_OWN | RTL_BUF_SIZE | keepEor;
|
||||||
|
RtlMemoryBarrier();
|
||||||
|
|
||||||
|
r->RxIndex = (r->RxIndex + 1) % RTL_RX_COUNT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) {
|
||||||
|
(void)dpc;
|
||||||
|
(void)systemArgument1;
|
||||||
|
(void)systemArgument2;
|
||||||
|
|
||||||
|
RTL816X_DEVICE* r = (RTL816X_DEVICE*)context;
|
||||||
|
uint16_t status = (uint16_t)r->PendingIsr;
|
||||||
|
r->PendingIsr = 0;
|
||||||
|
|
||||||
|
if (status & (RTL_ISR_ROK | RTL_ISR_RER | RTL_ISR_RXOVW | RTL_ISR_FOVW))
|
||||||
|
RtlHandleRx(r);
|
||||||
|
|
||||||
|
if (status & RTL_ISR_LINKCHG) {
|
||||||
|
uint8_t phy = RtlRead8(r, RTL_REG_PHY_STATUS);
|
||||||
|
DbgPrint("RTL816X: link change phy=0x%02x\n", phy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & (RTL_ISR_TER | RTL_ISR_SYSERR))
|
||||||
|
DbgPrint("RTL816X: interrupt status error bits=0x%04x\n", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RtlIsr(void) {
|
||||||
|
if (!gDevicePresent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RTL816X_DEVICE* r = &gDevice;
|
||||||
|
uint16_t status = RtlRead16(r, RTL_REG_INTR_STAT);
|
||||||
|
if (!status || status == 0xFFFFu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RtlWrite16(r, RTL_REG_INTR_STAT, status);
|
||||||
|
r->PendingIsr |= status;
|
||||||
|
KeInsertQueueDpc(&r->Dpc, (void*)0, (void*)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS RtlSendFrame(NET_DEVICE* device, const void* frame, size_t length) {
|
||||||
|
if (!device || !frame)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (length > NET_ETHERNET_MAX_FRAME)
|
||||||
|
return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
if (length < NET_ETHERNET_HEADER_LENGTH)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
RTL816X_DEVICE* r = (RTL816X_DEVICE*)device->DriverContext;
|
||||||
|
if (!r)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&r->TxLock);
|
||||||
|
uint32_t index = r->TxIndex;
|
||||||
|
RTL_DESC* d = &r->TxRing[index];
|
||||||
|
uint32_t opts = d->opts1;
|
||||||
|
if (opts & RTL_DESC_OWN) {
|
||||||
|
KeReleaseSpinlock(&r->TxLock, flags);
|
||||||
|
return STATUS_QUEUE_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t wireLength = length < NET_ETHERNET_MIN_FRAME ? NET_ETHERNET_MIN_FRAME : length;
|
||||||
|
uint8_t* buf = r->TxBuffers + index * RTL_BUF_SIZE;
|
||||||
|
memcpy(buf, frame, length);
|
||||||
|
if (wireLength > length)
|
||||||
|
memset(buf + length, 0, wireLength - length);
|
||||||
|
|
||||||
|
uint32_t eor = opts & RTL_DESC_EOR;
|
||||||
|
d->opts2 = 0;
|
||||||
|
RtlMemoryBarrier();
|
||||||
|
d->opts1 = RTL_DESC_OWN | RTL_DESC_FS | RTL_DESC_LS | eor | (uint32_t)wireLength;
|
||||||
|
RtlMemoryBarrier();
|
||||||
|
RtlWrite8(r, RTL_REG_TX_POLL, RTL_TXPOLL_NPQ);
|
||||||
|
|
||||||
|
r->TxIndex = (index + 1) % RTL_TX_COUNT;
|
||||||
|
KeReleaseSpinlock(&r->TxLock, flags);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS RtlInitialize(const HAL_API* hal) {
|
||||||
|
gHal = hal;
|
||||||
|
memset(&gDevice, 0, sizeof(gDevice));
|
||||||
|
gDevice.TxLock = (SPINLOCK)SPINLOCK_INIT;
|
||||||
|
|
||||||
|
uint16_t deviceId;
|
||||||
|
if (!RtlFindDevice(&gDevice.Bus, &gDevice.Dev, &gDevice.Fn, &deviceId)) {
|
||||||
|
DbgPrint("RTL816X: no RTL8168/RTL8169 PCI device found; skipping\n");
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
gDevice.DeviceId = deviceId;
|
||||||
|
|
||||||
|
DbgPrint("RTL816X: found Realtek PCI device 10ec:%04x at %u:%u.%u\n",
|
||||||
|
deviceId, gDevice.Bus, gDevice.Dev, gDevice.Fn);
|
||||||
|
|
||||||
|
KSTATUS st = RtlSelectRegisterBar(&gDevice);
|
||||||
|
if (!KSUCCESS(st)) {
|
||||||
|
DbgPrint("RTL816X: no usable register BAR (0x%x)\n", st);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlEnablePci(&gDevice);
|
||||||
|
|
||||||
|
gDevice.Irq = hal->Pci.GetIrqLine(gDevice.Bus, gDevice.Dev, gDevice.Fn);
|
||||||
|
if (gDevice.Irq == 0xFFu) {
|
||||||
|
DbgPrint("RTL816X: no legacy PCI IRQ line wired\n");
|
||||||
|
return STATUS_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = RtlReset(&gDevice);
|
||||||
|
if (!KSUCCESS(st)) {
|
||||||
|
DbgPrint("RTL816X: reset timed out\n");
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlReadMac(&gDevice);
|
||||||
|
DbgPrint("RTL816X: MAC %02x:%02x:%02x:%02x:%02x:%02x irq=%u\n",
|
||||||
|
gDevice.NetDevice.HardwareAddress[0], gDevice.NetDevice.HardwareAddress[1],
|
||||||
|
gDevice.NetDevice.HardwareAddress[2], gDevice.NetDevice.HardwareAddress[3],
|
||||||
|
gDevice.NetDevice.HardwareAddress[4], gDevice.NetDevice.HardwareAddress[5],
|
||||||
|
gDevice.Irq);
|
||||||
|
|
||||||
|
st = RtlAllocateDma(&gDevice);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
return st;
|
||||||
|
|
||||||
|
RtlInitDescriptors(&gDevice);
|
||||||
|
KeInitializeDpc(&gDevice.Dpc, RtlDpcRoutine, &gDevice);
|
||||||
|
|
||||||
|
gDevice.NetDevice.Mtu = NET_MTU_ETHERNET;
|
||||||
|
gDevice.NetDevice.DriverContext = &gDevice;
|
||||||
|
gDevice.NetDevice.SendFrame = RtlSendFrame;
|
||||||
|
RtlProgramHardware(&gDevice);
|
||||||
|
|
||||||
|
gDevicePresent = true;
|
||||||
|
KeIrqRegister(gDevice.Irq, RtlIsr);
|
||||||
|
|
||||||
|
st = NetRegisterDevice(&gDevice.NetDevice);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
return st;
|
||||||
|
|
||||||
|
DbgPrint("RTL816X: initialized; RX/TX enabled on IRQ %u\n", gDevice.Irq);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
KSTATUS DriverEntry(const HAL_API* hal) {
|
||||||
|
DbgPrint("RTL816X: driver loading...\n");
|
||||||
|
KSTATUS st = RtlInitialize(hal);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
DbgPrint("RTL816X: init failed: 0x%x\n", st);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
@@ -0,0 +1,620 @@
|
|||||||
|
// ethernet.c - tiny pre-lwIP Ethernet/ARP/IPv4/ICMP shim.
|
||||||
|
//
|
||||||
|
// Goals:
|
||||||
|
// * prove NIC RX/TX works before lwIP is integrated;
|
||||||
|
// * reply to ARP for a configured static IPv4 address;
|
||||||
|
// * reply to ICMP echo requests for that address;
|
||||||
|
// * provide a tiny NetPingIpv4() smoke-test path;
|
||||||
|
// * auto-configure QEMU's default user-mode network only when the MAC looks
|
||||||
|
// like the QEMU OUI (52:54:00), so real hardware is not assigned a bogus IP.
|
||||||
|
//
|
||||||
|
// This is not intended to grow into a full stack. When lwIP arrives, keep the
|
||||||
|
// NET_DEVICE send/receive boundary and replace this protocol code with lwIP's
|
||||||
|
// netif input path.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <ke_debug.h>
|
||||||
|
#include <ke_spinlock.h>
|
||||||
|
#include <ethernet.h>
|
||||||
|
|
||||||
|
#define NET_MAX_ETH_STATES 8
|
||||||
|
#define NET_ARP_CACHE_SIZE 8
|
||||||
|
|
||||||
|
#define ETH_ADDR_LEN NET_ETHERNET_ADDRESS_LENGTH
|
||||||
|
#define ETH_HDR_LEN NET_ETHERNET_HEADER_LENGTH
|
||||||
|
#define ETH_TYPE_IPV4 0x0800u
|
||||||
|
#define ETH_TYPE_ARP 0x0806u
|
||||||
|
|
||||||
|
#define ARP_HTYPE_ETHERNET 0x0001u
|
||||||
|
#define ARP_PTYPE_IPV4 0x0800u
|
||||||
|
#define ARP_OPER_REQUEST 0x0001u
|
||||||
|
#define ARP_OPER_REPLY 0x0002u
|
||||||
|
#define ARP_PACKET_LEN 28u
|
||||||
|
|
||||||
|
#define IPV4_PROTO_ICMP 1u
|
||||||
|
#define ICMP_ECHO_REPLY 0u
|
||||||
|
#define ICMP_ECHO_REQUEST 8u
|
||||||
|
|
||||||
|
#define NET_ETH_MAX_FRAME NET_ETHERNET_MAX_FRAME
|
||||||
|
#define NET_LOG_LIMIT 16u
|
||||||
|
|
||||||
|
static const uint8_t gBroadcastMac[ETH_ADDR_LEN] = {
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct NET_ARP_ENTRY {
|
||||||
|
bool Valid;
|
||||||
|
uint32_t Ip;
|
||||||
|
uint8_t HardwareAddress[ETH_ADDR_LEN];
|
||||||
|
} NET_ARP_ENTRY;
|
||||||
|
|
||||||
|
typedef struct NET_ETH_STATE {
|
||||||
|
bool Present;
|
||||||
|
bool Ipv4Configured;
|
||||||
|
bool GatewayResolved;
|
||||||
|
uint32_t Address;
|
||||||
|
uint32_t Netmask;
|
||||||
|
uint32_t Gateway;
|
||||||
|
uint8_t GatewayHardwareAddress[ETH_ADDR_LEN];
|
||||||
|
NET_ARP_ENTRY ArpCache[NET_ARP_CACHE_SIZE];
|
||||||
|
uint32_t ArpNextReplace;
|
||||||
|
bool PendingPing;
|
||||||
|
uint32_t PendingPingTarget;
|
||||||
|
uint32_t PendingPingNextHop;
|
||||||
|
uint32_t RxFrames;
|
||||||
|
uint32_t TxFrames;
|
||||||
|
uint16_t IcmpEchoSequence;
|
||||||
|
uint32_t ArpFrames;
|
||||||
|
uint32_t Ipv4Frames;
|
||||||
|
uint32_t IcmpFrames;
|
||||||
|
} NET_ETH_STATE;
|
||||||
|
|
||||||
|
static NET_ETH_STATE gEthStates[NET_MAX_ETH_STATES];
|
||||||
|
static SPINLOCK gEthLock = SPINLOCK_INIT;
|
||||||
|
|
||||||
|
static uint16_t NetLoadBe16(const uint8_t* p) {
|
||||||
|
return (uint16_t)(((uint16_t)p[0] << 8) | p[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t NetLoadBe32(const uint8_t* p) {
|
||||||
|
return ((uint32_t)p[0] << 24) |
|
||||||
|
((uint32_t)p[1] << 16) |
|
||||||
|
((uint32_t)p[2] << 8) |
|
||||||
|
(uint32_t)p[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetStoreBe16(uint8_t* p, uint16_t v) {
|
||||||
|
p[0] = (uint8_t)(v >> 8);
|
||||||
|
p[1] = (uint8_t)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetStoreBe32(uint8_t* p, uint32_t v) {
|
||||||
|
p[0] = (uint8_t)(v >> 24);
|
||||||
|
p[1] = (uint8_t)(v >> 16);
|
||||||
|
p[2] = (uint8_t)(v >> 8);
|
||||||
|
p[3] = (uint8_t)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetPrintIpv4(uint32_t ip) {
|
||||||
|
DbgPrint("%u.%u.%u.%u",
|
||||||
|
(unsigned)((ip >> 24) & 0xFFu),
|
||||||
|
(unsigned)((ip >> 16) & 0xFFu),
|
||||||
|
(unsigned)((ip >> 8) & 0xFFu),
|
||||||
|
(unsigned)(ip & 0xFFu));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetPrintMac(const uint8_t* mac) {
|
||||||
|
DbgPrint("%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetHasEthernetAddress(const NET_DEVICE* device) {
|
||||||
|
return device && device->HardwareAddressLength == ETH_ADDR_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetIsQemuMac(const NET_DEVICE* device) {
|
||||||
|
return NetHasEthernetAddress(device) &&
|
||||||
|
device->HardwareAddress[0] == 0x52 &&
|
||||||
|
device->HardwareAddress[1] == 0x54 &&
|
||||||
|
device->HardwareAddress[2] == 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetIsBroadcastMac(const uint8_t* mac) {
|
||||||
|
return mac && memcmp(mac, gBroadcastMac, ETH_ADDR_LEN) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetIsMulticastMac(const uint8_t* mac) {
|
||||||
|
return mac && (mac[0] & 0x01u) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetEthernetFrameIsForDevice(const NET_DEVICE* device, const uint8_t* frame) {
|
||||||
|
if (!NetHasEthernetAddress(device) || !frame)
|
||||||
|
return false;
|
||||||
|
return memcmp(frame, device->HardwareAddress, ETH_ADDR_LEN) == 0 ||
|
||||||
|
NetIsBroadcastMac(frame) ||
|
||||||
|
NetIsMulticastMac(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetIpv4SameSubnet(uint32_t a, uint32_t b, uint32_t mask) {
|
||||||
|
return ((a ^ b) & mask) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NET_ETH_STATE* NetGetEthState(NET_DEVICE* device) {
|
||||||
|
if (!device || device->Index >= NET_MAX_ETH_STATES)
|
||||||
|
return (NET_ETH_STATE*)0;
|
||||||
|
return &gEthStates[device->Index];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t NetChecksum(const uint8_t* data, size_t length) {
|
||||||
|
uint32_t sum = 0;
|
||||||
|
|
||||||
|
while (length >= 2) {
|
||||||
|
sum += NetLoadBe16(data);
|
||||||
|
data += 2;
|
||||||
|
length -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length)
|
||||||
|
sum += (uint16_t)((uint16_t)data[0] << 8);
|
||||||
|
|
||||||
|
while (sum >> 16)
|
||||||
|
sum = (sum & 0xFFFFu) + (sum >> 16);
|
||||||
|
|
||||||
|
return (uint16_t)~sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetBuildEthernetHeader(uint8_t* frame,
|
||||||
|
const uint8_t* dst,
|
||||||
|
const uint8_t* src,
|
||||||
|
uint16_t etherType) {
|
||||||
|
memcpy(frame, dst, ETH_ADDR_LEN);
|
||||||
|
memcpy(frame + ETH_ADDR_LEN, src, ETH_ADDR_LEN);
|
||||||
|
NetStoreBe16(frame + 12, etherType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetArpLookup(NET_ETH_STATE* state, uint32_t ip, uint8_t* macOut) {
|
||||||
|
if (!state)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < NET_ARP_CACHE_SIZE; ++i) {
|
||||||
|
if (state->ArpCache[i].Valid && state->ArpCache[i].Ip == ip) {
|
||||||
|
if (macOut)
|
||||||
|
memcpy(macOut, state->ArpCache[i].HardwareAddress, ETH_ADDR_LEN);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetArpRemember(NET_ETH_STATE* state, uint32_t ip, const uint8_t* mac) {
|
||||||
|
if (!state || !mac || ip == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < NET_ARP_CACHE_SIZE; ++i) {
|
||||||
|
if (state->ArpCache[i].Valid && state->ArpCache[i].Ip == ip) {
|
||||||
|
memcpy(state->ArpCache[i].HardwareAddress, mac, ETH_ADDR_LEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned slot = state->ArpNextReplace % NET_ARP_CACHE_SIZE;
|
||||||
|
state->ArpNextReplace = (state->ArpNextReplace + 1) % NET_ARP_CACHE_SIZE;
|
||||||
|
state->ArpCache[slot].Valid = true;
|
||||||
|
state->ArpCache[slot].Ip = ip;
|
||||||
|
memcpy(state->ArpCache[slot].HardwareAddress, mac, ETH_ADDR_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS NetSendArp(NET_DEVICE* device,
|
||||||
|
uint16_t operation,
|
||||||
|
const uint8_t* targetMac,
|
||||||
|
uint32_t targetIp) {
|
||||||
|
NET_ETH_STATE* state = NetGetEthState(device);
|
||||||
|
if (!state || !state->Ipv4Configured)
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
if (!NetHasEthernetAddress(device))
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (operation == ARP_OPER_REPLY && !targetMac)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
uint8_t frame[ETH_HDR_LEN + ARP_PACKET_LEN];
|
||||||
|
const uint8_t* ethDst = (operation == ARP_OPER_REQUEST) ? gBroadcastMac : targetMac;
|
||||||
|
NetBuildEthernetHeader(frame, ethDst, device->HardwareAddress, ETH_TYPE_ARP);
|
||||||
|
|
||||||
|
uint8_t* arp = frame + ETH_HDR_LEN;
|
||||||
|
NetStoreBe16(arp + 0, ARP_HTYPE_ETHERNET);
|
||||||
|
NetStoreBe16(arp + 2, ARP_PTYPE_IPV4);
|
||||||
|
arp[4] = ETH_ADDR_LEN;
|
||||||
|
arp[5] = 4;
|
||||||
|
NetStoreBe16(arp + 6, operation);
|
||||||
|
memcpy(arp + 8, device->HardwareAddress, ETH_ADDR_LEN);
|
||||||
|
NetStoreBe32(arp + 14, state->Address);
|
||||||
|
|
||||||
|
if (operation == ARP_OPER_REQUEST)
|
||||||
|
memset(arp + 18, 0, ETH_ADDR_LEN);
|
||||||
|
else
|
||||||
|
memcpy(arp + 18, targetMac, ETH_ADDR_LEN);
|
||||||
|
NetStoreBe32(arp + 24, targetIp);
|
||||||
|
|
||||||
|
KSTATUS st = NetSendFrame(device->Index, frame, sizeof(frame));
|
||||||
|
if (KSUCCESS(st))
|
||||||
|
state->TxFrames++;
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetSendArpRequest(NET_DEVICE* device, uint32_t targetIp) {
|
||||||
|
DbgPrint("NET: %s tx ARP who-has ", device->Name);
|
||||||
|
NetPrintIpv4(targetIp);
|
||||||
|
DbgPrint("\n");
|
||||||
|
|
||||||
|
KSTATUS st = NetSendArp(device, ARP_OPER_REQUEST, gBroadcastMac, targetIp);
|
||||||
|
if (!KSUCCESS(st))
|
||||||
|
DbgPrint("NET: %s ARP probe failed: 0x%x\n", device->Name, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS NetSendIcmpEchoRequestResolved(NET_DEVICE* device,
|
||||||
|
NET_ETH_STATE* state,
|
||||||
|
uint32_t targetIp,
|
||||||
|
const uint8_t* nextHopMac) {
|
||||||
|
if (!device || !state || !state->Ipv4Configured || !nextHopMac)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
IPV4_HEADER_LEN = 20,
|
||||||
|
ICMP_HEADER_LEN = 8,
|
||||||
|
ICMP_PAYLOAD_LEN = 32,
|
||||||
|
ICMP_TOTAL_LEN = ICMP_HEADER_LEN + ICMP_PAYLOAD_LEN,
|
||||||
|
IPV4_TOTAL_LEN = IPV4_HEADER_LEN + ICMP_TOTAL_LEN,
|
||||||
|
FRAME_LEN = ETH_HDR_LEN + IPV4_TOTAL_LEN
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t frame[FRAME_LEN];
|
||||||
|
NetBuildEthernetHeader(frame,
|
||||||
|
nextHopMac,
|
||||||
|
device->HardwareAddress,
|
||||||
|
ETH_TYPE_IPV4);
|
||||||
|
|
||||||
|
uint8_t* ip = frame + ETH_HDR_LEN;
|
||||||
|
memset(ip, 0, IPV4_TOTAL_LEN);
|
||||||
|
ip[0] = 0x45; // IPv4, 20-byte header.
|
||||||
|
ip[1] = 0; // DSCP/ECN.
|
||||||
|
NetStoreBe16(ip + 2, IPV4_TOTAL_LEN);
|
||||||
|
NetStoreBe16(ip + 4, state->IcmpEchoSequence);
|
||||||
|
NetStoreBe16(ip + 6, 0x4000); // Don't fragment.
|
||||||
|
ip[8] = 64; // TTL.
|
||||||
|
ip[9] = IPV4_PROTO_ICMP;
|
||||||
|
NetStoreBe32(ip + 12, state->Address);
|
||||||
|
NetStoreBe32(ip + 16, targetIp);
|
||||||
|
NetStoreBe16(ip + 10, NetChecksum(ip, IPV4_HEADER_LEN));
|
||||||
|
|
||||||
|
uint8_t* icmp = ip + IPV4_HEADER_LEN;
|
||||||
|
icmp[0] = ICMP_ECHO_REQUEST;
|
||||||
|
icmp[1] = 0;
|
||||||
|
NetStoreBe16(icmp + 4, 0x4b4e); // temporary pre-lwIP ping ID: 'KN'.
|
||||||
|
NetStoreBe16(icmp + 6, state->IcmpEchoSequence);
|
||||||
|
for (unsigned i = 0; i < ICMP_PAYLOAD_LEN; ++i)
|
||||||
|
icmp[ICMP_HEADER_LEN + i] = (uint8_t)('A' + (i % 26));
|
||||||
|
NetStoreBe16(icmp + 2, NetChecksum(icmp, ICMP_TOTAL_LEN));
|
||||||
|
|
||||||
|
DbgPrint("NET: %s tx ICMP echo request to ", device->Name);
|
||||||
|
NetPrintIpv4(targetIp);
|
||||||
|
DbgPrint(" seq=%u\n", (unsigned)state->IcmpEchoSequence);
|
||||||
|
|
||||||
|
KSTATUS st = NetSendFrame(device->Index, frame, FRAME_LEN);
|
||||||
|
if (KSUCCESS(st)) {
|
||||||
|
state->TxFrames++;
|
||||||
|
state->IcmpEchoSequence++;
|
||||||
|
} else {
|
||||||
|
DbgPrint("NET: %s ICMP echo request failed: 0x%x\n", device->Name, st);
|
||||||
|
}
|
||||||
|
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KSTATUS NetPingIpv4OnDevice(NET_DEVICE* device,
|
||||||
|
NET_ETH_STATE* state,
|
||||||
|
uint32_t targetIp) {
|
||||||
|
if (!device || !state || !state->Ipv4Configured || targetIp == 0)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
uint32_t nextHop = targetIp;
|
||||||
|
if (!NetIpv4SameSubnet(state->Address, targetIp, state->Netmask)) {
|
||||||
|
if (state->Gateway == 0)
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
nextHop = state->Gateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t nextHopMac[ETH_ADDR_LEN];
|
||||||
|
if (NetArpLookup(state, nextHop, nextHopMac))
|
||||||
|
return NetSendIcmpEchoRequestResolved(device, state, targetIp, nextHopMac);
|
||||||
|
|
||||||
|
state->PendingPing = true;
|
||||||
|
state->PendingPingTarget = targetIp;
|
||||||
|
state->PendingPingNextHop = nextHop;
|
||||||
|
|
||||||
|
DbgPrint("NET: %s ping ", device->Name);
|
||||||
|
NetPrintIpv4(targetIp);
|
||||||
|
DbgPrint(" waiting for ARP ");
|
||||||
|
NetPrintIpv4(nextHop);
|
||||||
|
DbgPrint("\n");
|
||||||
|
|
||||||
|
NetSendArpRequest(device, nextHop);
|
||||||
|
return STATUS_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetHandleArp(NET_DEVICE* device, NET_ETH_STATE* state,
|
||||||
|
const uint8_t* frame, size_t length) {
|
||||||
|
if (length < ETH_HDR_LEN + ARP_PACKET_LEN)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint8_t* arp = frame + ETH_HDR_LEN;
|
||||||
|
if (NetLoadBe16(arp + 0) != ARP_HTYPE_ETHERNET ||
|
||||||
|
NetLoadBe16(arp + 2) != ARP_PTYPE_IPV4 ||
|
||||||
|
arp[4] != ETH_ADDR_LEN || arp[5] != 4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint16_t op = NetLoadBe16(arp + 6);
|
||||||
|
const uint8_t* senderMac = arp + 8;
|
||||||
|
uint32_t senderIp = NetLoadBe32(arp + 14);
|
||||||
|
uint32_t targetIp = NetLoadBe32(arp + 24);
|
||||||
|
state->ArpFrames++;
|
||||||
|
|
||||||
|
if (state->ArpFrames <= NET_LOG_LIMIT) {
|
||||||
|
DbgPrint("NET: %s rx ARP %s ",
|
||||||
|
device->Name,
|
||||||
|
op == ARP_OPER_REQUEST ? "request" :
|
||||||
|
op == ARP_OPER_REPLY ? "reply" : "op");
|
||||||
|
NetPrintIpv4(senderIp);
|
||||||
|
DbgPrint(" ");
|
||||||
|
NetPrintMac(senderMac);
|
||||||
|
DbgPrint(" -> ");
|
||||||
|
NetPrintIpv4(targetIp);
|
||||||
|
DbgPrint("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((op == ARP_OPER_REQUEST || op == ARP_OPER_REPLY) && senderIp != 0)
|
||||||
|
NetArpRemember(state, senderIp, senderMac);
|
||||||
|
|
||||||
|
if (op == ARP_OPER_REPLY && state->Ipv4Configured && senderIp == state->Gateway) {
|
||||||
|
memcpy(state->GatewayHardwareAddress, senderMac, ETH_ADDR_LEN);
|
||||||
|
state->GatewayResolved = true;
|
||||||
|
DbgPrint("NET: %s gateway resolved ", device->Name);
|
||||||
|
NetPrintIpv4(senderIp);
|
||||||
|
DbgPrint(" = ");
|
||||||
|
NetPrintMac(senderMac);
|
||||||
|
DbgPrint("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == ARP_OPER_REPLY && state->PendingPing && senderIp == state->PendingPingNextHop) {
|
||||||
|
uint32_t pingTarget = state->PendingPingTarget;
|
||||||
|
state->PendingPing = false;
|
||||||
|
(void)NetSendIcmpEchoRequestResolved(device, state, pingTarget, senderMac);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == ARP_OPER_REQUEST && state->Ipv4Configured && targetIp == state->Address) {
|
||||||
|
KSTATUS st = NetSendArp(device, ARP_OPER_REPLY, senderMac, senderIp);
|
||||||
|
if (KSUCCESS(st)) {
|
||||||
|
DbgPrint("NET: %s tx ARP reply for ", device->Name);
|
||||||
|
NetPrintIpv4(state->Address);
|
||||||
|
DbgPrint("\n");
|
||||||
|
} else {
|
||||||
|
DbgPrint("NET: %s ARP reply failed: 0x%x\n", device->Name, st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetHandleIcmpEcho(NET_DEVICE* device, NET_ETH_STATE* state,
|
||||||
|
const uint8_t* frame, size_t length,
|
||||||
|
const uint8_t* ip, size_t ipHeaderLength,
|
||||||
|
size_t totalLength) {
|
||||||
|
(void)length;
|
||||||
|
|
||||||
|
if (totalLength < ipHeaderLength + 8)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint8_t* icmp = ip + ipHeaderLength;
|
||||||
|
size_t icmpLength = totalLength - ipHeaderLength;
|
||||||
|
if (NetChecksum(icmp, icmpLength) != 0)
|
||||||
|
return;
|
||||||
|
if (icmp[0] != ICMP_ECHO_REQUEST || icmp[1] != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ETH_HDR_LEN + totalLength > NET_ETH_MAX_FRAME)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t out[NET_ETH_MAX_FRAME];
|
||||||
|
NetBuildEthernetHeader(out, frame + 6, device->HardwareAddress, ETH_TYPE_IPV4);
|
||||||
|
memcpy(out + ETH_HDR_LEN, ip, totalLength);
|
||||||
|
|
||||||
|
uint8_t* outIp = out + ETH_HDR_LEN;
|
||||||
|
uint8_t* outIcmp = outIp + ipHeaderLength;
|
||||||
|
|
||||||
|
// Swap IPv4 endpoints and rebuild the IPv4 header checksum.
|
||||||
|
memcpy(outIp + 12, ip + 16, 4);
|
||||||
|
memcpy(outIp + 16, ip + 12, 4);
|
||||||
|
outIp[8] = 64;
|
||||||
|
outIp[10] = 0;
|
||||||
|
outIp[11] = 0;
|
||||||
|
NetStoreBe16(outIp + 10, NetChecksum(outIp, ipHeaderLength));
|
||||||
|
|
||||||
|
// Convert echo request to echo reply and rebuild the ICMP checksum.
|
||||||
|
outIcmp[0] = ICMP_ECHO_REPLY;
|
||||||
|
outIcmp[2] = 0;
|
||||||
|
outIcmp[3] = 0;
|
||||||
|
NetStoreBe16(outIcmp + 2, NetChecksum(outIcmp, icmpLength));
|
||||||
|
|
||||||
|
KSTATUS st = NetSendFrame(device->Index, out, ETH_HDR_LEN + totalLength);
|
||||||
|
if (KSUCCESS(st)) {
|
||||||
|
state->TxFrames++;
|
||||||
|
if (state->IcmpFrames <= NET_LOG_LIMIT) {
|
||||||
|
DbgPrint("NET: %s tx ICMP echo reply to ", device->Name);
|
||||||
|
NetPrintIpv4(NetLoadBe32(ip + 12));
|
||||||
|
DbgPrint("\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DbgPrint("NET: %s ICMP reply failed: 0x%x\n", device->Name, st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetHandleIpv4(NET_DEVICE* device, NET_ETH_STATE* state,
|
||||||
|
const uint8_t* frame, size_t length) {
|
||||||
|
if (!state->Ipv4Configured || length < ETH_HDR_LEN + 20)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint8_t* ip = frame + ETH_HDR_LEN;
|
||||||
|
uint8_t version = (uint8_t)(ip[0] >> 4);
|
||||||
|
size_t ipHeaderLength = (size_t)(ip[0] & 0x0Fu) * 4u;
|
||||||
|
if (version != 4 || ipHeaderLength < 20 || length < ETH_HDR_LEN + ipHeaderLength)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t totalLength = NetLoadBe16(ip + 2);
|
||||||
|
if (totalLength < ipHeaderLength || length < ETH_HDR_LEN + totalLength)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (NetChecksum(ip, ipHeaderLength) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint16_t fragment = NetLoadBe16(ip + 6);
|
||||||
|
if (fragment & 0x3FFFu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t dstIp = NetLoadBe32(ip + 16);
|
||||||
|
if (dstIp != state->Address)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state->Ipv4Frames++;
|
||||||
|
if (ip[9] == IPV4_PROTO_ICMP) {
|
||||||
|
if (totalLength < ipHeaderLength + 8)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint8_t* icmp = ip + ipHeaderLength;
|
||||||
|
size_t icmpLength = totalLength - ipHeaderLength;
|
||||||
|
if (NetChecksum(icmp, icmpLength) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state->IcmpFrames++;
|
||||||
|
|
||||||
|
if (icmp[0] == ICMP_ECHO_REPLY && icmp[1] == 0) {
|
||||||
|
DbgPrint("NET: %s rx ICMP echo reply from ", device->Name);
|
||||||
|
NetPrintIpv4(NetLoadBe32(ip + 12));
|
||||||
|
DbgPrint(" seq=%u\n", (unsigned)NetLoadBe16(icmp + 6));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->IcmpFrames <= NET_LOG_LIMIT) {
|
||||||
|
DbgPrint("NET: %s rx ICMP type=%u code=%u from ",
|
||||||
|
device->Name,
|
||||||
|
(unsigned)icmp[0],
|
||||||
|
(unsigned)icmp[1]);
|
||||||
|
NetPrintIpv4(NetLoadBe32(ip + 12));
|
||||||
|
DbgPrint("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
NetHandleIcmpEcho(device, state, frame, length, ip, ipHeaderLength, totalLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) KSTATUS NetConfigureIpv4Static(uint32_t deviceIndex,
|
||||||
|
uint32_t address,
|
||||||
|
uint32_t netmask,
|
||||||
|
uint32_t gateway) {
|
||||||
|
if (deviceIndex >= NET_MAX_ETH_STATES || address == 0)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
NET_DEVICE* device = NetGetDevice(deviceIndex);
|
||||||
|
if (!device)
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
if (!NetHasEthernetAddress(device))
|
||||||
|
return STATUS_NOT_SUPPORTED;
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gEthLock);
|
||||||
|
NET_ETH_STATE* state = &gEthStates[deviceIndex];
|
||||||
|
state->Ipv4Configured = true;
|
||||||
|
state->GatewayResolved = false;
|
||||||
|
state->PendingPing = false;
|
||||||
|
state->IcmpEchoSequence = 1;
|
||||||
|
state->Address = address;
|
||||||
|
state->Netmask = netmask;
|
||||||
|
state->Gateway = gateway;
|
||||||
|
state->ArpNextReplace = 0;
|
||||||
|
memset(state->GatewayHardwareAddress, 0, sizeof(state->GatewayHardwareAddress));
|
||||||
|
memset(state->ArpCache, 0, sizeof(state->ArpCache));
|
||||||
|
KeReleaseSpinlock(&gEthLock, flags);
|
||||||
|
|
||||||
|
DbgPrint("NET: eth%u IPv4 ", deviceIndex);
|
||||||
|
NetPrintIpv4(address);
|
||||||
|
DbgPrint(" mask ");
|
||||||
|
NetPrintIpv4(netmask);
|
||||||
|
DbgPrint(" gw ");
|
||||||
|
NetPrintIpv4(gateway);
|
||||||
|
DbgPrint("\n");
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) KSTATUS NetPingIpv4(uint32_t deviceIndex, uint32_t targetIp) {
|
||||||
|
NET_DEVICE* device = NetGetDevice(deviceIndex);
|
||||||
|
if (!device || deviceIndex >= NET_MAX_ETH_STATES)
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
|
||||||
|
NET_ETH_STATE* state = &gEthStates[deviceIndex];
|
||||||
|
if (!state->Present || !state->Ipv4Configured)
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
|
||||||
|
return NetPingIpv4OnDevice(device, state, targetIp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetEthernetAttachDevice(NET_DEVICE* device) {
|
||||||
|
if (!device || device->Index >= NET_MAX_ETH_STATES)
|
||||||
|
return;
|
||||||
|
if (!NetHasEthernetAddress(device)) {
|
||||||
|
DbgPrint("NET: %s has unsupported hardware address length %u; Ethernet shim disabled\n",
|
||||||
|
device->Name, (unsigned)device->HardwareAddressLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gEthLock);
|
||||||
|
NET_ETH_STATE* state = &gEthStates[device->Index];
|
||||||
|
memset(state, 0, sizeof(*state));
|
||||||
|
state->Present = true;
|
||||||
|
KeReleaseSpinlock(&gEthLock, flags);
|
||||||
|
|
||||||
|
if (NetIsQemuMac(device)) {
|
||||||
|
NetConfigureIpv4Static(device->Index,
|
||||||
|
NET_IPV4_ADDR(10, 0, 2, 15),
|
||||||
|
NET_IPV4_ADDR(255, 255, 255, 0),
|
||||||
|
NET_IPV4_ADDR(10, 0, 2, 2));
|
||||||
|
state = &gEthStates[device->Index];
|
||||||
|
(void)NetPingIpv4OnDevice(device, state, state->Gateway);
|
||||||
|
} else {
|
||||||
|
DbgPrint("NET: %s has no IPv4 address yet; ARP/ICMP replies disabled\n",
|
||||||
|
device->Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetEthernetReceive(NET_DEVICE* device, const void* frame, size_t length) {
|
||||||
|
NET_ETH_STATE* state = NetGetEthState(device);
|
||||||
|
if (!state || !state->Present || !frame || length < ETH_HDR_LEN)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint8_t* b = (const uint8_t*)frame;
|
||||||
|
if (!NetEthernetFrameIsForDevice(device, b))
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint16_t etherType = NetLoadBe16(b + 12);
|
||||||
|
state->RxFrames++;
|
||||||
|
|
||||||
|
switch (etherType) {
|
||||||
|
case ETH_TYPE_ARP:
|
||||||
|
NetHandleArp(device, state, b, length);
|
||||||
|
break;
|
||||||
|
case ETH_TYPE_IPV4:
|
||||||
|
NetHandleIpv4(device, state, b, length);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
// ethernet.h - tiny pre-lwIP Ethernet/ARP/ICMP shim.
|
||||||
|
//
|
||||||
|
// This is deliberately small: it gives NIC drivers something useful to feed
|
||||||
|
// while lwIP is not wired up yet. Keep the public surface generic so the lwIP
|
||||||
|
// netif glue can reuse NetSendFrame() and bypass this file later.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <ke_status.h>
|
||||||
|
#include <net_device.h>
|
||||||
|
|
||||||
|
#define NET_IPV4_ADDR(a, b, c, d) \
|
||||||
|
((((uint32_t)(a) & 0xFFu) << 24) | \
|
||||||
|
(((uint32_t)(b) & 0xFFu) << 16) | \
|
||||||
|
(((uint32_t)(c) & 0xFFu) << 8) | \
|
||||||
|
((uint32_t)(d) & 0xFFu))
|
||||||
|
|
||||||
|
__declspec(dllexport) KSTATUS NetConfigureIpv4Static(uint32_t deviceIndex,
|
||||||
|
uint32_t address,
|
||||||
|
uint32_t netmask,
|
||||||
|
uint32_t gateway);
|
||||||
|
__declspec(dllexport) KSTATUS NetPingIpv4(uint32_t deviceIndex, uint32_t targetIp);
|
||||||
|
|
||||||
|
void NetEthernetAttachDevice(NET_DEVICE* device);
|
||||||
|
void NetEthernetReceive(NET_DEVICE* device, const void* frame, size_t length);
|
||||||
+28
-4
@@ -4,10 +4,17 @@
|
|||||||
#include <ke_irql.h>
|
#include <ke_irql.h>
|
||||||
#include <ke_dpc.h>
|
#include <ke_dpc.h>
|
||||||
#include <hal_api.h>
|
#include <hal_api.h>
|
||||||
|
#include <ke_debug.h>
|
||||||
|
|
||||||
#define MAX_IRQS 24
|
#define MAX_IRQS 24
|
||||||
|
#define MAX_IRQ_HANDLERS 8
|
||||||
|
|
||||||
static void (*gIrqHandlers[MAX_IRQS])(void);
|
typedef struct IRQ_HANDLER_LIST {
|
||||||
|
void (*Handlers[MAX_IRQ_HANDLERS])(void);
|
||||||
|
uint8_t Count;
|
||||||
|
} IRQ_HANDLER_LIST;
|
||||||
|
|
||||||
|
static IRQ_HANDLER_LIST gIrqHandlers[MAX_IRQS];
|
||||||
static const HAL_API *gHal;
|
static const HAL_API *gHal;
|
||||||
static volatile uint32_t gInterruptDepth = 0;
|
static volatile uint32_t gInterruptDepth = 0;
|
||||||
|
|
||||||
@@ -24,7 +31,19 @@ void KeIrqInitialize(const HAL_API *hal) {
|
|||||||
|
|
||||||
__declspec(dllexport) void KeIrqRegister(uint8_t irq, void (*handler)(void)) {
|
__declspec(dllexport) void KeIrqRegister(uint8_t irq, void (*handler)(void)) {
|
||||||
if (irq >= MAX_IRQS || !handler) return;
|
if (irq >= MAX_IRQS || !handler) return;
|
||||||
gIrqHandlers[irq] = handler;
|
|
||||||
|
IRQ_HANDLER_LIST* list = &gIrqHandlers[irq];
|
||||||
|
for (uint8_t i = 0; i < list->Count; i++) {
|
||||||
|
if (list->Handlers[i] == handler)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list->Count >= MAX_IRQ_HANDLERS) {
|
||||||
|
DbgPrint("KeIrqRegister: IRQ %u handler table full\n", irq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list->Handlers[list->Count++] = handler;
|
||||||
gHal->Intc.RouteIrq(irq);
|
gHal->Intc.RouteIrq(irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,8 +59,13 @@ void KeIrqDispatch(uint8_t irq) {
|
|||||||
|
|
||||||
KIRQL oldIrql = KeRaiseIrql(targetIrql);
|
KIRQL oldIrql = KeRaiseIrql(targetIrql);
|
||||||
|
|
||||||
if (irq < MAX_IRQS && gIrqHandlers[irq])
|
if (irq < MAX_IRQS) {
|
||||||
gIrqHandlers[irq]();
|
IRQ_HANDLER_LIST* list = &gIrqHandlers[irq];
|
||||||
|
for (uint8_t i = 0; i < list->Count; i++) {
|
||||||
|
if (list->Handlers[i])
|
||||||
|
list->Handlers[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handlers are no longer allowed to context-switch directly. Timer IRQs
|
// Handlers are no longer allowed to context-switch directly. Timer IRQs
|
||||||
// only request rescheduling; KiExitInterrupt performs the switch after this
|
// only request rescheduling; KiExitInterrupt performs the switch after this
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
// HAL pointer - set once at boot by KeSyscallSetHal()
|
// HAL pointer - set once at boot by KeSyscallSetHal()
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
static const HAL_API* gHal;
|
static const HAL_API* gHal;
|
||||||
|
static uint64_t (*gCatchAllDebugBucket)(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5);
|
||||||
|
|
||||||
typedef struct SYS_IO_STATUS_BLOCK_K {
|
typedef struct SYS_IO_STATUS_BLOCK_K {
|
||||||
volatile uint32_t Status;
|
volatile uint32_t Status;
|
||||||
@@ -84,7 +85,10 @@ typedef struct SYS_USER_ASYNC_APC_CONTEXT {
|
|||||||
void* UserContext;
|
void* UserContext;
|
||||||
} SYS_USER_ASYNC_APC_CONTEXT;
|
} SYS_USER_ASYNC_APC_CONTEXT;
|
||||||
|
|
||||||
void KeSyscallSetHal(const HAL_API* hal) { gHal = hal; }
|
void KeSyscallInit(const HAL_API* hal, uint64_t (*CatchAllDebugBucket)(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5)) {
|
||||||
|
gHal = hal;
|
||||||
|
gCatchAllDebugBucket = CatchAllDebugBucket;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Helper: look up a handle and type-check it in one call.
|
// Helper: look up a handle and type-check it in one call.
|
||||||
@@ -1494,6 +1498,11 @@ static uint64_t SyscallRemoveDir(uint64_t a0, uint64_t a1, uint64_t a2,
|
|||||||
return IoRemoveDir(path);
|
return IoRemoveDir(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t SyscallCatchAllDebugBucket(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||||
|
uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||||
|
return gCatchAllDebugBucket(a0, a1, a2, a3, a4, a5);
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Dispatch table - index must match SYS_* in syscall_nr.h exactly.
|
// Dispatch table - index must match SYS_* in syscall_nr.h exactly.
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -1576,6 +1585,7 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
|
|||||||
/* 73 SYS_GET_TICK */ SyscallGetTick,
|
/* 73 SYS_GET_TICK */ SyscallGetTick,
|
||||||
/* 74 SYS_DEBUG_PRINT */ SyscallDebugPrint,
|
/* 74 SYS_DEBUG_PRINT */ SyscallDebugPrint,
|
||||||
/* 75 SYS_QUERY_TRAP_FRAME */ SyscallQueryTrapFrame,
|
/* 75 SYS_QUERY_TRAP_FRAME */ SyscallQueryTrapFrame,
|
||||||
|
/* 76 SYS_CATCHALLDEBUGBUCKET */ SyscallCatchAllDebugBucket
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <hal_api.h>
|
#include <hal_api.h>
|
||||||
|
|
||||||
// Set the HAL API pointer for syscalls
|
// Set the HAL API pointer for syscalls
|
||||||
void KeSyscallSetHal(const HAL_API* hal);
|
void KeSyscallInit(const HAL_API* hal, uint64_t (*CatchAllDebugBucket)(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5));
|
||||||
|
|
||||||
// Syscall dispatcher, called from assembly entry point
|
// Syscall dispatcher, called from assembly entry point
|
||||||
uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1,
|
uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1,
|
||||||
|
|||||||
+64
-5
@@ -13,6 +13,7 @@
|
|||||||
#include <ke_irq.h>
|
#include <ke_irq.h>
|
||||||
#include <ke_dpc.h>
|
#include <ke_dpc.h>
|
||||||
#include <ke_syscall.h>
|
#include <ke_syscall.h>
|
||||||
|
#include <ethernet.h>
|
||||||
#include <ke_debug.h>
|
#include <ke_debug.h>
|
||||||
#include <ke_util.h>
|
#include <ke_util.h>
|
||||||
#include <ldr_image.h>
|
#include <ldr_image.h>
|
||||||
@@ -401,10 +402,15 @@ void SystemInitThread(void* arg) {
|
|||||||
args->hal->Screen.Clear(0x000000);
|
args->hal->Screen.Clear(0x000000);
|
||||||
|
|
||||||
// Print memory status now that the system is fully alive
|
// Print memory status now that the system is fully alive
|
||||||
DbgPrint("Memory Status:\n");
|
DbgPrint(
|
||||||
DbgPrint(" -> Free (PMM): %.2f MB\n", (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f));
|
"Memory Status:\n"
|
||||||
DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f));
|
" -> Free (PMM): %.2f MB\n"
|
||||||
DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f));
|
" -> Kernel Pool: %.2f MB\n"
|
||||||
|
" -> User Heap: %.2f MB\n",
|
||||||
|
(float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f),
|
||||||
|
(float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f),
|
||||||
|
(float)MmGetUserHeapUsage() / (1024.0f * 1024.0f)
|
||||||
|
);
|
||||||
|
|
||||||
if (args->bootInfo->Debug & 2)
|
if (args->bootInfo->Debug & 2)
|
||||||
KiRunKernelSelfTests(args->hal);
|
KiRunKernelSelfTests(args->hal);
|
||||||
@@ -432,6 +438,59 @@ void SystemInitThread(void* arg) {
|
|||||||
MmFreePool(args);
|
MmFreePool(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for debug! for science!
|
||||||
|
uint64_t KeCatchAllDebugBucket(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||||
|
if (a0 == 0) {
|
||||||
|
DbgPrint(
|
||||||
|
"Memory Status:\n"
|
||||||
|
" -> Free (PMM): %.2f MB\n"
|
||||||
|
" -> Kernel Pool: %.2f MB\n"
|
||||||
|
" -> User Heap: %.2f MB\n",
|
||||||
|
(float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f),
|
||||||
|
(float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f),
|
||||||
|
(float)MmGetUserHeapUsage() / (1024.0f * 1024.0f)
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a0 == 1) {
|
||||||
|
uint32_t deviceIndex = (uint32_t)a1;
|
||||||
|
uint32_t targetIp;
|
||||||
|
|
||||||
|
// Usage from shell debug mode:
|
||||||
|
// 1,0,10,0,2,2 ping 10.0.2.2 on eth0
|
||||||
|
// 1,0,167772674,0,0,0 ping packed IPv4 0x0A000202 on eth0
|
||||||
|
// 1,0,0,0,0,0 ping the default QEMU gateway 10.0.2.2
|
||||||
|
if (a2 == 0 && a3 == 0 && a4 == 0 && a5 == 0) {
|
||||||
|
targetIp = NET_IPV4_ADDR(10, 0, 2, 2);
|
||||||
|
} else if (a3 == 0 && a4 == 0 && a5 == 0 && a2 > 0xFFu) {
|
||||||
|
targetIp = (uint32_t)a2;
|
||||||
|
} else {
|
||||||
|
if (a2 > 0xFFu || a3 > 0xFFu || a4 > 0xFFu || a5 > 0xFFu) {
|
||||||
|
DbgPrint("DebugBucket[1]: invalid IPv4 octet(s): %llu.%llu.%llu.%llu\n",
|
||||||
|
a2, a3, a4, a5);
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
targetIp = NET_IPV4_ADDR((uint8_t)a2, (uint8_t)a3,
|
||||||
|
(uint8_t)a4, (uint8_t)a5);
|
||||||
|
}
|
||||||
|
|
||||||
|
DbgPrint("DebugBucket[1]: ping eth%u %u.%u.%u.%u\n",
|
||||||
|
deviceIndex,
|
||||||
|
(targetIp >> 24) & 0xFFu,
|
||||||
|
(targetIp >> 16) & 0xFFu,
|
||||||
|
(targetIp >> 8) & 0xFFu,
|
||||||
|
targetIp & 0xFFu);
|
||||||
|
|
||||||
|
KSTATUS status = NetPingIpv4(deviceIndex, targetIp);
|
||||||
|
DbgPrint("DebugBucket[1]: NetPingIpv4 returned 0x%X\n", status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
DbgPrint("DebugBucket: unknown command %llu\n", a0);
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Main Kernel Entry
|
// Main Kernel Entry
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -495,7 +554,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
|||||||
|
|
||||||
HalRegisterScheduler(KeSchedulerTick);
|
HalRegisterScheduler(KeSchedulerTick);
|
||||||
|
|
||||||
KeSyscallSetHal(hal);
|
KeSyscallInit(hal, KeCatchAllDebugBucket);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Dynamic Linking & Driver Execution
|
// Dynamic Linking & Driver Execution
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
// net_device.c - minimal network-device registry and RX handoff.
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <net_device.h>
|
||||||
|
#include <ethernet.h>
|
||||||
|
#include <ke_debug.h>
|
||||||
|
#include <ke_spinlock.h>
|
||||||
|
|
||||||
|
#define NET_MAX_DEVICES 8u
|
||||||
|
#define NET_RX_LOG_LIMIT 16u
|
||||||
|
|
||||||
|
static NET_DEVICE* gNetDevices[NET_MAX_DEVICES];
|
||||||
|
static uint32_t gNetDeviceCount = 0;
|
||||||
|
static SPINLOCK gNetLock = SPINLOCK_INIT;
|
||||||
|
|
||||||
|
static void NetMakeDefaultName(NET_DEVICE* device, uint32_t index) {
|
||||||
|
if (!device || device->Name[0])
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The registry is currently capped at eight devices, so ethN is enough and
|
||||||
|
// keeps the name stable for early boot diagnostics.
|
||||||
|
device->Name[0] = 'e';
|
||||||
|
device->Name[1] = 't';
|
||||||
|
device->Name[2] = 'h';
|
||||||
|
device->Name[3] = (char)('0' + (index % 10u));
|
||||||
|
device->Name[4] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetIsDeviceRegisteredLocked(const NET_DEVICE* device) {
|
||||||
|
for (uint32_t i = 0; i < gNetDeviceCount; ++i) {
|
||||||
|
if (gNetDevices[i] == device)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NetDeviceHasEthernetAddress(const NET_DEVICE* device) {
|
||||||
|
return device && device->HardwareAddressLength == NET_ETHERNET_ADDRESS_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NetLogHardwareAddress(const NET_DEVICE* device) {
|
||||||
|
if (NetDeviceHasEthernetAddress(device)) {
|
||||||
|
DbgPrint("%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
device->HardwareAddress[0], device->HardwareAddress[1],
|
||||||
|
device->HardwareAddress[2], device->HardwareAddress[3],
|
||||||
|
device->HardwareAddress[4], device->HardwareAddress[5]);
|
||||||
|
} else {
|
||||||
|
DbgPrint("hwlen=%u", (unsigned)device->HardwareAddressLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) KSTATUS NetRegisterDevice(NET_DEVICE* device) {
|
||||||
|
if (!device || !device->SendFrame)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (device->HardwareAddressLength > NET_HWADDR_MAX)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
||||||
|
if (NetIsDeviceRegisteredLocked(device)) {
|
||||||
|
KeReleaseSpinlock(&gNetLock, flags);
|
||||||
|
return STATUS_ALREADY_EXISTS;
|
||||||
|
}
|
||||||
|
if (gNetDeviceCount >= NET_MAX_DEVICES) {
|
||||||
|
KeReleaseSpinlock(&gNetLock, flags);
|
||||||
|
return STATUS_QUEUE_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t index = gNetDeviceCount;
|
||||||
|
device->Index = index;
|
||||||
|
if (device->Mtu == 0)
|
||||||
|
device->Mtu = NET_MTU_ETHERNET;
|
||||||
|
memset(&device->Stats, 0, sizeof(device->Stats));
|
||||||
|
NetMakeDefaultName(device, index);
|
||||||
|
gNetDevices[index] = device;
|
||||||
|
gNetDeviceCount++;
|
||||||
|
KeReleaseSpinlock(&gNetLock, flags);
|
||||||
|
|
||||||
|
DbgPrint("NET: registered %s index=%u mac=", device->Name, device->Index);
|
||||||
|
NetLogHardwareAddress(device);
|
||||||
|
DbgPrint(" mtu=%u\n", device->Mtu);
|
||||||
|
|
||||||
|
NetEthernetAttachDevice(device);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) uint32_t NetGetDeviceCount(void) {
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
||||||
|
uint32_t count = gNetDeviceCount;
|
||||||
|
KeReleaseSpinlock(&gNetLock, flags);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) NET_DEVICE* NetGetDevice(uint32_t index) {
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
||||||
|
NET_DEVICE* device = (index < gNetDeviceCount) ? gNetDevices[index] : (NET_DEVICE*)0;
|
||||||
|
KeReleaseSpinlock(&gNetLock, flags);
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) KSTATUS NetGetDeviceStats(uint32_t index,
|
||||||
|
NET_DEVICE_STATS* stats) {
|
||||||
|
if (!stats)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
NET_DEVICE* device = NetGetDevice(index);
|
||||||
|
if (!device)
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
||||||
|
memcpy(stats, &device->Stats, sizeof(*stats));
|
||||||
|
KeReleaseSpinlock(&gNetLock, flags);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) KSTATUS NetSendFrame(uint32_t index,
|
||||||
|
const void* frame,
|
||||||
|
size_t length) {
|
||||||
|
NET_DEVICE* device = NetGetDevice(index);
|
||||||
|
if (!device || !device->SendFrame)
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
if (!frame || length < NET_ETHERNET_HEADER_LENGTH)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (length > (size_t)device->Mtu + NET_ETHERNET_HEADER_LENGTH)
|
||||||
|
return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
|
KSTATUS st = device->SendFrame(device, frame, length);
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
||||||
|
if (KSUCCESS(st)) {
|
||||||
|
device->Stats.TxPackets++;
|
||||||
|
device->Stats.TxBytes += length;
|
||||||
|
} else {
|
||||||
|
device->Stats.TxErrors++;
|
||||||
|
}
|
||||||
|
KeReleaseSpinlock(&gNetLock, flags);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(dllexport) void NetReceiveFrame(NET_DEVICE* device,
|
||||||
|
const void* frame,
|
||||||
|
size_t length) {
|
||||||
|
static volatile uint32_t sLoggedFrames = 0;
|
||||||
|
|
||||||
|
if (!device || !frame || length < NET_ETHERNET_HEADER_LENGTH) {
|
||||||
|
if (device) {
|
||||||
|
uintptr_t dropFlags = KeAcquireSpinlock(&gNetLock);
|
||||||
|
device->Stats.RxDrops++;
|
||||||
|
KeReleaseSpinlock(&gNetLock, dropFlags);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t flags = KeAcquireSpinlock(&gNetLock);
|
||||||
|
device->Stats.RxPackets++;
|
||||||
|
device->Stats.RxBytes += length;
|
||||||
|
KeReleaseSpinlock(&gNetLock, flags);
|
||||||
|
|
||||||
|
// Temporary observability until lwIP is attached. Avoid spamming the
|
||||||
|
// console on busy LANs. Log before protocol handling so RX/TX ordering is
|
||||||
|
// easier to read when an interrupt fires during a send path.
|
||||||
|
if (sLoggedFrames < NET_RX_LOG_LIMIT) {
|
||||||
|
const uint8_t* b = (const uint8_t*)frame;
|
||||||
|
uint16_t etherType = ((uint16_t)b[12] << 8) | b[13];
|
||||||
|
DbgPrint("NET: %s rx len=%u ethertype=0x%04x dst=%02x:%02x:%02x:%02x:%02x:%02x src=%02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
device->Name,
|
||||||
|
(unsigned)length,
|
||||||
|
etherType,
|
||||||
|
b[0], b[1], b[2], b[3], b[4], b[5],
|
||||||
|
b[6], b[7], b[8], b[9], b[10], b[11]);
|
||||||
|
sLoggedFrames++;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetEthernetReceive(device, frame, length);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
// net_device.h - tiny kernel network-device registry.
|
||||||
|
//
|
||||||
|
// This layer is intentionally below the future socket/IP stack. NIC drivers
|
||||||
|
// register a NET_DEVICE and feed complete Ethernet frames into NetReceiveFrame().
|
||||||
|
// Protocol code can later subscribe above this layer or bind a real stack.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <ke_status.h>
|
||||||
|
|
||||||
|
#define NET_DEVICE_NAME_MAX 16u
|
||||||
|
#define NET_HWADDR_MAX 16u
|
||||||
|
#define NET_MTU_ETHERNET 1500u
|
||||||
|
#define NET_ETHERNET_ADDRESS_LENGTH 6u
|
||||||
|
#define NET_ETHERNET_HEADER_LENGTH 14u
|
||||||
|
#define NET_ETHERNET_MIN_FRAME 60u
|
||||||
|
#define NET_ETHERNET_MAX_FRAME (NET_ETHERNET_HEADER_LENGTH + NET_MTU_ETHERNET)
|
||||||
|
|
||||||
|
struct NET_DEVICE;
|
||||||
|
typedef struct NET_DEVICE NET_DEVICE;
|
||||||
|
|
||||||
|
typedef KSTATUS (*NET_SEND_FRAME)(NET_DEVICE* device,
|
||||||
|
const void* frame,
|
||||||
|
size_t length);
|
||||||
|
|
||||||
|
typedef struct NET_DEVICE_STATS {
|
||||||
|
uint64_t TxPackets;
|
||||||
|
uint64_t TxBytes;
|
||||||
|
uint64_t TxErrors;
|
||||||
|
uint64_t RxPackets;
|
||||||
|
uint64_t RxBytes;
|
||||||
|
uint64_t RxDrops;
|
||||||
|
} NET_DEVICE_STATS;
|
||||||
|
|
||||||
|
struct NET_DEVICE {
|
||||||
|
char Name[NET_DEVICE_NAME_MAX];
|
||||||
|
uint32_t Index;
|
||||||
|
uint32_t Mtu;
|
||||||
|
uint8_t HardwareAddressLength;
|
||||||
|
uint8_t HardwareAddress[NET_HWADDR_MAX];
|
||||||
|
void* DriverContext;
|
||||||
|
NET_SEND_FRAME SendFrame;
|
||||||
|
NET_DEVICE_STATS Stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
__declspec(dllexport) KSTATUS NetRegisterDevice(NET_DEVICE* device);
|
||||||
|
__declspec(dllexport) uint32_t NetGetDeviceCount(void);
|
||||||
|
__declspec(dllexport) NET_DEVICE* NetGetDevice(uint32_t index);
|
||||||
|
__declspec(dllexport) KSTATUS NetGetDeviceStats(uint32_t index,
|
||||||
|
NET_DEVICE_STATS* stats);
|
||||||
|
__declspec(dllexport) KSTATUS NetSendFrame(uint32_t index,
|
||||||
|
const void* frame,
|
||||||
|
size_t length);
|
||||||
|
__declspec(dllexport) void NetReceiveFrame(NET_DEVICE* device,
|
||||||
|
const void* frame,
|
||||||
|
size_t length);
|
||||||
@@ -81,5 +81,6 @@
|
|||||||
#define SYS_GET_TICK 72
|
#define SYS_GET_TICK 72
|
||||||
#define SYS_DEBUG_PRINT 73
|
#define SYS_DEBUG_PRINT 73
|
||||||
#define SYS_QUERY_TRAP_FRAME 74
|
#define SYS_QUERY_TRAP_FRAME 74
|
||||||
|
#define SYS_CATCHALLDEBUGBUCKET 75
|
||||||
|
|
||||||
#define SYS_MAX 75
|
#define SYS_MAX 76
|
||||||
@@ -24,6 +24,32 @@ static void printInt(int32_t n) {
|
|||||||
else { printUInt((uint32_t)n); }
|
else { printUInt((uint32_t)n); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t parseUInt64(const char* s) {
|
||||||
|
uint64_t v = 0;
|
||||||
|
while (*s == ' ') s++;
|
||||||
|
while (*s >= '0' && *s <= '9') v = v * 10 + (uint64_t)(*s++ - '0');
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse up to 6 comma-separated uint64 values.
|
||||||
|
// Returns the number of values actually parsed.
|
||||||
|
static int parseArgs(const char* line, uint64_t args[6]) {
|
||||||
|
int count = 0;
|
||||||
|
while (*line && count < 6) {
|
||||||
|
args[count++] = parseUInt64(line);
|
||||||
|
while (*line && *line != ',') line++; // skip to next comma
|
||||||
|
if (*line == ',') line++; // consume comma
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printUInt64(uint64_t n) {
|
||||||
|
if (n == 0) { print("0"); return; }
|
||||||
|
char buf[20]; int i = 0;
|
||||||
|
while (n > 0) { buf[i++] = (char)('0' + (int)(n % 10)); n /= 10; }
|
||||||
|
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Line editor
|
// Line editor
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -54,6 +80,7 @@ static char* trim(char* s) {
|
|||||||
static void cmd_help() {
|
static void cmd_help() {
|
||||||
print("Available commands:\n"
|
print("Available commands:\n"
|
||||||
" help - Show this list\n"
|
" help - Show this list\n"
|
||||||
|
" debug - Enter debug bucket mode\n"
|
||||||
" dir [path] - List directory contents\n"
|
" dir [path] - List directory contents\n"
|
||||||
" cd <path> - Change directory\n"
|
" cd <path> - Change directory\n"
|
||||||
" type <file> - Display file contents\n"
|
" type <file> - Display file contents\n"
|
||||||
@@ -63,6 +90,32 @@ static void cmd_help() {
|
|||||||
" exit - Close the shell\n");
|
" exit - Close the shell\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cmd_debug(void) {
|
||||||
|
print("Entered debug mode. Type args as: a0,a1,a2,a3,a4,a5\n");
|
||||||
|
print("Type 'exit' to leave.\n");
|
||||||
|
|
||||||
|
char line[MAX_CMD];
|
||||||
|
while (true) {
|
||||||
|
print("debug> ");
|
||||||
|
readLine(line, MAX_CMD);
|
||||||
|
char* t = trim(line);
|
||||||
|
|
||||||
|
if (strcmp(t, "exit") == 0) {
|
||||||
|
print("Exit debug.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*t == '\0') continue;
|
||||||
|
|
||||||
|
uint64_t args[6] = {0, 0, 0, 0, 0, 0};
|
||||||
|
parseArgs(t, args);
|
||||||
|
|
||||||
|
uint64_t ret = SysCatchAllDebugBucket(args[0], args[1], args[2],
|
||||||
|
args[3], args[4], args[5]);
|
||||||
|
printUInt64(ret);
|
||||||
|
print("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void cmd_write(const char* args) {
|
static void cmd_write(const char* args) {
|
||||||
char filename[MAX_CMD]; int i = 0;
|
char filename[MAX_CMD]; int i = 0;
|
||||||
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
|
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
|
||||||
@@ -150,6 +203,7 @@ static int runCommand(const char* cmd) {
|
|||||||
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
|
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
|
||||||
if (strcmp(cmd, "clear") == 0) { SysScreenClear(0); return 0; }
|
if (strcmp(cmd, "clear") == 0) { SysScreenClear(0); return 0; }
|
||||||
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
|
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
|
||||||
|
if (strcmp(cmd, "debug") == 0) { cmd_debug(); return 0; }
|
||||||
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
|
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
|
||||||
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
|
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
|
||||||
if (strncmp(cmd, "cd ", 3) == 0) { if (SysSetCwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
|
if (strncmp(cmd, "cd ", 3) == 0) { if (SysSetCwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
|
||||||
|
|||||||
@@ -233,3 +233,5 @@ void SysMouseGetPos(int* x, int* y);
|
|||||||
uint64_t SysGetTick(void);
|
uint64_t SysGetTick(void);
|
||||||
uint32_t SysDebugPrint(const char* str);
|
uint32_t SysDebugPrint(const char* str);
|
||||||
uint32_t SysQueryTrapFrame(SYS_TRAP_FRAME_INFO* info);
|
uint32_t SysQueryTrapFrame(SYS_TRAP_FRAME_INFO* info);
|
||||||
|
|
||||||
|
uint64_t SysCatchAllDebugBucket(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5);
|
||||||
@@ -75,5 +75,6 @@ SYS_MOUSE_GET_POS EQU 71
|
|||||||
SYS_GET_TICK EQU 72
|
SYS_GET_TICK EQU 72
|
||||||
SYS_DEBUG_PRINT EQU 73
|
SYS_DEBUG_PRINT EQU 73
|
||||||
SYS_QUERY_TRAP_FRAME EQU 74
|
SYS_QUERY_TRAP_FRAME EQU 74
|
||||||
|
SYS_CATCHALLDEBUGBUCKET EQU 75
|
||||||
|
|
||||||
SYS_MAX EQU 75
|
SYS_MAX EQU 76
|
||||||
@@ -197,4 +197,6 @@ SYSCALL_STUB2 GetTick, SYS_GET_TICK
|
|||||||
SYSCALL_STUB2 DebugPrint, SYS_DEBUG_PRINT
|
SYSCALL_STUB2 DebugPrint, SYS_DEBUG_PRINT
|
||||||
SYSCALL_STUB2 QueryTrapFrame, SYS_QUERY_TRAP_FRAME
|
SYSCALL_STUB2 QueryTrapFrame, SYS_QUERY_TRAP_FRAME
|
||||||
|
|
||||||
|
SYSCALL_STUB6 CatchAllDebugBucket, SYS_CATCHALLDEBUGBUCKET
|
||||||
|
|
||||||
END
|
END
|
||||||
@@ -1 +1 @@
|
|||||||
@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M.fd -drive format=vpc,file=disk.vhd -serial stdio
|
@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M.fd -drive format=vpc,file=disk.vhd -serial stdio -netdev user,id=net0 -device rtl8139,netdev=net0
|
||||||
@@ -1 +1 @@
|
|||||||
@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M_d.fd -device ahci,id=ahci -drive format=vpc,file=disk.vhd,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 -serial stdio -m 256M
|
@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M_d.fd -device ahci,id=ahci -drive format=vpc,file=disk.vhd,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 -serial stdio -m 256M -netdev user,id=net0 -device rtl8139,netdev=net0
|
||||||
@@ -1 +1 @@
|
|||||||
@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M_d.fd -device ahci,id=ahci -drive format=vpc,file=disk.vhd,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 -m 4G -serial stdio -d in_asm,cpu_reset,int -D qemu.log -no-reboot -no-shutdown
|
@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M_d.fd -device ahci,id=ahci -drive format=vpc,file=disk.vhd,if=none,id=disk0 -device ide-hd,bus=ahci.0,drive=disk0 -m 4G -serial stdio -d in_asm,cpu_reset,int -D qemu.log -no-reboot -no-shutdown -netdev user,id=net0 -device rtl8139,netdev=net0
|
||||||
@@ -1 +1 @@
|
|||||||
@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M.fd -drive format=vpc,file=disk.vhd -m 4G -serial stdio -d in_asm,cpu_reset,int -D qemu.log -no-reboot -no-shutdown
|
@start qemu-system-x86_64 -drive if=pflash,format=raw,readonly=on,file=OVMF\OVMF_CODE_4M.fd -drive if=pflash,format=raw,file=OVMF\OVMF_VARS_4M.fd -drive format=vpc,file=disk.vhd -m 4G -serial stdio -d in_asm,cpu_reset,int -D qemu.log -no-reboot -no-shutdown -netdev user,id=net0 -device rtl8139,netdev=net0
|
||||||
Reference in new issue
Block a user