Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 75 additions & 15 deletions drivers/PCI/PCI.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "PCI.h"
#include "PCI/PCI.h"

#include "Console.h"
#include "Io.h"
Expand All @@ -21,21 +21,81 @@ uint8_t PciConfigReadByte(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offse
return PciConfigReadDWord(bus, slot, func, offset) & 0xFF;
}

void PciEnumerate() {
for (uint8_t bus = 0; bus < 255; bus++) {
for (uint8_t device = 0; device < 32; device++) {
for (uint8_t func = 0; func < 8; func++) {
const uint32_t data = PciConfigReadDWord(bus, device, func, 0x00);
const uint16_t vendor_id = data & 0xFFFF;
if (vendor_id == 0xFFFF) continue; // No device here
const uint16_t device_id = (data >> 16) & 0xFFFF;
PrintKernel("PCI Device: Bus "); PrintKernelHex(bus);
PrintKernel(", Device "); PrintKernelHex(device);
PrintKernel(", Func "); PrintKernelHex(func);
PrintKernel(", Vendor 0x"); PrintKernelHex(vendor_id);
PrintKernel(", Device 0x"); PrintKernelHex(device_id);
PrintKernel("\n");
// The core scanning logic, now separated and reusable
static void PciScanBus(PciDeviceCallback callback) {
for (int bus = 0; bus < 256; bus++) {
for (int device = 0; device < 32; device++) {
// Read header type to check for multi-function devices
uint32_t header_type_reg = PciConfigReadDWord(bus, device, 0, 0x0C);
uint8_t header_type = (header_type_reg >> 16) & 0xFF;
int max_funcs = (header_type & 0x80) ? 8 : 1; // Is multi-function bit set?

for (int func = 0; func < max_funcs; func++) {
uint32_t id_reg = PciConfigReadDWord(bus, device, func, 0x00);
uint16_t vendor_id = id_reg & 0xFFFF;

if (vendor_id == 0xFFFF) continue; // Device doesn't exist

PciDevice pci_dev;
pci_dev.bus = bus;
pci_dev.device = device;
pci_dev.function = func;
pci_dev.vendor_id = vendor_id;
pci_dev.device_id = id_reg >> 16;

uint32_t class_reg = PciConfigReadDWord(bus, device, func, 0x08);
pci_dev.class_code = (class_reg >> 24) & 0xFF;
pci_dev.subclass = (class_reg >> 16) & 0xFF;
pci_dev.prog_if = (class_reg >> 8) & 0xFF;

// Call the provided callback with the device info
callback(pci_dev);
}
}
}
}

// Your old PciEnumerate function, now a simple wrapper around the scanner
static void PrintPciDeviceInfo(PciDevice device) {
PrintKernel("PCI: B:0x"); PrintKernelHex(device.bus);
PrintKernel(" D:0x"); PrintKernelHex(device.device);
PrintKernel(" F:0x"); PrintKernelHex(device.function);
PrintKernel(" -> VID:0x"); PrintKernelHex(device.vendor_id);
PrintKernel(" DID:0x"); PrintKernelHex(device.device_id);
PrintKernel(" (C:0x"); PrintKernelHex(device.class_code);
PrintKernel(" S:0x"); PrintKernelHex(device.subclass);
PrintKernel(")\n");
}

void PciEnumerate() {
PrintKernel("--- PCI Bus Enumeration ---\n");
PciScanBus(PrintPciDeviceInfo);
PrintKernel("---------------------------\n");
}

static void FindDeviceCallback(PciDevice device) {
if (device_found_flag) return; // We already found it, stop searching

if (device.vendor_id == target_vendor_id && device.device_id == target_device_id) {
found_device = device;
device_found_flag = 1;
}
}

// The public helper function to find a device
int PciFindDevice(uint16_t vendor_id, uint16_t device_id, PciDevice* out_device) {
// Set up the search criteria
target_vendor_id = vendor_id;
target_device_id = device_id;
device_found_flag = 0;

// Scan the bus using our specific search callback
PciScanBus(FindDeviceCallback);

if (device_found_flag) {
*out_device = found_device;
return 0; // Success
}

return -1; // Failure
}
31 changes: 26 additions & 5 deletions drivers/PCI/PCI.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
#ifndef VOIDFRAME_PCI_H
#define VOIDFRAME_PCI_H
#ifndef PCI_H
#define PCI_H

#define PCI_CONFIG_ADDRESS 0xCF8
#define PCI_CONFIG_DATA 0xCFC
#include "stdint.h" // Or your equivalent for standard types

// A structure to hold information about a discovered PCI device
typedef struct {
uint8_t bus;
uint8_t device;
uint8_t function;
uint16_t vendor_id;
uint16_t device_id;
uint8_t class_code;
uint8_t subclass;
uint8_t prog_if; // Programming Interface
} PciDevice;

static PciDevice found_device;
static int device_found_flag;
static uint16_t target_vendor_id;
static uint16_t target_device_id;
// Callback function pointer type
typedef void (*PciDeviceCallback)(PciDevice device);

// Function prototypes
void PciEnumerate();
int PciFindDevice(uint16_t vendor_id, uint16_t device_id, PciDevice* out_device);
uint32_t PciConfigReadDWord(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset);

#endif // VOIDFRAME_PCI_H
#endif // PCI_H
44 changes: 44 additions & 0 deletions drivers/ethernet/Packet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef PACKET_H
#define PACKET_H

#include <stdint.h>

// Endianness conversion - crucial for network packets
// Network byte order is Big Endian, x86 is Little Endian.
static inline uint16_t bswap16(uint16_t v) {
return (uint16_t)((v << 8) | (v >> 8));
}
#define HTONS(x) bswap16((uint16_t)(x))
#define NTOHS(x) bswap16((uint16_t)(x))

// Ethernet Header (14 bytes)
typedef struct {
uint8_t dest_mac[6];
uint8_t src_mac[6];
uint16_t ethertype;
} __attribute__((packed)) EthernetHeader;

// ARP Packet (28 bytes)
typedef struct {
uint16_t hardware_type; // 1 for Ethernet
uint16_t protocol_type; // 0x0800 for IPv4
uint8_t hardware_addr_len; // 6 for MAC
uint8_t protocol_addr_len; // 4 for IP
uint16_t opcode; // 1 for request, 2 for reply
uint8_t sender_mac[6];
uint8_t sender_ip[4];
uint8_t target_mac[6];
uint8_t target_ip[4];
} __attribute__((packed)) ArpPacket;

// Full ARP-over-Ethernet Packet (42 bytes)
typedef struct {
EthernetHeader eth;
ArpPacket arp;
} __attribute__((packed)) FullArpPacket;

_Static_assert(sizeof(EthernetHeader) == 14, "EthernetHeader must be 14 bytes");
_Static_assert(sizeof(ArpPacket) == 28, "ArpPacket must be 28 bytes");
_Static_assert(sizeof(FullArpPacket) == 42, "FullArpPacket must be 42 bytes");

#endif // PACKET_H
119 changes: 119 additions & 0 deletions drivers/ethernet/RTL8139.c
Original file line number Diff line number Diff line change
@@ -1,2 +1,121 @@
#include "RTL8139.h"


#include "Console.h"
#include "Io.h"
#include "KernelHeap.h" // For allocating memory
#include "MemOps.h"
#include "VMem.h"

// Global device object
static Rtl8139Device rtl_device;

void Rtl8139_Init() {
PrintKernel("Searching for RTL8139 (10EC:8139)...\n");

if (PciFindDevice(0x10EC, 0x8139, &rtl_device.pci_info) != 0) {
PrintKernel("RTL8139 Network Card not found.\n");
return;
}
PrintKernel("Found RTL8139!\n");

// --- Part 1: Read BAR0 and get the I/O base address ---
const uint32_t bar0 = PciConfigReadDWord(rtl_device.pci_info.bus, rtl_device.pci_info.device, rtl_device.pci_info.function, 0x10);
if (bar0 & 0x1) {
// I/O mapped
rtl_device.io_base = bar0 & ~0x3;
} else {
PrintKernel("RTL8139 BAR0 is memory mapped, not supported.\n");
return;
}
PrintKernel("I/O Base: 0x"); PrintKernelHex(rtl_device.io_base); PrintKernel("\n");

// --- Part 2: Power on and Reset ---
outb(rtl_device.io_base + REG_CONFIG_1, 0x00);
outb(rtl_device.io_base + REG_COMMAND, CMD_RESET);
while ((inb(rtl_device.io_base + REG_COMMAND) & CMD_RESET) != 0) { /* wait */ }
PrintKernel("RTL8139 reset complete.\n");

// --- Part 2.1: Reading MAC address ---
PrintKernel("Reading MAC Address: ");
for (int i = 0; i < 6; i++) {
rtl_device.mac_address[i] = inb(rtl_device.io_base + REG_MAC0 + i);
PrintKernelHex(rtl_device.mac_address[i]);
if (i < 5) PrintKernel(":");
}
PrintKernel("\n");

// --- Part 3: Allocate Buffers for DMA ---
// NOTE: This MUST be physically contiguous memory! For now, we assume KernelMemoryAlloc does this.
// In a real OS, you'd need a proper physical memory manager.
rtl_device.rx_buffer = KernelMemoryAlloc(RX_BUFFER_SIZE);
if (!rtl_device.rx_buffer) {
PrintKernel("Failed to allocate RX buffer.\n");
return;
}

for (int i = 0; i < TX_BUFFER_COUNT; i++) {
rtl_device.tx_buffers[i] = KernelMemoryAlloc(2048); // 2K per TX buffer
if (!rtl_device.tx_buffers[i]) {
PrintKernel("Failed to allocate TX buffer.\n");
// Clean up previously allocated buffers
for (int j = 0; j < i; j++) {
KernelFree(rtl_device.tx_buffers[j]);
}
KernelFree(rtl_device.rx_buffer);
return;
}
}

rtl_device.current_tx_buffer = 0;
PrintKernel("DMA buffers allocated.\n");

// --- Part 4: Tell the card where the receive buffer is ---
// The hardware needs the PHYSICAL address of the buffer.
uint32_t rx_phys_addr = VIRT_TO_PHYS(rtl_device.rx_buffer); // Assuming identity mapping for now
outl(rtl_device.io_base + REG_RX_BUFFER_START, rx_phys_addr);
PrintKernel("Receive buffer configured.\n");

// --- Part 5: Enable the Receiver and Transmitter ---
// This is the final step to "turn on" the card.
outb(rtl_device.io_base + REG_COMMAND, CMD_TX_ENABLE | CMD_RX_ENABLE);
PrintKernel("Transmitter and Receiver enabled.\n");

// Configure Rx register: accept broadcast, multicast, and packets for our MAC (AB+AM+APM)
// and wrap packets that are too long.
outl(rtl_device.io_base + REG_RX_CONFIG, (1 << 7) | (1 << 3) | (1 << 2) | (1 << 1));

PrintKernel("RTL8139 initialization finished!\n");
}

void Rtl8139_SendPacket(void* data, uint32_t len) {
if (len > 2048) {
PrintKernel("Packet too large to send.\n");
return;
}

// Get the I/O and memory addresses for the current transmit descriptor
int tx_index = rtl_device.current_tx_buffer;
uint32_t tx_addr_reg = REG_TX_ADDR_0 + (tx_index * 4);
uint32_t tx_stat_reg = REG_TX_STATUS_0 + (tx_index * 4);
uint8_t* tx_buffer = rtl_device.tx_buffers[tx_index];

// Copy the packet data to our DMA-safe buffer
FastMemcpy(tx_buffer, data, len);

// Tell the card where the data is (physical address)
uint32_t tx_phys_addr = (uint32_t)tx_buffer;
outl(rtl_device.io_base + tx_addr_reg, tx_phys_addr);

// Tell the card the length of the data and start sending!
outl(rtl_device.io_base + tx_stat_reg, len);

Comment on lines +97 to +112
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Check if previous transmission completed before reusing TX buffer.

The code doesn't verify if the previous transmission using this buffer has completed. This could lead to corrupting an in-flight packet.

Add a check for the TX_HOST_OWNS bit in the status register before using the buffer:

     // Get the I/O and memory addresses for the current transmit descriptor
     int tx_index = rtl_device.current_tx_buffer;
     uint32_t tx_addr_reg = REG_TX_ADDR_0 + (tx_index * 4);
     uint32_t tx_stat_reg = REG_TX_STATUS_0 + (tx_index * 4);
+    
+    // Check if the buffer is available (bit 13 = TX_HOST_OWNS)
+    uint32_t tx_status = inl(rtl_device.io_base + tx_stat_reg);
+    if (!(tx_status & (1 << 13))) {
+        PrintKernel("TX buffer not available, transmission in progress.\n");
+        return;
+    }
+    
     uint8_t* tx_buffer = rtl_device.tx_buffers[tx_index];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Get the I/O and memory addresses for the current transmit descriptor
int tx_index = rtl_device.current_tx_buffer;
uint32_t tx_addr_reg = REG_TX_ADDR_0 + (tx_index * 4);
uint32_t tx_stat_reg = REG_TX_STATUS_0 + (tx_index * 4);
uint8_t* tx_buffer = rtl_device.tx_buffers[tx_index];
// Copy the packet data to our DMA-safe buffer
FastMemcpy(tx_buffer, data, len);
// Tell the card where the data is (physical address)
uint32_t tx_phys_addr = (uint32_t)tx_buffer;
outl(rtl_device.io_base + tx_addr_reg, tx_phys_addr);
// Tell the card the length of the data and start sending!
outl(rtl_device.io_base + tx_stat_reg, len);
// Get the I/O and memory addresses for the current transmit descriptor
int tx_index = rtl_device.current_tx_buffer;
uint32_t tx_addr_reg = REG_TX_ADDR_0 + (tx_index * 4);
uint32_t tx_stat_reg = REG_TX_STATUS_0 + (tx_index * 4);
// Check if the buffer is available (bit 13 = TX_HOST_OWNS)
uint32_t tx_status = inl(rtl_device.io_base + tx_stat_reg);
if (!(tx_status & (1 << 13))) {
PrintKernel("TX buffer not available, transmission in progress.\n");
return;
}
uint8_t* tx_buffer = rtl_device.tx_buffers[tx_index];
// Copy the packet data to our DMA-safe buffer
FastMemcpy(tx_buffer, data, len);
// Tell the card where the data is (physical address)
uint32_t tx_phys_addr = (uint32_t)tx_buffer;
outl(rtl_device.io_base + tx_addr_reg, tx_phys_addr);
// Tell the card the length of the data and start sending!
outl(rtl_device.io_base + tx_stat_reg, len);
🤖 Prompt for AI Agents
In drivers/ethernet/RTL8139.c around lines 75 to 90, the code writes a new
packet into the current TX buffer without verifying the previous transmission
finished; read the TX status register (REG_TX_STATUS_0 + tx_index*4) and check
the TX_HOST_OWNS (or equivalent "buffer in use") bit before copying/writing the
new packet, and if the bit indicates the NIC still owns the descriptor either
wait/poll with a timeout (or return an error) to avoid overwriting an in-flight
packet; only proceed with FastMemcpy and the outl() writes when the status shows
the host owns the buffer, and handle timeout/error paths (restore
state/increment index) appropriately.

PrintKernel("Sent packet of "); PrintKernelInt(len); PrintKernel(" bytes.\n");

// Move to the next transmit buffer for the next send operation
rtl_device.current_tx_buffer = (tx_index + 1) % TX_BUFFER_COUNT;
}

const Rtl8139Device* GetRtl8139Device() {
return &rtl_device;
}
67 changes: 58 additions & 9 deletions drivers/ethernet/RTL8139.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,60 @@
#ifndef VOIDFRAME_RTL8139_H
#define VOIDFRAME_RTL8139_H
#ifndef RTL8139_H
#define RTL8139_H

#define MAC0 0x00
#define MAR0 0x08
#define RBSTART 0x30
#define CMD 0x37
#define IMR 0x3C
#define ISR 0x3E
#include <stdint.h>
#include "PCI/PCI.h"

#endif // VOIDFRAME_RTL8139_H
// =============================================================================
// RTL8139 Register Offsets (from the I/O base address)
// =============================================================================
#define REG_MAC0 0x00 // MAC Address (6 bytes)
#define REG_MAR0 0x08 // Multicast Address Register (8 bytes)
#define REG_TX_STATUS_0 0x10 // Transmit Status of Descriptor 0 (4 bytes)
#define REG_TX_ADDR_0 0x20 // Transmit Address of Descriptor 0 (4 bytes)
#define REG_RX_BUFFER_START 0x30 // Receive Buffer Start Address (RBSTART)
#define REG_COMMAND 0x37 // Command Register (1 byte)
#define REG_CAPR 0x38 // Current Address of Packet Read (2 bytes)
#define REG_IMR 0x3C // Interrupt Mask Register (2 bytes)
#define REG_ISR 0x3E // Interrupt Service Register (2 bytes)
#define REG_TX_CONFIG 0x40 // Tx Config Register (4 bytes)
#define REG_RX_CONFIG 0x44 // Rx Config Register (4 bytes)
#define REG_CONFIG_1 0x52 // Config Register 1 (1 byte)

// =============================================================================
// Command Register (REG_COMMAND) Bits
// =============================================================================
#define CMD_BUFFER_EMPTY (1 << 0) // Is receive buffer empty?
#define CMD_TX_ENABLE (1 << 2) // Enable Transmitter
#define CMD_RX_ENABLE (1 << 3) // Enable Receiver
#define CMD_RESET (1 << 4) // Software Reset

// =============================================================================
// Interrupt Service/Mask Register (ISR/IMR) Bits
// =============================================================================
#define ISR_RX_OK (1 << 0) // Receive OK
#define ISR_TX_OK (1 << 2) // Transmit OK
#define ISR_RX_ERR (1 << 1) // Receive Error
#define ISR_TX_ERR (1 << 3) // Transmit Error

// =============================================================================
// Driver Configuration Constants
// =============================================================================
#define RX_BUFFER_SIZE (8192 + 16) // 8K + 16-byte header
#define TX_BUFFER_COUNT 4 // Use all 4 hardware transmit buffers

// Device state structure
typedef struct {
PciDevice pci_info;
uint32_t io_base;
uint8_t mac_address[6];
uint8_t* rx_buffer;
uint8_t* tx_buffers[TX_BUFFER_COUNT];
int current_tx_buffer;
} Rtl8139Device;

// Function Prototypes
void Rtl8139_Init();
void Rtl8139_SendPacket(void* data, uint32_t len);
const Rtl8139Device* GetRtl8139Device();

#endif // RTL8139_H
Loading