blob: 79e8d30e1a7d7d958aee94992ff1d06534257282 [file] [log] [blame]
/*
* Copyright (c) 2009 The Regents of the University of California
* See LICENSE for details.
*/
#ifdef __SHARC__
#pragma nosharc
#endif
#include <arch/mmu.h>
#include <arch/x86.h>
#include <arch/smp.h>
#include <arch/apic.h>
#include <arch/pci.h>
#include "ne2k.h"
#include <ros/memlayout.h>
#include <atomic.h>
#include <stdio.h>
#include <string.h>
#include <trap.h>
#include <kmalloc.h>
#include <pmap.h>
#include <time.h>
/** @file
* @brief NE2K Driver Sketch
*
* EXPERIMENTAL.
*
* Rough driver. Appears to work in QEMU. Probably completely broken under heavy load.
*
* @author Paul Pearce <pearce@eecs.berkeley.edu>
*
* @todo Everything
*/
#define NE2K_RESET_R_ADDR 0x1f
#define NE2K_PG0_RW_CR 0x00
#define NE2K_PG0_RW_ISR 0x07
#define NE2K_PG0_W_IMR 0x0F
#define NE2K_PG0_W_PSTRT 0x1
#define NE2K_PG0_W_PSTP 0x2
#define NE2K_PG0_W_RCR 0xC
#define NE2K_PG0_R_RSR 0xC
#define NE2K_PG0_R_TSR 0x4
#define NE2K_PG0_W_TCR 0xD
#define NE2K_PG1_RW_PAR 0x1
#define NE2K_PG0_W_RSAR0 0x08
#define NE2K_PG0_W_RSAR1 0x09
#define NE2K_PG0_W_RBCR0 0x0A
#define NE2K_PG0_W_RBCR1 0x0B
#define NE2K_PG0_W_TBCR0 0x05
#define NE2K_PG0_W_TBCR1 0x06
#define NE2K_PG0_W_TPSR 0x04
#define NE2K_PG0_W_DCR 0x0E
#define NE2K_PG1_RW_CURR 0x07
#define NE2K_PG0_RW_BNRY 0x03
#define NE2K_PAGE_SIZE 256
#define NE2K_PMEM_START (16*1024)
#define NE2K_PMEM_SIZE (32*1024)
#define NE2K_NUM_PAGES ((NE2K_PMEM_SIZE - NE2K_PMEM_START) / NE2K_PAGE_SIZE)
#define NE2K_NUM_RECV_PAGES (NE2K_NUM_PAGES / 2)
#define NE2K_NUM_SEND_PAGES (NE2K_NUM_PAGES / 2)
#define NE2K_FIRST_RECV_PAGE (NE2K_PMEM_START / NE2K_PAGE_SIZE)
#define NE2K_LAST_RECV_PAGE NE2K_FIRST_RECV_PAGE + NE2K_NUM_RECV_PAGES
#define NE2K_FIRST_SEND_PAGE NE2K_LAST_RECV_PAGE + 1
#define SET_PAGE_0() (inb(ne2k_io_base_addr + NE2K_PG0_RW_CR) & 0x3F)
static uint32_t ne2k_irq; // Fix this
static uint32_t ne2k_io_base_addr;
static void *base_page;
static uint32_t num_pages = 0;
void ne2k_init() {
if (ne2k_scan_pci() < 0) return;
ne2k_mem_alloc();
ne2k_configure_nic();
ne2k_read_mac();
printk("Network Card MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
device_mac[0],device_mac[1],device_mac[2],
device_mac[3],device_mac[4],device_mac[5]);
//ne2k_test_interrupts();
send_frame = &ne2k_send_frame;
ne2k_setup_interrupts();
eth_up = 1;
return;
}
int ne2k_scan_pci() {
struct pci_device *pcidev;
uint32_t result;
printk("Searching for NE2000 Network device...");
STAILQ_FOREACH(pcidev, &pci_devices, all_dev) {
/* Ignore non NE2K Devices */
if ((pcidev->ven_id != NE2K_VENDOR_ID) ||
(pcidev->dev_id != NE2K_DEV_ID))
continue;
printk(" found on BUS %x DEV %x\n", pcidev->bus, pcidev->dev);
/* Find the IRQ */
ne2k_irq = pcidev->irqline;
ne2k_debug("-->IRQ: %u\n", ne2k_irq);
/* Loop over the BARs */
for (int k = 0; k <= 5; k++) {
int reg = 4 + k;
result = pcidev_read32(pcidev, reg << 2); // SHAME!
if (result == 0) // (0 denotes no valid data)
continue;
// Read the bottom bit of the BAR.
if (result & PCI_BAR_IO) {
result = result & PCI_BAR_IO_MASK;
ne2k_debug("-->BAR%u: %s --> %x\n", k, "IO", result);
} else {
result = result & PCI_BAR_MEM_MASK;
ne2k_debug("-->BAR%u: %s --> %x\n", k, "MEM", result);
}
// TODO Switch to memory mapped instead of IO?
if (k == 0) // BAR0 denotes the IO Addr for the device
ne2k_io_base_addr = result;
}
return 0;
}
printk(" not found. No device configured.\n");
return -1;
}
void ne2k_configure_nic() {
ne2k_debug("-->Configuring Device.\n");
// Reset. Yes reading from this addr resets it
inb(ne2k_io_base_addr + NE2K_RESET_R_ADDR);
// Configure
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, 0x22);
outb(ne2k_io_base_addr + NE2K_PG0_W_PSTRT, NE2K_FIRST_RECV_PAGE);
outb(ne2k_io_base_addr + NE2K_PG0_RW_BNRY, NE2K_FIRST_RECV_PAGE + 1);
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, (0x22 & 0x3F) | 0x40);
outb(ne2k_io_base_addr + NE2K_PG1_RW_CURR, NE2K_FIRST_RECV_PAGE + 2);
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, 0x22);
outb(ne2k_io_base_addr + NE2K_PG0_W_PSTP, NE2K_LAST_RECV_PAGE);
outb(ne2k_io_base_addr + NE2K_PG0_W_DCR, 0x94);
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, 0x22);
outb(ne2k_io_base_addr + NE2K_PG0_W_RCR, 0xDF);
outb(ne2k_io_base_addr + NE2K_PG0_W_TCR, 0xE0);
//uint8_t isr = inb(ne2k_io_base_addr + NE2K_PG0_RW_ISR);
//cprintf("isr: %x\n", isr);
return;
}
void ne2k_setup_interrupts() {
ne2k_debug("-->Setting interrupts.\n");
// Kernel based interrupt stuff
register_dev_irq(ne2k_irq, ne2k_interrupt_handler, 0);
SET_PAGE_0();
outb(ne2k_io_base_addr + NE2K_PG0_W_IMR, 0xBF);
outb(ne2k_io_base_addr + NE2K_PG0_RW_ISR, 0xFF);
return;
}
void ne2k_mem_alloc() {
num_pages = ROUNDUP(NE2K_NUM_PAGES * NE2K_PAGE_SIZE, PGSIZE) / PGSIZE;
base_page = get_cont_pages(LOG2_UP(num_pages), 0);
}
void ne2k_read_mac() {
uint8_t cr = inb(ne2k_io_base_addr + NE2K_PG0_RW_CR);
// Set correct bits
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, 0xA);
outb(ne2k_io_base_addr + NE2K_PG0_W_RSAR0, 0x0);
outb(ne2k_io_base_addr + NE2K_PG0_W_RSAR1, 0x0);
outb(ne2k_io_base_addr + NE2K_PG0_W_RBCR0, 0x6);
outb(ne2k_io_base_addr + NE2K_PG0_W_RBCR1, 0x0);
for (int i = 0; i < 6; i++)
device_mac[i] = inb(ne2k_io_base_addr + 0x10) & inb(ne2k_io_base_addr + 0x10);
// Set page 1
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, (cr & 0x3F) | 0x40);
for (int i = 0; i < 6; i++)
outb(ne2k_io_base_addr + NE2K_PG1_RW_PAR + i, device_mac[i]);
ne2k_debug("-->DEVICE MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", 0xFF & device_mac[0], 0xFF & device_mac[1],
0xFF & device_mac[2], 0xFF & device_mac[3],
0xFF & device_mac[4], 0xFF & device_mac[5]);
// Restore old setting.
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, cr);
return;
}
void ne2k_test_interrupts() {
cprintf("Generating Interrupt...\n");
outb(ne2k_io_base_addr + 0x0A, 0x00);
outb(ne2k_io_base_addr + 0x0B, 0x00);
outb(ne2k_io_base_addr + 0x00, 0x0A);
udelay(10000000);
cprintf("Generating Interrupt again...\n");
outb(ne2k_io_base_addr + 0x0A, 0x00);
outb(ne2k_io_base_addr + 0x0B, 0x00);
outb(ne2k_io_base_addr + 0x00, 0x0A);
udelay(10000000);
}
// We need to evaluate this routine in terms of concurrency.
// We also need to figure out whats up with different core interrupts
void ne2k_interrupt_handler(struct hw_trapframe *hw_tf, void *data)
{
ne2k_interrupt_debug("\nNE2K interrupt on core %u!\n", lapic_get_id());
SET_PAGE_0();
uint8_t isr= inb(ne2k_io_base_addr + NE2K_PG0_RW_ISR);
ne2k_interrupt_debug("isr: %x\n", isr);
while (isr != 0x0) {
// TODO: Other interrupt cases.
if (isr & 0x1) {
ne2k_interrupt_debug("-->Packet received.\n");
ne2k_handle_rx_packet();
}
SET_PAGE_0();
// Clear interrupts
isr = inb(ne2k_io_base_addr + NE2K_PG0_RW_ISR);
outb(ne2k_io_base_addr + NE2K_PG0_RW_ISR, isr);
}
ne2k_handle_rx_packet();
return;
}
// @TODO: Is this broken? Didn't change it after kmalloc changed
void ne2k_handle_rx_packet() {
SET_PAGE_0();
uint8_t bound = inb(ne2k_io_base_addr + NE2K_PG0_RW_BNRY);
uint8_t cr = inb(ne2k_io_base_addr + NE2K_PG0_RW_CR);
// Set page 1
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, (cr & 0x3F) | 0x40);
uint8_t next = inb(ne2k_io_base_addr + NE2K_PG1_RW_CURR);
// Restore old setting.
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, cr);
uint8_t start = NE2K_FIRST_RECV_PAGE;
uint8_t stop = NE2K_LAST_RECV_PAGE;
// Broken mult packets?
if (((bound + 1) == next) || (((bound + 1) == stop) && (start == next))) {
ne2k_debug("NO PACKET TO PROCESS\n");
return;
}
uint32_t kmalloc_size;
if (MAX_FRAME_SIZE % NE2K_PAGE_SIZE) {
kmalloc_size = ((MAX_FRAME_SIZE / NE2K_PAGE_SIZE) + 1) * NE2K_PAGE_SIZE;
} else {
kmalloc_size = MAX_FRAME_SIZE;
}
char *rx_buffer = kmalloc(kmalloc_size, 0);
if (rx_buffer == NULL) panic ("Can't allocate page for incoming packet.");
uint8_t curr = bound + 1;
uint8_t header[4];
uint16_t packet_len = 0xFFFF;
uint16_t page_count = 0;
for (int i = 0, n = 0; i < (MAX_FRAME_SIZE / NE2K_PAGE_SIZE); i++) {
if (curr == stop)
curr = start;
outb(ne2k_io_base_addr + NE2K_PG0_W_RSAR0, 0x0);
outb(ne2k_io_base_addr + NE2K_PG0_W_RSAR1, curr);
// Fix this. Its hard coded to 256
outb(ne2k_io_base_addr + NE2K_PG0_W_RBCR0, 0);
outb(ne2k_io_base_addr + NE2K_PG0_W_RBCR1, 0x1);
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, 0x0A);
for (int j = 0; j < NE2K_PAGE_SIZE; j++) {
uint8_t val = inb(ne2k_io_base_addr + 0x10);
if ((i == 0) && (j < 4)) {
header[j] = val;
} else {
rx_buffer[n++] = val;
}
}
if (i == 0) {
packet_len = ((uint16_t)header[3] << 8) | (uint16_t)header[2];
if (packet_len % NE2K_PAGE_SIZE) {
page_count = (packet_len / NE2K_PAGE_SIZE) + 1;
} else {
page_count = (packet_len / NE2K_PAGE_SIZE);
}
}
if ((i + 1) == page_count)
break;
curr++;
}
outb(ne2k_io_base_addr + NE2K_PG0_RW_BNRY, curr);
if (packet_len == 0) {
ne2k_debug("Triggered on an empty packet.\n");
return;
}
#ifdef CONFIG_APPSERVER
// Treat as a syscall frontend response packet if eth_type says so
// Will eventually go away, so not too worried about elegance here...
#include <frontend.h>
#include <arch/frontend.h>
uint16_t eth_type = htons(*(uint16_t*)(rx_buffer + 12));
if(eth_type == APPSERVER_ETH_TYPE) {
handle_appserver_packet(rx_buffer, packet_len);
kfree(rx_buffer);
return;
}
#endif
spin_lock(&packet_buffers_lock);
if (num_packet_buffers >= MAX_PACKET_BUFFERS) {
printk("WARNING: DROPPING PACKET!\n");
spin_unlock(&packet_buffers_lock);
kfree(rx_buffer);
return;
}
packet_buffers[packet_buffers_tail] = rx_buffer;
packet_buffers_sizes[packet_buffers_tail] = packet_len;
packet_buffers_tail = (packet_buffers_tail + 1) % MAX_PACKET_BUFFERS;
num_packet_buffers++;
spin_unlock(&packet_buffers_lock);
return;
}
// Main routine to send a frame. May be completely broken.
int ne2k_send_frame(const char *data, size_t len) {
if (data == NULL)
return -1;
if (len == 0)
return 0;
if (len > MAX_FRAME_SIZE) {
ne2k_frame_debug("-->Frame Too Large!\n");
return -1;
}
outb(ne2k_io_base_addr + NE2K_PG0_W_IMR, 0x00);
// The TPSR takes a page #
// The RSAR takes a byte offset, but since a page is 256 bits
// and we are writing on page boundries, the low bits are 0, and
// the high bits are a page #
outb(ne2k_io_base_addr + NE2K_PG0_W_TPSR, NE2K_FIRST_SEND_PAGE);
outb(ne2k_io_base_addr + NE2K_PG0_W_RSAR0, 0x0);
outb(ne2k_io_base_addr + NE2K_PG0_W_RSAR1, NE2K_FIRST_SEND_PAGE);
outb(ne2k_io_base_addr + NE2K_PG0_W_TBCR0, len & 0xFF);
outb(ne2k_io_base_addr + NE2K_PG0_W_TBCR1, len >> 8);
outb(ne2k_io_base_addr + NE2K_PG0_W_RBCR0, len & 0xFF);
outb(ne2k_io_base_addr + NE2K_PG0_W_RBCR1, len >> 8);
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, 0x12);
for (int i = 0; i<len; i = i + 1) {
outb(ne2k_io_base_addr + 0x10, *(uint8_t*)(data + i));
//ne2k_debug("sent: %x\n", *(uint8_t*)(data + i));
}
while(( inb(ne2k_io_base_addr + NE2K_PG0_RW_ISR) & 0x40) == 0);
outb(ne2k_io_base_addr + NE2K_PG0_RW_ISR, 0x40);
outb(ne2k_io_base_addr + NE2K_PG0_W_IMR, 0xBF);
outb(ne2k_io_base_addr + NE2K_PG0_RW_CR, 0x1E);
return len;
}