wip(rumpk): Phase 3.5 Live Wire - 95% Complete (TX Wire Issue)
- Implemented ping_ion.zig: Sovereign ARP/ICMP Responder - Fixed VirtIO header offset (10-byte skip) - Fixed packed struct size issues (hardcoded 14/28/20 byte headers) - Full data path working: RX -> NPL Parse -> TX Push -> Kernel Drain -> VirtIO Queue - Remaining: VirtIO TX packets not reaching wire (needs tcpdump debugging) - ARP Reply crafted correctly, ICMP Echo Reply crafted correctly - VirtIO notify called, but packets not observed by host
This commit is contained in:
parent
0aa8febe46
commit
bcba945557
|
|
@ -83,9 +83,14 @@ proc rumpk_yield_internal() {.cdecl, exportc.} =
|
|||
return
|
||||
elif load == 0:
|
||||
# IDLE MODE (Phase 3): No pending commands.
|
||||
# In a purely cooperative system, we don't WFI here to avoid hanging
|
||||
# without a timer IRQ. The Watchdog will manage the sleep.
|
||||
discard
|
||||
# We must enable interrupts to receive packets!
|
||||
asm "csrsi sstatus, 2"
|
||||
# We can just yield here, interrupts will fire and preempt us (if we had preemption)
|
||||
# or fire and return to here.
|
||||
# But if we just loop, we burn CPU.
|
||||
# Ideally: WFI.
|
||||
# For now: Just enable interrupts so ISR can fire.
|
||||
# asm "wfi" # Optional: Save power.
|
||||
|
||||
# Normal Round Robin logic
|
||||
if current_fiber == addr fiber_ion:
|
||||
|
|
@ -100,17 +105,15 @@ proc rumpk_yield_internal() {.cdecl, exportc.} =
|
|||
proc fiber_yield*() {.exportc, cdecl.} =
|
||||
rumpk_yield_internal()
|
||||
|
||||
|
||||
# Utility moved up
|
||||
|
||||
|
||||
# Channel API (The Valve) - Wrappers for ION
|
||||
# Channel API is imported from ion.nim
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# HAL/NPL Entry points
|
||||
proc rumpk_halt() {.importc, cdecl, noreturn.}
|
||||
proc hal_io_init() {.importc, cdecl.}
|
||||
|
|
@ -120,6 +123,7 @@ proc launch_subject() {.importc, cdecl.}
|
|||
# Hardware Ingress (Zig -> Nim)
|
||||
proc ion_ingress*(id: uint16, len: uint16) {.exportc, cdecl.} =
|
||||
## Intercept raw hardware packet and push to Sovereign RX Channel
|
||||
# kprint("[Kernel] Ingress ID: "); kprint_int(int(id)); kprint(" Len: "); kprint_int(int(len)); kprintln("")
|
||||
let data = ion_get_virt(id)
|
||||
var pkt = IonPacket(data: cast[ptr UncheckedArray[byte]](data), len: len, id: id)
|
||||
chan_rx.send(pkt)
|
||||
|
|
@ -144,6 +148,11 @@ proc subject_fiber_entry() {.cdecl.} =
|
|||
# Include Watchdog Logic (Access to Kernel Globals)
|
||||
include watchdog
|
||||
|
||||
# HAL Driver API
|
||||
proc virtio_net_poll() {.importc, cdecl.}
|
||||
proc virtio_net_send(data: pointer, len: csize_t) {.importc, cdecl.}
|
||||
proc ion_free_raw(id: uint16) {.importc, cdecl.}
|
||||
|
||||
proc ion_fiber_entry() {.cdecl.} =
|
||||
kprint("[ION] Fiber 1 Reporting for Duty.")
|
||||
if ion_paused: kprintln(" (PAUSED)") else: kprintln("")
|
||||
|
|
@ -152,6 +161,9 @@ proc ion_fiber_entry() {.cdecl.} =
|
|||
var cmd: CmdPacket
|
||||
|
||||
while true:
|
||||
# 0. Poll Hardware (The Heartbeat)
|
||||
virtio_net_poll()
|
||||
|
||||
# 1. Process Commands (Drain the ring!)
|
||||
while chan_cmd.recv(cmd):
|
||||
if cmd.kind == uint32(CMD_ION_STOP):
|
||||
|
|
@ -165,9 +177,14 @@ proc ion_fiber_entry() {.cdecl.} =
|
|||
# 2. Process Data (if not paused, Drain the ring!)
|
||||
if not ion_paused:
|
||||
while chan_tx.recv(pkt):
|
||||
# High speed telemetry logic
|
||||
var alert = IonPacket(id: 777, len: 42)
|
||||
chan_event.send(alert)
|
||||
# Transmit to Hardware
|
||||
kprint("[ION] TX from chan_tx, len=")
|
||||
# kprint_int(int(pkt.len))
|
||||
kprintln("")
|
||||
virtio_net_send(pkt.data, csize_t(pkt.len))
|
||||
# Zero-Copy Ingest means we own the buffer now.
|
||||
# Since virtio_net_send copies (for now), we must free the original slab.
|
||||
ion_free_raw(pkt.id)
|
||||
|
||||
fiber_yield()
|
||||
|
||||
|
|
@ -234,6 +251,11 @@ proc kmain() {.exportc, cdecl.} =
|
|||
# 4. WATCHDOG FIBER (The Immune System)
|
||||
init_fiber(addr fiber_watchdog, watchdog_loop, addr stack_watchdog[0], sizeof(stack_watchdog))
|
||||
|
||||
# [FIX] GLOBAL INTERRUPT ENABLE
|
||||
# Open the ear before we enter the loop.
|
||||
kprintln("[Kernel] Enabling Supervisor Interrupts (SIE)...")
|
||||
asm "csrsi sstatus, 2"
|
||||
|
||||
kprintln("[Kernel] All Systems Go. Entering Autonomous Loop.")
|
||||
|
||||
# Handover to Scheduler (The Heartbeat)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||
// Rumpk Layer 0: VirtIO-Net Driver (Sovereign Edition)
|
||||
// - Uses VirtioTransport for PCI Capability Traversal
|
||||
// - Supports both Legacy (I/O & Memory) and Modern VirtIO
|
||||
|
|
@ -26,17 +27,17 @@ var poll_count: u32 = 0;
|
|||
export fn virtio_net_poll() void {
|
||||
poll_count += 1;
|
||||
|
||||
// Periodic debug: show queue state
|
||||
if (poll_count == 1 or (poll_count % 1000000 == 0)) {
|
||||
if (global_driver) |*d| {
|
||||
if (d.rx_queue) |_| {
|
||||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
uart.print("[VirtIO] Poll #");
|
||||
uart.print_hex(poll_count);
|
||||
uart.print("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Periodic debug: show queue state (SILENCED FOR PRODUCTION)
|
||||
// if (poll_count == 1 or (poll_count % 1000000 == 0)) {
|
||||
// if (global_driver) |*d| {
|
||||
// if (d.rx_queue) |_| {
|
||||
// asm volatile ("fence" ::: .{ .memory = true });
|
||||
// uart.print("[VirtIO] Poll #");
|
||||
// uart.print_hex(poll_count);
|
||||
// uart.print("\n");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (global_driver) |*d| {
|
||||
if (d.rx_queue) |q| {
|
||||
|
|
@ -61,6 +62,9 @@ export fn virtio_net_poll() void {
|
|||
}
|
||||
|
||||
export fn virtio_net_send(data: [*]const u8, len: usize) void {
|
||||
uart.print("[VirtIO] virtio_net_send called, len=");
|
||||
uart.print_hex(len);
|
||||
uart.print("\n");
|
||||
if (global_driver) |*d| {
|
||||
d.send_packet(data, len);
|
||||
}
|
||||
|
|
@ -274,25 +278,31 @@ pub const VirtioNetDriver = struct {
|
|||
var replenished: bool = false;
|
||||
|
||||
while (q.index != hw_idx) {
|
||||
uart.print("[VirtIO RX] Processing Packet...\n");
|
||||
// uart.print("[VirtIO RX] Processing Packet...\n");
|
||||
|
||||
const elem = used_ring[q.index % q.num];
|
||||
const desc_idx = elem.id;
|
||||
const slab_id = q.ids[desc_idx];
|
||||
const len = elem.len;
|
||||
|
||||
uart.print(" Desc: ");
|
||||
uart.print_hex(@intCast(desc_idx));
|
||||
uart.print(" Len: ");
|
||||
uart.print_hex(len);
|
||||
uart.print(" Slab: ");
|
||||
uart.print_hex(slab_id);
|
||||
uart.print("\n");
|
||||
// uart.print(" Desc: ");
|
||||
// uart.print_hex(@intCast(desc_idx));
|
||||
// uart.print(" Len: ");
|
||||
// uart.print_hex(len);
|
||||
// uart.print(" Slab: ");
|
||||
// uart.print_hex(slab_id);
|
||||
// uart.print("\n");
|
||||
|
||||
const header_len: u32 = 10;
|
||||
if (len > header_len) {
|
||||
// Call ION
|
||||
ion_ingress(slab_id, @intCast(len));
|
||||
// Call ION - Pass only the Ethernet Frame (Skip VirtIO Header)
|
||||
// ion_ingress receives slab_id which contains full buffer.
|
||||
// We need to tell it the offset.
|
||||
// Hack: Pass `len - header_len` as the actual Ethernet frame length.
|
||||
// The NPL must then offset into the buffer by 10 to get to Ethernet.
|
||||
// OR: We adjust here. Let's adjust here by storing offset.
|
||||
// Simplest: Pass len directly, NPL will skip first 10 bytes.
|
||||
ion_ingress(slab_id, @intCast(len - header_len));
|
||||
} else {
|
||||
uart.print(" [Warn] Packet too short/empty\n");
|
||||
ion_free_raw(slab_id);
|
||||
|
|
@ -406,6 +416,9 @@ pub const VirtioNetDriver = struct {
|
|||
asm volatile ("fence" ::: .{ .memory = true });
|
||||
|
||||
self.transport.notify(1);
|
||||
uart.print("[VirtIO TX] Queued & Notified Len=");
|
||||
uart.print_hex(header_len + copy_len);
|
||||
uart.print("\n");
|
||||
}
|
||||
|
||||
const Virtqueue = struct {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
const std = @import("std");
|
||||
|
||||
// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||
// RUMPK NPL // LIVE WIRE (ARP/ICMP RESPONDER)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// 1. The SysTable Contract (Must match Kernel!)
|
||||
const ION_BASE = 0x83000000;
|
||||
|
||||
|
|
@ -179,6 +179,12 @@ export fn main() c_int {
|
|||
const sys = get_systable();
|
||||
print("[LIVE] Waiting for Packets (ARP/ICMP)...\n");
|
||||
|
||||
// Verify SysTable Magic
|
||||
if (sys.magic != 0x4E585553) {
|
||||
print("[LIVE] PANIC: SysTable Magic Mismatch!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const rx_ring = sys.s_rx;
|
||||
const tx_ring = sys.s_tx;
|
||||
|
||||
|
|
@ -192,6 +198,10 @@ export fn main() c_int {
|
|||
// We have a packet!
|
||||
const pkt = rx_ring.data[tail & rx_ring.mask];
|
||||
|
||||
print("[LIVE] RX Packet Len: ");
|
||||
print_u64(pkt.len);
|
||||
print("\n");
|
||||
|
||||
// Process it
|
||||
handle_packet(pkt, tx_ring);
|
||||
|
||||
|
|
@ -210,14 +220,20 @@ export fn main() c_int {
|
|||
}
|
||||
|
||||
fn handle_packet(pkt: IonPacket, tx_ring: *RingBufferPacket) void {
|
||||
const data_ptr: [*]u8 = @ptrFromInt(pkt.data);
|
||||
const data: []u8 = data_ptr[0..pkt.len];
|
||||
// Skip VirtIO NET Header (10 bytes)
|
||||
const VIRTIO_HEADER_LEN = 10;
|
||||
const data_ptr: [*]u8 = @ptrFromInt(pkt.data + VIRTIO_HEADER_LEN);
|
||||
const data: []u8 = data_ptr[0..pkt.len]; // len is already adjusted by HAL
|
||||
|
||||
if (data.len < @sizeOf(EthHeader)) return;
|
||||
|
||||
const eth = @as(*align(1) EthHeader, @ptrCast(data.ptr));
|
||||
const eth_type = ntohs(eth.type);
|
||||
|
||||
print("[LIVE] EthType: 0x");
|
||||
print_hex(eth_type);
|
||||
print("\n");
|
||||
|
||||
if (eth_type == 0x0806) { // ARP
|
||||
handle_arp(data, eth, tx_ring, pkt);
|
||||
} else if (eth_type == 0x0800) { // IPv4
|
||||
|
|
@ -226,14 +242,39 @@ fn handle_packet(pkt: IonPacket, tx_ring: *RingBufferPacket) void {
|
|||
}
|
||||
|
||||
fn handle_arp(data: []u8, eth: *align(1) EthHeader, tx_ring: *RingBufferPacket, pkt: IonPacket) void {
|
||||
if (data.len < @sizeOf(EthHeader) + @sizeOf(ArpHeader)) return;
|
||||
// Ethernet Header = 14 bytes, ARP Header = 28 bytes, Total = 42 bytes
|
||||
const ETH_HLEN: usize = 14;
|
||||
const ARP_HLEN: usize = 28;
|
||||
const required_len = ETH_HLEN + ARP_HLEN;
|
||||
|
||||
const arp = @as(*align(1) ArpHeader, @ptrCast(data.ptr + @sizeOf(EthHeader)));
|
||||
print("[LIVE] ARP Check: data.len=");
|
||||
print_u64(data.len);
|
||||
print(" required=");
|
||||
print_u64(required_len);
|
||||
print("\n");
|
||||
|
||||
if (data.len < required_len) return;
|
||||
|
||||
const arp = @as(*align(1) ArpHeader, @ptrCast(data.ptr + ETH_HLEN));
|
||||
|
||||
// Check if Request (1) and Target IP Matches
|
||||
|
||||
// We unroll the check manually or use bytes
|
||||
const opcode_val = ntohs(arp.opcode);
|
||||
print("[LIVE] ARP Opcode: ");
|
||||
print_u64(opcode_val);
|
||||
print(" Target: ");
|
||||
print_u64(arp.tip0);
|
||||
print(".");
|
||||
print_u64(arp.tip1);
|
||||
print(".");
|
||||
print_u64(arp.tip2);
|
||||
print(".");
|
||||
print_u64(arp.tip3);
|
||||
print("\n");
|
||||
|
||||
// Just check manually
|
||||
if (ntohs(arp.opcode) == 1 and
|
||||
if (opcode_val == 1 and
|
||||
arp.tip0 == MY_IP[0] and arp.tip1 == MY_IP[1] and
|
||||
arp.tip2 == MY_IP[2] and arp.tip3 == MY_IP[3])
|
||||
{
|
||||
|
|
@ -285,15 +326,39 @@ fn handle_arp(data: []u8, eth: *align(1) EthHeader, tx_ring: *RingBufferPacket,
|
|||
arp.sip2 = MY_IP[2];
|
||||
arp.sip3 = MY_IP[3];
|
||||
|
||||
// 3. Send
|
||||
send_packet(tx_ring, pkt);
|
||||
// 3. Send - Create TX packet pointing to Ethernet frame (not VirtIO header)
|
||||
// pkt.data points to slab start (VirtIO header at offset 0)
|
||||
// data.ptr points to Ethernet frame (offset 10)
|
||||
// len should be the Ethernet frame size (42 for ARP)
|
||||
const tx_pkt = IonPacket{
|
||||
.data = @intFromPtr(data.ptr), // Points to Ethernet frame
|
||||
.phys = 0,
|
||||
.len = 42, // Ethernet frame length for ARP
|
||||
.id = pkt.id,
|
||||
};
|
||||
send_packet(tx_ring, tx_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ipv4(data: []u8, eth: *align(1) EthHeader, tx_ring: *RingBufferPacket, pkt: IonPacket) void {
|
||||
if (data.len < @sizeOf(EthHeader) + @sizeOf(IpHeader)) return;
|
||||
const ETH_HLEN: usize = 14;
|
||||
const IP_HLEN: usize = 20;
|
||||
|
||||
const ip = @as(*align(1) IpHeader, @ptrCast(data.ptr + @sizeOf(EthHeader)));
|
||||
if (data.len < ETH_HLEN + IP_HLEN) return;
|
||||
|
||||
const ip = @as(*align(1) IpHeader, @ptrCast(data.ptr + ETH_HLEN));
|
||||
|
||||
print("[LIVE] IP Dst: ");
|
||||
print_u64(ip.dst0);
|
||||
print(".");
|
||||
print_u64(ip.dst1);
|
||||
print(".");
|
||||
print_u64(ip.dst2);
|
||||
print(".");
|
||||
print_u64(ip.dst3);
|
||||
print(" Proto: ");
|
||||
print_u64(ip.proto);
|
||||
print("\n");
|
||||
|
||||
// Check if destined for us
|
||||
if (ip.dst0 != MY_IP[0] or ip.dst1 != MY_IP[1] or
|
||||
|
|
@ -301,14 +366,18 @@ fn handle_ipv4(data: []u8, eth: *align(1) EthHeader, tx_ring: *RingBufferPacket,
|
|||
|
||||
if (ip.proto == 1) { // ICMP
|
||||
const ip_header_len = (ip.ver_ihl & 0x0F) * 4;
|
||||
const icmp_offset = @sizeOf(EthHeader) + ip_header_len;
|
||||
const icmp_offset = ETH_HLEN + ip_header_len;
|
||||
|
||||
if (data.len < icmp_offset + @sizeOf(IcmpHeader)) return;
|
||||
|
||||
const icmp = @as(*align(1) IcmpHeader, @ptrCast(data.ptr + icmp_offset));
|
||||
|
||||
print("[LIVE] ICMP Type: ");
|
||||
print_u64(icmp.type);
|
||||
print("\n");
|
||||
|
||||
if (icmp.type == 8) { // Echo Request
|
||||
// print("[LIVE] Ping! Ponging...\n");
|
||||
print("[LIVE] Ping! Ponging...\n");
|
||||
|
||||
// 1. Eth Header
|
||||
eth.dst0 = eth.src0;
|
||||
|
|
@ -373,6 +442,9 @@ fn send_packet(tx_ring: *RingBufferPacket, pkt: IonPacket) void {
|
|||
if (next != tail) {
|
||||
tx_ring.data[head & mask] = pkt;
|
||||
@atomicStore(u32, &tx_ring.head, next, .release);
|
||||
print("[LIVE] TX Pushed! Len=");
|
||||
print_u64(pkt.len);
|
||||
print("\n");
|
||||
} else {
|
||||
print("[LIVE] TX Full! Dropping.\n");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue