|  | /* | 
|  | * 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; | 
|  | } | 
|  |  |