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
@@ -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_dpc.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_debug.h>
|
||||
|
||||
#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 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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -40,8 +59,13 @@ void KeIrqDispatch(uint8_t irq) {
|
||||
|
||||
KIRQL oldIrql = KeRaiseIrql(targetIrql);
|
||||
|
||||
if (irq < MAX_IRQS && gIrqHandlers[irq])
|
||||
gIrqHandlers[irq]();
|
||||
if (irq < MAX_IRQS) {
|
||||
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
|
||||
// only request rescheduling; KiExitInterrupt performs the switch after this
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
// HAL pointer - set once at boot by KeSyscallSetHal()
|
||||
// ---------------------------------------------------------------------------
|
||||
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 {
|
||||
volatile uint32_t Status;
|
||||
@@ -84,7 +85,10 @@ typedef struct SYS_USER_ASYNC_APC_CONTEXT {
|
||||
void* UserContext;
|
||||
} 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.
|
||||
@@ -1494,6 +1498,11 @@ static uint64_t SyscallRemoveDir(uint64_t a0, uint64_t a1, uint64_t a2,
|
||||
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.
|
||||
// ===========================================================================
|
||||
@@ -1576,6 +1585,7 @@ static const SYSCALL_HANDLER gSyscallTable[SYS_MAX] = {
|
||||
/* 73 SYS_GET_TICK */ SyscallGetTick,
|
||||
/* 74 SYS_DEBUG_PRINT */ SyscallDebugPrint,
|
||||
/* 75 SYS_QUERY_TRAP_FRAME */ SyscallQueryTrapFrame,
|
||||
/* 76 SYS_CATCHALLDEBUGBUCKET */ SyscallCatchAllDebugBucket
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <hal_api.h>
|
||||
|
||||
// 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
|
||||
uint64_t KeSyscallDispatch(uint64_t nr, uint64_t a0, uint64_t a1,
|
||||
|
||||
+64
-5
@@ -13,6 +13,7 @@
|
||||
#include <ke_irq.h>
|
||||
#include <ke_dpc.h>
|
||||
#include <ke_syscall.h>
|
||||
#include <ethernet.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_util.h>
|
||||
#include <ldr_image.h>
|
||||
@@ -401,10 +402,15 @@ void SystemInitThread(void* arg) {
|
||||
args->hal->Screen.Clear(0x000000);
|
||||
|
||||
// Print memory status now that the system is fully alive
|
||||
DbgPrint("Memory Status:\n");
|
||||
DbgPrint(" -> Free (PMM): %.2f MB\n", (float)MmFreeFrameCount() * PAGE_SIZE / (1024.0f * 1024.0f));
|
||||
DbgPrint(" -> Kernel Pool: %.2f MB\n", (float)MmGetKernelPoolUsage() / (1024.0f * 1024.0f));
|
||||
DbgPrint(" -> User Heap: %.2f MB\n", (float)MmGetUserHeapUsage() / (1024.0f * 1024.0f));
|
||||
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)
|
||||
);
|
||||
|
||||
if (args->bootInfo->Debug & 2)
|
||||
KiRunKernelSelfTests(args->hal);
|
||||
@@ -432,6 +438,59 @@ void SystemInitThread(void* arg) {
|
||||
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
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -495,7 +554,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
|
||||
HalRegisterScheduler(KeSchedulerTick);
|
||||
|
||||
KeSyscallSetHal(hal);
|
||||
KeSyscallInit(hal, KeCatchAllDebugBucket);
|
||||
|
||||
//
|
||||
// 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_DEBUG_PRINT 73
|
||||
#define SYS_QUERY_TRAP_FRAME 74
|
||||
#define SYS_CATCHALLDEBUGBUCKET 75
|
||||
|
||||
#define SYS_MAX 75
|
||||
#define SYS_MAX 76
|
||||
Reference in new issue
Block a user