Yehuda Elyasaf
2023-03-16 21:10:05 UTC
Hi,
I wrote an rtl8139 NIC driver. It looks like it works, but when I send a packet (simple ARP packet) an ISR is called (isr 3 - "Breakpoint"). I'm running it with qemu.
I enabled NIC debug mode in qemu's source code and recompiled it, and got
the following logs from the NIC:
RTL8139: Set IRQ to 0 (0000 0000)
RTL8139: entered rtl8139_set_next_tctr_time
RTL8139: Config1 write val=0x00
RTL8139: Configuration registers are write-protected
RTL8139: ChipCmd write val=0x00000010
RTL8139: ChipCmd reset
RTL8139: Set IRQ to 0 (0000 0000)
RTL8139: entered rtl8139_set_next_tctr_time
RTL8139: receiver buffer is empty
RTL8139: ChipCmd read val=0x0001
RTL8139: TxConfig read val=0x74800000
RTL8139: ChipCmd write val=0x0000000c
RTL8139: ChipCmd enable receiver
RTL8139: ChipCmd enable transmitter
RTL8139: TxConfig write val=0x03000700
RTL8139: RxConfig write val=0x01001e3e
RTL8139: RxConfig write reset buffer size to 65536
RTL8139: RxBuf write val=0x00c25e94
RTL8139: IntrMask write(w) val=0xffff
RTL8139: Set IRQ to 0 (0000 e1ff)
RTL8139: TxStatus/TxAddr[0] read addr=0x10 size=0x4 val=0x00002000
RTL8139: TxAddr write offset=0x0 val=0x00c35e94
RTL8139: TxStatus write offset=0x0 val=0x0030003c descriptor=0
RTL8139: +++ transmitting from descriptor 0
RTL8139: +++ transmit reading 60 bytes from host memory at 0x00c35e94
RTL8139: +++ transmitted 60 bytes from descriptor 0
RTL8139: Set IRQ to 1 (0004 e1ff)
RTL8139: IntrStatus read(w) val=0x0004
RTL8139: IntrStatus write(w) val=0x0004
RTL8139: Set IRQ to 0 (0000 e1ff)
RTL8139: entered rtl8139_set_next_tctr_time
RTL8139: Set IRQ to 0 (0000 e1ff)
The logs look alright, I have no idea what the problem is.
I compared the logs with another OS's logs (PrettyOS) and got the very same logs (except the RxBuffer address, which is randomly determined).
Can you help me please?
Here is the source code:
https://gitlab.com/yehudaelyasaf/PuTTYnOS/-/blob/NetworkStack/kernel/network/nic/rtl8139.c
#include "rtl8139.h"
#include "../network.h"
#include "../../io/pci.h"
#include "../../asm.h"
#include "../../../lib/printf.h"
#include "../../../lib/convert.h"
#include "../../../lib/queue.h"
#include "../../../lib/memory.h"
#define VENDOR_ID 0x10EC
#define DEVICE_ID 0x8139
#define RX_BUFFER_LEN 0x10000
#define TX_BUFFER_LEN 4096
#define QUEUE_BUFFER_LEN 128
#define SEND_MAX_SIZE 0x700
const int RTL_TRANSMIT_COMMAND[] = {0x10, 0x14, 0x18, 0x1C};
const int RTL_TRANSMIT_START[] = {0x20, 0x24, 0x28, 0x2C};
NICPacket RTLQueueBuffer[QUEUE_BUFFER_LEN] = {0};
char buffer[RX_BUFFER_LEN + TX_BUFFER_LEN];
char *rx_buffer, *tx_buffer;
Queue RTLQueue;
uint8_t RTL8139IrqNumber = 0;
uint32_t ioAddr = 0;
bool initRTL8139(NetwotkAdapter* nic){
kprint("\tScanning for NIC...\n");
uint32_t pciAddr = PCI_ScanForDevice(VENDOR_ID, DEVICE_ID);
if (pciAddr == -1){
kprint("\tCouldn't find NIC address on PCI!");
return false;
}
ioAddr = PCI_Read(pciAddr + 0x10);
//two last bits reperesent address type
ioAddr &= (~0x3);
RTL8139IrqNumber = PCI_Read(pciAddr + 0x3C);
if(ioAddr == -1){
kprint("\tCouldn't find NIC!");
return false;
}
else{
kprint("\tFound device: RTL8139\n");
}
uint16_t pciCommand = PCI_Read(pciAddr + 0x6);
pciCommand |= 1 << 2;
PCI_Write(pciAddr, pciCommand);
rx_buffer = buffer;
tx_buffer = buffer + RX_BUFFER_LEN;
//power on
out8bit(ioAddr + INIT_RTL_CONTROL_REGISTER, POWER_ON_CODE);
//reset card
out8bit( ioAddr + RTL_CONTROL_REGISTER, RESET_CODE);
while( (in8bit(ioAddr + RTL_CONTROL_REGISTER) & RESET_CODE));
memset(0, rx_buffer, RX_BUFFER_LEN);
out8bit(ioAddr + RTL_CONTROL_REGISTER, 0x0C); // Sets the RE and TE bits high, start recieving packets
out32bit(ioAddr + TX_CONFIG, 0x03000700);
out32bit(ioAddr + RX_CONFIG, /*0xf*/0x01001e3e); //TODO: why?
irqInstallHandler(RTL8139IrqNumber, RTLIrqHandler);
out32bit(ioAddr + RBSTART, rx_buffer); // send uint32_t memory location to RBSTART (0x30)
out16bit(ioAddr + IMR_ISR_FLAGS, 0xFFFF); // Sets the TOK and ROK bits high
for (int i = 0; i < 6; i++)
nic->MAC[i] = in8bit(ioAddr+i);
//print MAC adress
char MACStr[20];
MACtos(nic->MAC, MACStr);
printf("\tMAC: %s\n", MACStr);
nic->IOBase = ioAddr;
nic->send = RTLSendPacket;
nic->sendMaxLen = SEND_MAX_SIZE;
RTLQueue = (Queue){RTLQueueBuffer, 0, QUEUE_BUFFER_LEN, sizeof(NICPacket)};
return true;
}
void RTLIrqHandler(IsrFrame registers) {
kcprint("I GOT A MESSAGE\n", GREEN, DEFAULT_COLOR);
//printPacket("MSG", rx_buffer, 100);
while(1)
printf("GOT!\n");
for(int i=0; i<RX_BUFFER_LEN; i++)
if(rx_buffer[i]!=0)
kprinti(i);
out8bit(ioAddr + IMR_ISR_FLAGS + 2, 0);
out8bit(ioAddr + IMR_ISR_FLAGS + 3, 0x5);
}
void RTLSendPacket(NICPacket packet) {
// TODO: implement locking task switch (cli and sti?)
queuePush(&RTLQueue, &packet);
NICPacket* packetAddr = queueHead(RTLQueue);
memcpy(packet.data, packetAddr->data, packet.size); // deep copy
RTLSendNextPacketInQueue();
}
bool RTLSendNextPacketInQueue() {
int i = 0;
for (; i < 4 && in16bit(ioAddr + RTL_TRANSMIT_COMMAND[i]) & (1 << 15); i++);
if (i == 4) // no pairs which arent used
return false; // return false, it couldn't send the next packet.
NICPacket *packet = queueHead(RTLQueue);
if (!packet) // no packet in queue
return false;
memcpy(packet->data, tx_buffer, packet->size);
printPacket("aaaaa", tx_buffer, packet->size);
out32bit(ioAddr + RTL_TRANSMIT_START[i], tx_buffer);
out32bit(ioAddr + RTL_TRANSMIT_COMMAND[i], ((uint32_t)packet->size) | (48 << 16));
while(true) kprint("a");
queuePop(&RTLQueue, 0);
return true;
}
https://gitlab.com/yehudaelyasaf/PuTTYnOS/-/blob/NetworkStack/kernel/network/nic/rtl8139.h:
#pragma once
#include <stdint.h>
#include "../../cpu/isr.h"
#include "../network.h"
#define MAC_ADDRES_GROUPS 6
enum RTL8139{
MAC0 = 0x0,
INIT_RTL_CONTROL_REGISTER = 0x52,
RBSTART = 0x30,
RTL_CONTROL_REGISTER = 0x37,
IMR_ISR_FLAGS = 0x3C,
TX_CONFIG = 0x40,
RX_CONFIG = 0x44, // what packets to accept. Broadcast, Multicast, Physical match, All packets
POWER_ON_CODE = 0x0,
RESET_CODE = 0X10
};
// initiates RTL device, returns true if successful.
bool initRTL8139(NetwotkAdapter* nic);
// this function is the way for software to send packets
void RTLSendPacket(NICPacket packet);
/*
this is how it actually works, behind the scenes.
SendPacket is pushing packets to queue, and this function checks
which transmit register is available, and sends the next packet in the queue in it.
if nothing is available, or other error occured, it returns false and dont send. else, it returns true.
*/
bool RTLSendNextPacketInQueue();
// This is the IRQ handler that handles recieving packets
void RTLIrqHandler(IsrFrame registers);
uint8_t* getMac();
I wrote an rtl8139 NIC driver. It looks like it works, but when I send a packet (simple ARP packet) an ISR is called (isr 3 - "Breakpoint"). I'm running it with qemu.
I enabled NIC debug mode in qemu's source code and recompiled it, and got
the following logs from the NIC:
RTL8139: Set IRQ to 0 (0000 0000)
RTL8139: entered rtl8139_set_next_tctr_time
RTL8139: Config1 write val=0x00
RTL8139: Configuration registers are write-protected
RTL8139: ChipCmd write val=0x00000010
RTL8139: ChipCmd reset
RTL8139: Set IRQ to 0 (0000 0000)
RTL8139: entered rtl8139_set_next_tctr_time
RTL8139: receiver buffer is empty
RTL8139: ChipCmd read val=0x0001
RTL8139: TxConfig read val=0x74800000
RTL8139: ChipCmd write val=0x0000000c
RTL8139: ChipCmd enable receiver
RTL8139: ChipCmd enable transmitter
RTL8139: TxConfig write val=0x03000700
RTL8139: RxConfig write val=0x01001e3e
RTL8139: RxConfig write reset buffer size to 65536
RTL8139: RxBuf write val=0x00c25e94
RTL8139: IntrMask write(w) val=0xffff
RTL8139: Set IRQ to 0 (0000 e1ff)
RTL8139: TxStatus/TxAddr[0] read addr=0x10 size=0x4 val=0x00002000
RTL8139: TxAddr write offset=0x0 val=0x00c35e94
RTL8139: TxStatus write offset=0x0 val=0x0030003c descriptor=0
RTL8139: +++ transmitting from descriptor 0
RTL8139: +++ transmit reading 60 bytes from host memory at 0x00c35e94
RTL8139: +++ transmitted 60 bytes from descriptor 0
RTL8139: Set IRQ to 1 (0004 e1ff)
RTL8139: IntrStatus read(w) val=0x0004
RTL8139: IntrStatus write(w) val=0x0004
RTL8139: Set IRQ to 0 (0000 e1ff)
RTL8139: entered rtl8139_set_next_tctr_time
RTL8139: Set IRQ to 0 (0000 e1ff)
The logs look alright, I have no idea what the problem is.
I compared the logs with another OS's logs (PrettyOS) and got the very same logs (except the RxBuffer address, which is randomly determined).
Can you help me please?
Here is the source code:
https://gitlab.com/yehudaelyasaf/PuTTYnOS/-/blob/NetworkStack/kernel/network/nic/rtl8139.c
#include "rtl8139.h"
#include "../network.h"
#include "../../io/pci.h"
#include "../../asm.h"
#include "../../../lib/printf.h"
#include "../../../lib/convert.h"
#include "../../../lib/queue.h"
#include "../../../lib/memory.h"
#define VENDOR_ID 0x10EC
#define DEVICE_ID 0x8139
#define RX_BUFFER_LEN 0x10000
#define TX_BUFFER_LEN 4096
#define QUEUE_BUFFER_LEN 128
#define SEND_MAX_SIZE 0x700
const int RTL_TRANSMIT_COMMAND[] = {0x10, 0x14, 0x18, 0x1C};
const int RTL_TRANSMIT_START[] = {0x20, 0x24, 0x28, 0x2C};
NICPacket RTLQueueBuffer[QUEUE_BUFFER_LEN] = {0};
char buffer[RX_BUFFER_LEN + TX_BUFFER_LEN];
char *rx_buffer, *tx_buffer;
Queue RTLQueue;
uint8_t RTL8139IrqNumber = 0;
uint32_t ioAddr = 0;
bool initRTL8139(NetwotkAdapter* nic){
kprint("\tScanning for NIC...\n");
uint32_t pciAddr = PCI_ScanForDevice(VENDOR_ID, DEVICE_ID);
if (pciAddr == -1){
kprint("\tCouldn't find NIC address on PCI!");
return false;
}
ioAddr = PCI_Read(pciAddr + 0x10);
//two last bits reperesent address type
ioAddr &= (~0x3);
RTL8139IrqNumber = PCI_Read(pciAddr + 0x3C);
if(ioAddr == -1){
kprint("\tCouldn't find NIC!");
return false;
}
else{
kprint("\tFound device: RTL8139\n");
}
uint16_t pciCommand = PCI_Read(pciAddr + 0x6);
pciCommand |= 1 << 2;
PCI_Write(pciAddr, pciCommand);
rx_buffer = buffer;
tx_buffer = buffer + RX_BUFFER_LEN;
//power on
out8bit(ioAddr + INIT_RTL_CONTROL_REGISTER, POWER_ON_CODE);
//reset card
out8bit( ioAddr + RTL_CONTROL_REGISTER, RESET_CODE);
while( (in8bit(ioAddr + RTL_CONTROL_REGISTER) & RESET_CODE));
memset(0, rx_buffer, RX_BUFFER_LEN);
out8bit(ioAddr + RTL_CONTROL_REGISTER, 0x0C); // Sets the RE and TE bits high, start recieving packets
out32bit(ioAddr + TX_CONFIG, 0x03000700);
out32bit(ioAddr + RX_CONFIG, /*0xf*/0x01001e3e); //TODO: why?
irqInstallHandler(RTL8139IrqNumber, RTLIrqHandler);
out32bit(ioAddr + RBSTART, rx_buffer); // send uint32_t memory location to RBSTART (0x30)
out16bit(ioAddr + IMR_ISR_FLAGS, 0xFFFF); // Sets the TOK and ROK bits high
for (int i = 0; i < 6; i++)
nic->MAC[i] = in8bit(ioAddr+i);
//print MAC adress
char MACStr[20];
MACtos(nic->MAC, MACStr);
printf("\tMAC: %s\n", MACStr);
nic->IOBase = ioAddr;
nic->send = RTLSendPacket;
nic->sendMaxLen = SEND_MAX_SIZE;
RTLQueue = (Queue){RTLQueueBuffer, 0, QUEUE_BUFFER_LEN, sizeof(NICPacket)};
return true;
}
void RTLIrqHandler(IsrFrame registers) {
kcprint("I GOT A MESSAGE\n", GREEN, DEFAULT_COLOR);
//printPacket("MSG", rx_buffer, 100);
while(1)
printf("GOT!\n");
for(int i=0; i<RX_BUFFER_LEN; i++)
if(rx_buffer[i]!=0)
kprinti(i);
out8bit(ioAddr + IMR_ISR_FLAGS + 2, 0);
out8bit(ioAddr + IMR_ISR_FLAGS + 3, 0x5);
}
void RTLSendPacket(NICPacket packet) {
// TODO: implement locking task switch (cli and sti?)
queuePush(&RTLQueue, &packet);
NICPacket* packetAddr = queueHead(RTLQueue);
memcpy(packet.data, packetAddr->data, packet.size); // deep copy
RTLSendNextPacketInQueue();
}
bool RTLSendNextPacketInQueue() {
int i = 0;
for (; i < 4 && in16bit(ioAddr + RTL_TRANSMIT_COMMAND[i]) & (1 << 15); i++);
if (i == 4) // no pairs which arent used
return false; // return false, it couldn't send the next packet.
NICPacket *packet = queueHead(RTLQueue);
if (!packet) // no packet in queue
return false;
memcpy(packet->data, tx_buffer, packet->size);
printPacket("aaaaa", tx_buffer, packet->size);
out32bit(ioAddr + RTL_TRANSMIT_START[i], tx_buffer);
out32bit(ioAddr + RTL_TRANSMIT_COMMAND[i], ((uint32_t)packet->size) | (48 << 16));
while(true) kprint("a");
queuePop(&RTLQueue, 0);
return true;
}
https://gitlab.com/yehudaelyasaf/PuTTYnOS/-/blob/NetworkStack/kernel/network/nic/rtl8139.h:
#pragma once
#include <stdint.h>
#include "../../cpu/isr.h"
#include "../network.h"
#define MAC_ADDRES_GROUPS 6
enum RTL8139{
MAC0 = 0x0,
INIT_RTL_CONTROL_REGISTER = 0x52,
RBSTART = 0x30,
RTL_CONTROL_REGISTER = 0x37,
IMR_ISR_FLAGS = 0x3C,
TX_CONFIG = 0x40,
RX_CONFIG = 0x44, // what packets to accept. Broadcast, Multicast, Physical match, All packets
POWER_ON_CODE = 0x0,
RESET_CODE = 0X10
};
// initiates RTL device, returns true if successful.
bool initRTL8139(NetwotkAdapter* nic);
// this function is the way for software to send packets
void RTLSendPacket(NICPacket packet);
/*
this is how it actually works, behind the scenes.
SendPacket is pushing packets to queue, and this function checks
which transmit register is available, and sends the next packet in the queue in it.
if nothing is available, or other error occured, it returns false and dont send. else, it returns true.
*/
bool RTLSendNextPacketInQueue();
// This is the IRQ handler that handles recieving packets
void RTLIrqHandler(IsrFrame registers);
uint8_t* getMac();