diff options
Diffstat (limited to 'src/kernel/arch')
-rw-r--r-- | src/kernel/arch/amd64/boot.c | 6 | ||||
-rw-r--r-- | src/kernel/arch/amd64/driver/fsroot.c | 1 | ||||
-rw-r--r-- | src/kernel/arch/amd64/driver/rtl8139.c | 150 | ||||
-rw-r--r-- | src/kernel/arch/amd64/driver/rtl8139.h | 4 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/irq.c | 5 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/irq.h | 2 | ||||
-rw-r--r-- | src/kernel/arch/amd64/interrupts/isr.c | 6 | ||||
-rw-r--r-- | src/kernel/arch/amd64/pci.c | 75 | ||||
-rw-r--r-- | src/kernel/arch/amd64/pci.h | 15 | ||||
-rw-r--r-- | src/kernel/arch/amd64/port_io.h | 10 |
10 files changed, 270 insertions, 4 deletions
diff --git a/src/kernel/arch/amd64/boot.c b/src/kernel/arch/amd64/boot.c index e92b040..ed19b79 100644 --- a/src/kernel/arch/amd64/boot.c +++ b/src/kernel/arch/amd64/boot.c @@ -7,6 +7,7 @@ #include <kernel/arch/amd64/driver/video.h> #include <kernel/arch/amd64/interrupts/idt.h> #include <kernel/arch/amd64/interrupts/irq.h> +#include <kernel/arch/amd64/pci.h> #include <kernel/arch/amd64/tty/tty.h> #include <kernel/arch/generic.h> #include <kernel/mem/alloc.h> @@ -71,10 +72,11 @@ void kmain_early(void *mbi) { ps2_init(); serial_init(); video_init(vid); - - kprintf("ata...\n"); pata_init(); + kprintf("pci...\n"); + pci_init(); + kprintf("running init...\n"); process_seed(init.addr, init.len); process_switch_any(); diff --git a/src/kernel/arch/amd64/driver/fsroot.c b/src/kernel/arch/amd64/driver/fsroot.c index 3bbf2d1..2344205 100644 --- a/src/kernel/arch/amd64/driver/fsroot.c +++ b/src/kernel/arch/amd64/driver/fsroot.c @@ -14,6 +14,7 @@ static int handle(struct vfs_request *req) { "com1\0" "ps2/\0" "ata/\0" + "eth/\0" "video/"; if (!req->caller) return -1; switch (req->type) { diff --git a/src/kernel/arch/amd64/driver/rtl8139.c b/src/kernel/arch/amd64/driver/rtl8139.c new file mode 100644 index 0000000..d9e5af0 --- /dev/null +++ b/src/kernel/arch/amd64/driver/rtl8139.c @@ -0,0 +1,150 @@ +#include <kernel/arch/amd64/driver/rtl8139.h> +#include <kernel/arch/amd64/pci.h> +#include <kernel/arch/amd64/port_io.h> +#include <kernel/mem/virt.h> +#include <kernel/panic.h> +#include <kernel/vfs/request.h> +#include <stdbool.h> + +static void accept(struct vfs_request *req); +static struct vfs_backend backend = BACKEND_KERN(accept); +static struct vfs_request *blocked_on = NULL; + + +enum { + MAC = 0, + RBSTART = 0x30, + CMD = 0x37, + CAPR = 0x38, + CBR = 0x3A, + INTRMASK = 0x3C, + INTRSTATUS = 0x3E, + RCR = 0x44, /* receive configure */ + CONFIG1 = 0x52, +}; + +static uint16_t iobase; + +#define buflen_shift 3 +#define rxbuf_baselen ((8 * 1024) << buflen_shift) +static char rxbuf[rxbuf_baselen + 16 + 1500]; +static size_t rxpos; + +static void rx_irq_enable(bool v) { + uint16_t mask = 1 | 4; /* rx/tx ok */ + port_out16(iobase + INTRMASK, v ? mask : 0); +} + +void rtl8139_init(uint32_t bdf) { + if (iobase) panic_unimplemented(); /* multiple devices */ + iobase = pcicfg_iobase(bdf); + + /* also includes the status, because i have only implemented w32 */ + uint32_t cmd = pcicfg_r32(bdf, PCICFG_CMD); + cmd |= 1 << 2; /* bus mastering */ + pcicfg_w32(bdf, PCICFG_CMD, cmd); + + + port_out8(iobase + CONFIG1, 0); /* power on */ + + port_out8(iobase + CMD, 0x10); /* software reset */ + while (port_in8(iobase + CMD) & 0x10); + + assert((long)(void*)rxbuf <= 0xFFFFFFFF); + port_out32(iobase + RBSTART, (long)(void*)rxbuf); + + uint32_t rcr = 0; + // rcr |= 1 << 0; /* accept all packets */ + rcr |= 1 << 1; /* accept packets with our mac */ + rcr |= 1 << 2; /* accept multicast */ + rcr |= 1 << 3; /* accept broadcast */ + rcr |= 1 << 7; /* WARP */ + rcr |= buflen_shift << 11; + rcr |= 7 << 13; /* no rx threshold, copy whole packets */ + port_out32(iobase + RCR, rcr); + + port_out8(iobase + CMD, 0xC); /* enable RX TX */ + + rx_irq_enable(false); + + uint64_t mac = (((uint64_t)port_in32(iobase + MAC + 4) & 0xFFFF) << 32) + port_in32(iobase + MAC); + kprintf("rtl8139 mac %012x\n", mac); + + vfs_mount_root_register("/eth", &backend); +} + +void rtl8139_irq(void) { + uint16_t status = port_in16(iobase + INTRSTATUS); + // TODO don't assume this is an rx irq + + do { + if (blocked_on) { + accept(blocked_on); + blocked_on = blocked_on->postqueue_next; + } else { + rx_irq_enable(false); + break; + } + } while (!(port_in8(iobase + CMD) & 1)); /* bit 0 - Rx Buffer Empty */ + + //kprintf("rxpos %x cbr %x\n", rxpos, port_in16(iobase + CBR)); + port_out16(iobase + INTRSTATUS, status); +} + +static int try_rx(struct pagedir *pages, void __user *dest, size_t dlen) { + uint16_t flags, size, pktsize; + /* bit 0 - Rx Buffer Empty */ + if (port_in8(iobase + CMD) & 1) return -1; + + /* https://github.com/qemu/qemu/blob/04ddcda6a/hw/net/rtl8139.c#L1169 */ + /* https://www.cs.usfca.edu/~cruse/cs326f04/RTL8139D_DataSheet.pdf page 12 + * bits of interest: + * 0 - Receive OK + * 14 - Physical Address Matched */ + flags = *(uint16_t*)(rxbuf + rxpos); + rxpos += 2; + /* doesn't include the header, includes a 4 byte crc */ + size = *(uint16_t*)(rxbuf + rxpos); + rxpos += 2; + if (size == 0) panic_invalid_state(); + pktsize = size - 4; + + // kprintf("packet size 0x%x, flags 0x%x, rxpos %x\n", size, flags, rxpos - 4); + virt_cpy_to(pages, dest, rxbuf + rxpos, pktsize); + + rxpos += size; + rxpos = (rxpos + 3) & ~3; + while (rxpos >= rxbuf_baselen) rxpos -= rxbuf_baselen; + /* the device adds the 0x10 back, it's supposed to avoid overflow */ + port_out16(iobase + CAPR, rxpos - 0x10); + return pktsize; +} + +static void accept(struct vfs_request *req) { + switch (req->type) { + long ret; + case VFSOP_OPEN: + vfsreq_finish_short(req, req->input.len == 0 ? 0 : -1); + break; + case VFSOP_READ: + if (!req->caller) { + vfsreq_finish_short(req, -1); + break; + } + ret = try_rx(req->caller->pages, req->output.buf, req->output.len); + if (ret < 0) { + // TODO this is a pretty common pattern in drivers, try to make it unneeded + assert(!req->postqueue_next); + struct vfs_request **slot = &blocked_on; + while (*slot) slot = &(*slot)->postqueue_next; + *slot = req; + rx_irq_enable(true); + } else { + vfsreq_finish_short(req, ret); + } + break; + default: + vfsreq_finish_short(req, -1); + break; + } +} diff --git a/src/kernel/arch/amd64/driver/rtl8139.h b/src/kernel/arch/amd64/driver/rtl8139.h new file mode 100644 index 0000000..2677d82 --- /dev/null +++ b/src/kernel/arch/amd64/driver/rtl8139.h @@ -0,0 +1,4 @@ +#pragma once +#include <stdint.h> +void rtl8139_init(uint32_t bdf); +void rtl8139_irq(void); diff --git a/src/kernel/arch/amd64/interrupts/irq.c b/src/kernel/arch/amd64/interrupts/irq.c index 1760825..818ea8e 100644 --- a/src/kernel/arch/amd64/interrupts/irq.c +++ b/src/kernel/arch/amd64/interrupts/irq.c @@ -24,11 +24,12 @@ void irq_init(void) { port_out8(PIC2+1, 0x1); uint16_t mask = 0xffff; - mask &= ~(1 << IRQ_PIT); mask &= ~(1 << 2); // cascade - mask &= ~(1 << IRQ_PS2KB); mask &= ~(1 << IRQ_COM1); + mask &= ~(1 << IRQ_PIT); + mask &= ~(1 << IRQ_PS2KB); mask &= ~(1 << IRQ_PS2MOUSE); + mask &= ~(1 << IRQ_RTL8139); port_out8(PIC1+1, mask & 0xff); port_out8(PIC2+1, (mask >> 8) & 0xff); diff --git a/src/kernel/arch/amd64/interrupts/irq.h b/src/kernel/arch/amd64/interrupts/irq.h index 3243224..f949824 100644 --- a/src/kernel/arch/amd64/interrupts/irq.h +++ b/src/kernel/arch/amd64/interrupts/irq.h @@ -8,5 +8,7 @@ #define IRQ_COM1 4 #define IRQ_PS2MOUSE 12 +#define IRQ_RTL8139 11 + void irq_init(void); void irq_eoi(uint8_t line); diff --git a/src/kernel/arch/amd64/interrupts/isr.c b/src/kernel/arch/amd64/interrupts/isr.c index d1c2b26..17dce6e 100644 --- a/src/kernel/arch/amd64/interrupts/isr.c +++ b/src/kernel/arch/amd64/interrupts/isr.c @@ -1,4 +1,5 @@ #include <kernel/arch/amd64/driver/ps2.h> +#include <kernel/arch/amd64/driver/rtl8139.h> #include <kernel/arch/amd64/driver/serial.h> #include <kernel/arch/amd64/interrupts/irq.h> #include <kernel/arch/amd64/interrupts/isr.h> @@ -45,6 +46,11 @@ void isr_stage3(int interrupt, uint64_t *stackframe) { irq_eoi(IRQ_COM1); return; + case IRQ_IBASE + IRQ_RTL8139: + rtl8139_irq(); + irq_eoi(interrupt - IRQ_IBASE); + return; + default: if ((stackframe[1] & 0x3) == 0) { log_interrupt(interrupt, stackframe); diff --git a/src/kernel/arch/amd64/pci.c b/src/kernel/arch/amd64/pci.c new file mode 100644 index 0000000..93fffec --- /dev/null +++ b/src/kernel/arch/amd64/pci.c @@ -0,0 +1,75 @@ +#include <kernel/arch/amd64/driver/rtl8139.h> +#include <kernel/arch/amd64/interrupts/irq.h> +#include <kernel/arch/amd64/pci.h> +#include <kernel/arch/amd64/port_io.h> +#include <kernel/arch/generic.h> +#include <kernel/panic.h> + +static const uint16_t CONFIG_ADDRESS = 0xCF8; +static const uint16_t CONFIG_DATA = 0xCFC; + + +uint8_t pcicfg_r8(uint32_t bdf, uint32_t offset) { + return pcicfg_r32(bdf, offset) >> ((offset & 3) * 8); +} + +uint16_t pcicfg_r16(uint32_t bdf, uint32_t offset) { + return pcicfg_r32(bdf, offset) >> ((offset & 2) * 8); +} + +uint32_t pcicfg_r32(uint32_t bdf, uint32_t offset) { + port_out32(CONFIG_ADDRESS, 0x80000000 | bdf | (offset & ~3)); + return port_in32(CONFIG_DATA); +} + +void pcicfg_w32(uint32_t bdf, uint32_t offset, uint32_t value) { + port_out32(CONFIG_ADDRESS, 0x80000000 | bdf | (offset & ~3)); + port_out32(CONFIG_DATA, value); +} + + +uint16_t pcicfg_iobase(uint32_t bdf) { + /* cuts corners, assumes header type 0, etc */ + uint32_t bar = pcicfg_r32(bdf, 0x10); + if (!(bar & 1)) + panic_unimplemented(); /* not an io bar */ + return bar & ~3; +} + +static uint32_t bdf_of(uint32_t bus, uint32_t device, uint32_t func) { + return (bus << 16) | (device << 11) | (func << 8); +} + +static void scan_bus(uint32_t bus) { + for (int slot = 0; slot < 32; slot++) { + int fn_amt = 1; + for (int fn = 0; fn < fn_amt; fn++) { + uint32_t bdf = bdf_of(bus, slot, fn); + uint32_t id = pcicfg_r32(bdf, 0); + if (id == 0xFFFFFFFF) break; + kprintf("pci %02x:%02x.%x\t%x\n", bus, slot, fn, id); + + uint8_t hdr_type = pcicfg_r8(bdf, 0xE); + if (hdr_type & 0x80) { + fn_amt = 8; + hdr_type &= ~0x80; + } + if (hdr_type != 0) { + kprintf("pci: skipping unknown header %x\n", hdr_type); + continue; + } + + if (id == 0x813910ec) { + kprintf("rtl irq %u\n", pcicfg_r8(bdf, 0x3C)); + pcicfg_w32(bdf, 0x3C, IRQ_RTL8139); + kprintf("rtl irq %u\n", pcicfg_r8(bdf, 0x3C)); + rtl8139_init(bdf); + } + } + } +} + +void pci_init(void) { + scan_bus(0); + // TODO multiple host controllers +} diff --git a/src/kernel/arch/amd64/pci.h b/src/kernel/arch/amd64/pci.h new file mode 100644 index 0000000..0efe24e --- /dev/null +++ b/src/kernel/arch/amd64/pci.h @@ -0,0 +1,15 @@ +#pragma once +#include <stdint.h> + +#define PCICFG_CMD 0x4 + +uint8_t pcicfg_r8(uint32_t bdf, uint32_t offset); +uint16_t pcicfg_r16(uint32_t bdf, uint32_t offset); +uint32_t pcicfg_r32(uint32_t bdf, uint32_t offset); + +void pcicfg_w16(uint32_t bdf, uint32_t offset, uint32_t value); +void pcicfg_w32(uint32_t bdf, uint32_t offset, uint32_t value); + +uint16_t pcicfg_iobase(uint32_t bdf); + +void pci_init(void); diff --git a/src/kernel/arch/amd64/port_io.h b/src/kernel/arch/amd64/port_io.h index eac9331..6cf6cc0 100644 --- a/src/kernel/arch/amd64/port_io.h +++ b/src/kernel/arch/amd64/port_io.h @@ -8,6 +8,10 @@ static inline void port_out16(uint16_t port, uint16_t val) { asm volatile("outw %0, %1" : : "a" (val), "Nd" (port)); } +static inline void port_out32(uint16_t port, uint32_t val) { + asm volatile("outl %0, %1" : : "a" (val), "Nd" (port)); +} + static inline uint8_t port_in8(uint16_t port) { uint8_t val; asm volatile("inb %1, %0" : "=a" (val) : "Nd" (port)); @@ -20,3 +24,9 @@ static inline uint16_t port_in16(uint16_t port) { return val; } +static inline uint32_t port_in32(uint16_t port) { + uint32_t val; + asm volatile("inl %1, %0" : "=a" (val) : "Nd" (port)); + return val; +} + |