From f8e15da04472f5ed6a26e588de4a23cb3e1ba20b Mon Sep 17 00:00:00 2001 From: Anton Kling Date: Thu, 26 Oct 2023 22:35:58 +0200 Subject: Kernel: Fixup rtl8139, add basic ARP and ethernet support Currently the ARP appears to be able to respond to requests. --- Makefile | 4 +- README.md | 7 ++++ drivers/rtl8139.c | 109 +++++++++++++++++++++++++++++++++++++++++------------ drivers/rtl8139.h | 3 ++ network/arp.c | 83 ++++++++++++++++++++++++++++++++++++++++ network/arp.h | 3 ++ network/bytes.c | 10 +++++ network/bytes.h | 5 +++ network/ethernet.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ network/ethernet.h | 5 +++ 10 files changed, 304 insertions(+), 26 deletions(-) create mode 100644 network/arp.c create mode 100644 network/arp.h create mode 100644 network/bytes.c create mode 100644 network/bytes.h create mode 100644 network/ethernet.c create mode 100644 network/ethernet.h diff --git a/Makefile b/Makefile index 49bbb94..04fad93 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC="./sysroot/bin/i686-sb-gcc" AS="./sysroot/bin/i686-sb-as" -OBJ = arch/i386/boot.o init/kernel.o cpu/gdt.o cpu/reload_gdt.o cpu/idt.o cpu/io.o libc/stdio/print.o drivers/keyboard.o log.o drivers/pit.o libc/string/memcpy.o libc/string/strlen.o libc/string/memcmp.o drivers/ata.o libc/string/memset.o cpu/syscall.o read_eip.o libc/exit/assert.o process.o cpu/int_syscall.o libc/string/strcpy.o arch/i386/mmu.o kmalloc.o fs/ext2.o fs/vfs.o fs/devfs.o cpu/spinlock.o random.o libc/string/strcmp.o crypto/ChaCha20/chacha20.o crypto/SHA1/sha1.o fs/tmpfs.o libc/string/isequal.o drivers/pst.o halts.o scalls/ppoll.o scalls/ftruncate.o kubsan.o scalls/mmap.o drivers/serial.o scalls/accept.o scalls/bind.o scalls/socket.o socket.o poll.o fs/fifo.o hashmap/hashmap.o fs/shm.o scalls/shm.o elf.o ksbrk.o sched/scheduler.o scalls/stat.o libc/string/copy.o libc/string/strncpy.o drivers/mouse.o libc/string/strlcpy.o libc/string/strcat.o drivers/vbe.o scalls/msleep.o scalls/uptime.o scalls/mkdir.o drivers/pci.o drivers/rtl8139.o +OBJ = arch/i386/boot.o init/kernel.o cpu/gdt.o cpu/reload_gdt.o cpu/idt.o cpu/io.o libc/stdio/print.o drivers/keyboard.o log.o drivers/pit.o libc/string/memcpy.o libc/string/strlen.o libc/string/memcmp.o drivers/ata.o libc/string/memset.o cpu/syscall.o read_eip.o libc/exit/assert.o process.o cpu/int_syscall.o libc/string/strcpy.o arch/i386/mmu.o kmalloc.o fs/ext2.o fs/vfs.o fs/devfs.o cpu/spinlock.o random.o libc/string/strcmp.o crypto/ChaCha20/chacha20.o crypto/SHA1/sha1.o fs/tmpfs.o libc/string/isequal.o drivers/pst.o halts.o scalls/ppoll.o scalls/ftruncate.o kubsan.o scalls/mmap.o drivers/serial.o scalls/accept.o scalls/bind.o scalls/socket.o socket.o poll.o fs/fifo.o hashmap/hashmap.o fs/shm.o scalls/shm.o elf.o ksbrk.o sched/scheduler.o scalls/stat.o libc/string/copy.o libc/string/strncpy.o drivers/mouse.o libc/string/strlcpy.o libc/string/strcat.o drivers/vbe.o scalls/msleep.o scalls/uptime.o scalls/mkdir.o drivers/pci.o drivers/rtl8139.o network/ethernet.o network/arp.o network/bytes.o CFLAGS = -O2 -fsanitize=vla-bound,shift-exponent,pointer-overflow,shift,signed-integer-overflow,bounds -ggdb -ffreestanding -Wall -Werror -mgeneral-regs-only -Wimplicit-fallthrough -I./libc/include/ -I. INCLUDE=-I./includes/ -I./libc/include/ @@ -23,7 +23,7 @@ nk: qemu-system-i386 -d int -no-reboot -no-shutdown -serial file:./serial.log -hda ext2.img -m 1G -cdrom myos.iso -s run: - qemu-system-i386 -enable-kvm -d int -no-reboot -no-shutdown -chardev stdio,id=char0,logfile=serial.log,signal=off -serial chardev:char0 -hda ext2.img -m 1G -cdrom myos.iso -s + qemu-system-i386 -enable-kvm -netdev user,id=n0,hostfwd=udp:127.0.0.1:6001-:6000 -device rtl8139,netdev=n0 -d int -no-reboot -no-shutdown -chardev stdio,id=char0,logfile=serial.log,signal=off -serial chardev:char0 -hda ext2.img -m 1G -cdrom myos.iso -s myos.iso: myos.bin cp myos.bin isodir/boot diff --git a/README.md b/README.md index 0a6aa67..03340c3 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,16 @@ as GRUB). * libc * Window Manager * Terminal Emulator +* Simple Text Editor(ante) * Very basic shell * Shell utilities(cat, yes, echo etc) +### WIP + +* PCI (somewhat functional for current use) +* rtl8139 Network Card (approaching somewhat complete) +* ARP/Ethernet/IPv4 very much not complete + and some other stuff. ## Screenshot diff --git a/drivers/rtl8139.c b/drivers/rtl8139.c index 0d665b1..0c1606b 100644 --- a/drivers/rtl8139.c +++ b/drivers/rtl8139.c @@ -4,6 +4,7 @@ #include #include #include +#include #define RBSTART 0x30 #define CMD 0x37 @@ -13,31 +14,84 @@ #define TSAD0 0x20 // transmit start address struct PCI_DEVICE rtl8139; -uint8_t device_buffer[8192 + 16]; -uint32_t g_base_address; +uint8_t *device_buffer; uint8_t *send_buffers[4]; -#define ROK (1 << 0) -#define TOK (1 << 2) +struct _INT_PACKET_HEADER { + uint8_t ROK : 1; + uint8_t FAE : 1; + uint8_t CRC : 1; + uint8_t LONG : 1; + uint8_t RUNT : 1; + uint8_t ISE : 1; + uint8_t reserved : 5; + uint8_t BAR : 1; + uint8_t PAM : 1; + uint8_t MAR : 1; +}; + +struct PACKET_HEADER { + union { + uint16_t raw; + struct _INT_PACKET_HEADER data; + }; +}; + +uint16_t current_packet_read = 0; + +void handle_packet(void) { + assert(sizeof(struct _INT_PACKET_HEADER) == sizeof(uint16_t)); + + uint16_t *buf = (uint16_t *)(device_buffer + current_packet_read); + struct PACKET_HEADER packet_header; + packet_header.raw = *buf; + assert(packet_header.data.ROK); + kprintf("packet_header.raw: %x\n", packet_header.raw); + uint16_t packet_length = *(buf + 1); + kprintf("packet_length: %x\n", packet_length); + + uint8_t packet_buffer[8192 + 16]; + if (current_packet_read + packet_length >= 8192 + 16) { + uint32_t first_run = ((uint8_t *)buf + (8192 + 16)) - device_buffer; + memcpy(packet_buffer, buf, first_run); + memcpy(packet_buffer, device_buffer, packet_length - first_run); + } else { + memcpy(packet_buffer, buf, packet_length); + } + + handle_ethernet((uint8_t *)packet_buffer + 4, packet_length); + + // Thanks to exscape + // https://github.com/exscape/exscapeOS/blob/master/src/kernel/net/rtl8139.c + // and the programmers guide + // https://www.cs.usfca.edu/~cruse/cs326f04/RTL8139_ProgrammersGuide.pdf I + // have no clue what this calculation, I can't find anything possibly relating + // to this in the manual, but it does work I guess. + current_packet_read = (current_packet_read + packet_length + 4 + 3) & (~3); + current_packet_read %= 8192 + 16; + outw(rtl8139.gen.base_mem_io + 0x38, current_packet_read - 0x10); +} __attribute__((interrupt)) void rtl8139_handler(void *regs) { (void)regs; uint16_t status = inw(rtl8139.gen.base_mem_io + 0x3e); + kprintf("status: %x\n", status); - if (status & TOK) { + outw(rtl8139.gen.base_mem_io + 0x3E, 0x5); + if (status & (1 << 2)) { kprintf("Packet sent\n"); } - if (status & ROK) { + if (status & (1 << 0)) { kprintf("Received packet\n"); + handle_packet(); } - outw(rtl8139.gen.base_mem_io + 0x3E, 0x5); EOI(0xB); } -int rtl8139_send_data(const struct PCI_DEVICE *device, uint8_t *data, - uint16_t data_size) { +int rtl8139_send_data(uint8_t *data, uint16_t data_size) { + const struct PCI_DEVICE *device = &rtl8139; // FIXME: It should block or fail if there is too little space for the // buffer if (data_size > 0x1000) @@ -54,6 +108,19 @@ int rtl8139_send_data(const struct PCI_DEVICE *device, uint8_t *data, return 1; } +void get_mac_address(uint8_t mac[6]) { + uint32_t base_address = rtl8139.gen.base_mem_io; + // Read the MAC address + uint64_t mac_address; + { + uint32_t low_mac = inl(base_address); + uint16_t high_mac = inw(base_address + 0x4); + mac_address = ((uint64_t)high_mac << 32) | low_mac; + } + kprintf("mac_address: %x\n", mac_address); + memcpy(mac, &mac_address, sizeof(uint8_t[6])); +} + uint8_t rtl8139_get_transmit_status(uint32_t base_address) { uint32_t status_register = inl(base_address + 0x3E); if ((status_register >> 3) & 0x1) @@ -76,14 +143,10 @@ void rtl8139_init(void) { uint32_t base_address = rtl8139.gen.base_mem_io; uint8_t interrupt_line = pci_get_interrupt_line(&rtl8139); - // Read the MAC address - uint64_t mac_address; - { - uint32_t low_mac = inl(base_address); - uint16_t high_mac = inw(base_address + 0x4); - mac_address = ((uint64_t)high_mac << 32) | low_mac; - } - kprintf("mac_address: %x\n", mac_address); + // Enable bus mastering + uint32_t register1 = pci_config_read32(&rtl8139, 0, 0x4); + register1 |= (1 << 2); + pci_config_write32(&rtl8139, 0, 0x4, register1); // Turning on the device outb(base_address + 0x52, 0x0); @@ -92,19 +155,21 @@ void rtl8139_init(void) { outb(base_address + CMD, 0x10); for (; 0 != (inb(base_address + CMD) & 0x10);) ; - + device_buffer = ksbrk(8192 + 16); + memset(device_buffer, 0, 8192 + 16); // Setupt the recieve buffer uint32_t rx_buffer = (uint32_t)virtual_to_physical(device_buffer, NULL); outl(base_address + RBSTART, rx_buffer); // Set IMR + ISR - outw(base_address + IMR, (1 << 2) | (1 << 3)); + outw(base_address + IMR, (1 << 2) | (1 << 3) | (1 << 0)); + // Set transmit and reciever enable outb(base_address + 0x37, (1 << 2) | (1 << 3)); // Configure the recieve buffer outl(base_address + 0x44, - 0xf | (1 << 7)); // (1 << 7) is the WRAP bit, 0xf is AB+AM+APM+AAP + 0xf); // 0xf is AB+AM+APM+AAP install_handler(rtl8139_handler, INT_32_INTERRUPT_GATE(0x0), 0x20 + interrupt_line); @@ -114,10 +179,6 @@ void rtl8139_init(void) { for (int i = 0; i < 4; i++) send_buffers[i] = ksbrk(0x1000); asm("sti"); - for (int i = 0; i < 10; i++) { - char buffer[512]; - rtl8139_send_data(&rtl8139, (uint8_t *)buffer, 512); - } for (;;) asm("sti"); } diff --git a/drivers/rtl8139.h b/drivers/rtl8139.h index 1f3d84e..6e13fdd 100644 --- a/drivers/rtl8139.h +++ b/drivers/rtl8139.h @@ -1 +1,4 @@ +#include +void get_mac_address(uint8_t mac[6]); void rtl8139_init(void); +int rtl8139_send_data(uint8_t *data, uint16_t data_size); diff --git a/network/arp.c b/network/arp.c new file mode 100644 index 0000000..eb8aca3 --- /dev/null +++ b/network/arp.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include + +struct ARP_DATA { + uint16_t htype; // Hardware type + uint16_t ptype; // Protocol type + uint8_t hlen; // Hardware address length (Ethernet = 6) + uint8_t plen; // Protocol address length (IPv4 = 4) + uint16_t opcode; // ARP Operation Code + uint8_t srchw[6]; // Source hardware address - hlen bytes (see above) + uint8_t srcpr[4]; // Source protocol address - plen bytes (see above). + // If IPv4 can just be a "u32" type. + uint8_t dsthw[6]; // Destination hardware address - hlen bytes (see above) + uint8_t dstpr[4]; // Destination protocol address - plen bytes (see + // above). If IPv4 can just be a "u32" type. +}; + +// FIXME: This is hardcoded, don't do this. +uint8_t ip_address[4] = {10, 0, 2, 15}; + +void print_mac(const char *str, uint8_t *mac) { + kprintf("%s: ", str); + for (int i = 0; i < 6; i++) { + kprintf("%x", mac[i]); + if (5 != i) + kprintf(":"); + } + kprintf("\n"); +} + +void print_ip(const char *str, uint8_t *ip) { + kprintf("%s: ", str); + for (int i = 0; i < 4; i++) { + kprintf("%d", ip[i]); + if (3 != i) + kprintf("."); + } + kprintf("\n"); +} + +void handle_arp(uint8_t *payload) { + struct ARP_DATA *data = (struct ARP_DATA *)payload; + + // Assert that communication is over ethernet + assert(1 == ntohs(data->htype)); + // Assert it is a request + assert(0x0001 == ntohs(data->opcode)); + // Assert that request uses IP + assert(0x0800 == ntohs(data->ptype)); + + assert(6 == data->hlen); + assert(4 == data->plen); + print_mac("srchw: ", data->srchw); + print_ip("srcpr: ", data->srcpr); + + print_mac("dsthw: ", data->dsthw); + print_ip("dstpr: ", data->dstpr); + + uint8_t mac[6]; + get_mac_address(mac); + print_mac("THIS DEVICE MAC: ", mac); + assert(0 == memcmp(data->dstpr, ip_address, sizeof(uint8_t[4]))); + + // Now we have to construct a ARP response + struct ARP_DATA response; + response.htype = htons(1); + response.ptype = htons(0x0800); + response.opcode = htons(0x00002); + response.hlen = 6; + response.plen = 4; + get_mac_address(response.srchw); + memcpy(response.srcpr, ip_address, sizeof(uint8_t[4])); + + memcpy(response.dsthw, data->srchw, sizeof(uint8_t[6])); + memcpy(response.dstpr, data->srcpr, sizeof(uint8_t[4])); + + send_ethernet_packet(data->srchw, 0x0806, (uint8_t*)&response, sizeof(response)); +} diff --git a/network/arp.h b/network/arp.h new file mode 100644 index 0000000..1e8df1b --- /dev/null +++ b/network/arp.h @@ -0,0 +1,3 @@ +#include + +void handle_arp(uint8_t *payload); diff --git a/network/bytes.c b/network/bytes.c new file mode 100644 index 0000000..94afa73 --- /dev/null +++ b/network/bytes.c @@ -0,0 +1,10 @@ +#include + +uint16_t ntohs(uint16_t net) { return (net >> 8) | (net << 8); } + +uint16_t htons(uint16_t net) { return (net >> 8) | (net << 8); } + +uint32_t htonl(uint32_t net) { + return (((net & 0x000000FF) << 24) | ((net & 0x0000FF00) << 8) | + ((net & 0x00FF0000) >> 8) | ((net & 0xFF000000) >> 24)); +} diff --git a/network/bytes.h b/network/bytes.h new file mode 100644 index 0000000..c291589 --- /dev/null +++ b/network/bytes.h @@ -0,0 +1,5 @@ +#include + +uint16_t ntohs(uint16_t net); +uint16_t htons(uint16_t net); +uint32_t htonl(uint32_t net); diff --git a/network/ethernet.c b/network/ethernet.c new file mode 100644 index 0000000..ae8e814 --- /dev/null +++ b/network/ethernet.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include + +struct ETHERNET_HEADER { + uint8_t mac_dst[6]; + uint8_t mac_src[6]; + uint16_t type; +}; + +uint32_t crc32(const char *buf, size_t len) { + static uint32_t table[256]; + static int have_table = 0; + uint32_t rem; + uint8_t octet; + int i, j; + const char *p, *q; + + if (have_table == 0) { + for (i = 0; i < 256; i++) { + rem = i; + for (j = 0; j < 8; j++) { + if (rem & 1) { + rem >>= 1; + rem ^= 0xedb88320; + } else + rem >>= 1; + } + table[i] = rem; + } + have_table = 1; + } + + uint32_t crc = 0xFFFFFFFF; + q = buf + len; + for (p = buf; p < q; p++) { + octet = *p; + crc = (crc >> 8) ^ table[(crc & 0xff) ^ octet]; + } + return ~crc; +} + +void handle_ethernet(uint8_t *packet, uint64_t packet_length) { + struct ETHERNET_HEADER *eth_header = (struct ETHERNET_HEADER *)packet; + packet += sizeof(struct ETHERNET_HEADER); + uint8_t *payload = packet; + packet += packet_length - sizeof(struct ETHERNET_HEADER); + uint32_t crc = *((uint32_t *)packet - 1); + kprintf("PACKET crc: %x\n", crc); + kprintf("OUR OWN CALCULATED crc: %x\n", + crc32((const char *)eth_header, (packet_length - 4))); + + kprintf("mac dst: "); + for (int i = 0; i < 6; i++) + kprintf("%x", eth_header->mac_dst[i]); + kprintf("\n"); + kprintf("mac src: "); + for (int i = 0; i < 6; i++) + kprintf("%x", eth_header->mac_src[i]); + kprintf("\n"); + + uint16_t type = ntohs(eth_header->type); + kprintf("Etheretype: %x\n", type); + switch (type) { + case 0x0806: + handle_arp(payload); + break; + case 0x0800: + kprintf("IPV4 message\n"); + break; + default: + kprintf("Can't handle ethernet type\n"); + break; + } +} + +void send_ethernet_packet(uint8_t mac_dst[6], uint16_t type, uint8_t *payload, + uint64_t payload_length) { + // FIXME: Janky allocation, do this better + uint64_t buffer_size = + sizeof(struct ETHERNET_HEADER) + payload_length + sizeof(uint32_t); + uint8_t *buffer = kmalloc(buffer_size); + uint8_t *buffer_start = buffer; + struct ETHERNET_HEADER *eth_header = (struct ETHERNET_HEADER *)buffer; + buffer += sizeof(struct ETHERNET_HEADER); + memcpy(buffer, payload, payload_length); + buffer += payload_length; + + memcpy(eth_header->mac_dst, mac_dst, sizeof(uint8_t[6])); + get_mac_address(eth_header->mac_src); + eth_header->type = htons(type); + *(uint32_t *)(buffer) = + htonl(crc32((const char *)buffer_start, buffer_size - 4)); + + assert(rtl8139_send_data(buffer_start, buffer_size)); + kprintf("sent data\n"); +} diff --git a/network/ethernet.h b/network/ethernet.h new file mode 100644 index 0000000..88a4dc6 --- /dev/null +++ b/network/ethernet.h @@ -0,0 +1,5 @@ +#include + +void handle_ethernet(uint8_t *packet, uint64_t packet_length); +void send_ethernet_packet(uint8_t mac_dst[6], uint16_t type, uint8_t *payload, + uint64_t payload_length); -- cgit v1.2.3