summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/arch/amd64/boot.c6
-rw-r--r--src/kernel/arch/amd64/driver/fsroot.c1
-rw-r--r--src/kernel/arch/amd64/driver/rtl8139.c150
-rw-r--r--src/kernel/arch/amd64/driver/rtl8139.h4
-rw-r--r--src/kernel/arch/amd64/interrupts/irq.c5
-rw-r--r--src/kernel/arch/amd64/interrupts/irq.h2
-rw-r--r--src/kernel/arch/amd64/interrupts/isr.c6
-rw-r--r--src/kernel/arch/amd64/pci.c75
-rw-r--r--src/kernel/arch/amd64/pci.h15
-rw-r--r--src/kernel/arch/amd64/port_io.h10
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;
+}
+