//https://github.com/blanham/ChickenOS/blob/master/src/device/net/e1000.c //https://wiki.osdev.org/Intel_Ethernet_i217 //https://github.com/torvalds/linux/blob/master/drivers/net/ethernet/intel/e1000/e1000_hw.c //https://github.com/qemu/qemu/blob/master/hw/net/e1000.c //registers etc. verified from pdf at https://pdos.csail.mit.edu/6.828/2006/readings/hardware/8254x_GBe_SDM.pdf #include #include "log.h" #include "e1000.h" #include "kmalloc.h" #include "netdev.h" #include "arp.h" #include "lib/string/string.h" #define REG_CTRL 0x0000 // device control R/W #define REG_CTRL_LRST 1<<3 // Link Reset / transition to 0 to initiates auto-negotiation #define REG_CTRL_SLU 1<<6 // Set Link Up / signal forced #define REG_STATUS 0x0008 // device status R #define REG_EERD 0x0014 // EEPROM / R #define REG_ICR 0x00C0 // interrupt cause / (reading clears) / R #define REG_IMS 0x00D0 // interrupt mask / R/W // RECEIVE #define REG_RCTL 0x0100 // Receive Control R/W #define REG_RDBAL 0x2800 #define REG_RDBAH 0x2804 #define REG_RDLEN 0x2808 #define REG_RDH 0x2810 #define REG_RDT 0x2818 #define REG_MTA 0x5200 // multicast table array /// TRANSMIT #define REG_TCTL 0x0400 // Transmit Control R/W #define REG_TDBAL 0x3800 #define REG_TDBAH 0x3804 #define REG_TDLEN 0x3808 #define REG_TDH 0x3810 #define REG_TDT 0x3818 // check? #define TSTA_DD (1 << 0) // Descriptor Done /* /// #define INTEL_VEND 0x8086 // Vendor ID for Intel #define E1000_DEV 0x100E // Device ID for the e1000 Qemu, Bochs, and VirtualBox emmulated NICs #define E1000_I217 0x153A // Device ID for Intel I217 #define E1000_82577LM 0x10EA // Device ID for Intel 82577LM // I have gathered those from different Hobby online operating systems instead of getting them one by one from the manual #define REG_CTRL_EXT 0x0018 #define REG_RXDCTL 0x3828 // RX Descriptor Control #define REG_RDTR 0x2820 // RX Delay Timer Register #define REG_RADV 0x282C // RX Int. Absolute Delay Timer #define REG_RSRPD 0x2C00 // RX Small Packet Detect Interrupt #define REG_TIPG 0x0410 // Transmit Inter Packet Gap #define CTRL_SLU 0x40 //set link up #define RCTL_EN (1 << 1) // Receiver Enable #define RCTL_SBP (1 << 2) // Store Bad Packets #define RCTL_UPE (1 << 3) // Unicast Promiscuous Enabled #define RCTL_MPE (1 << 4) // Multicast Promiscuous Enabled #define RCTL_LPE (1 << 5) // Long Packet Reception Enable #define RCTL_LBM_NONE (0 << 6) // No Loopback #define RCTL_LBM_PHY (3 << 6) // PHY or external SerDesc loopback #define RTCL_RDMTS_HALF (0 << 8) // Free Buffer Threshold is 1/2 of RDLEN #define RTCL_RDMTS_QUARTER (1 << 8) // Free Buffer Threshold is 1/4 of RDLEN #define RTCL_RDMTS_EIGHTH (2 << 8) // Free Buffer Threshold is 1/8 of RDLEN #define RCTL_MO_36 (0 << 12) // Multicast Offset - bits 47:36 #define RCTL_MO_35 (1 << 12) // Multicast Offset - bits 46:35 #define RCTL_MO_34 (2 << 12) // Multicast Offset - bits 45:34 #define RCTL_MO_32 (3 << 12) // Multicast Offset - bits 43:32 #define RCTL_BAM (1 << 15) // Broadcast Accept Mode #define RCTL_VFE (1 << 18) // VLAN Filter Enable #define RCTL_CFIEN (1 << 19) // Canonical Form Indicator Enable #define RCTL_CFI (1 << 20) // Canonical Form Indicator Bit Value #define RCTL_DPF (1 << 22) // Discard Pause Frames #define RCTL_PMCF (1 << 23) // Pass MAC Control Frames #define RCTL_SECRC (1 << 26) // Strip Ethernet CRC // Buffer Sizes #define RCTL_BSIZE_256 (3 << 16) #define RCTL_BSIZE_512 (2 << 16) #define RCTL_BSIZE_1024 (1 << 16) #define RCTL_BSIZE_2048 (0 << 16) #define RCTL_BSIZE_4096 ((3 << 16) | (1 << 25)) #define RCTL_BSIZE_8192 ((2 << 16) | (1 << 25)) #define RCTL_BSIZE_16384 ((1 << 16) | (1 << 25)) // Transmit Command #define CMD_EOP (1 << 0) // End of Packet #define CMD_IFCS (1 << 1) // Insert FCS #define CMD_IC (1 << 2) // Insert Checksum #define CMD_RS (1 << 3) // Report Status #define CMD_RPS (1 << 4) // Report Packet Sent #define CMD_VLE (1 << 6) // VLAN Packet Enable #define CMD_IDE (1 << 7) // Interrupt Delay Enable // TCTL Register #define TCTL_EN (1 << 1) // Transmit Enable #define TCTL_PSP (1 << 3) // Pad Short Packets #define TCTL_CT_SHIFT 4 // Collision Threshold #define TCTL_COLD_SHIFT 12 // Collision Distance #define TCTL_SWXOFF (1 << 22) // Software XOFF Transmission #define TCTL_RTLC (1 << 24) // Re-transmit on Late Collision #define TSTA_DD (1 << 0) // Descriptor Done #define TSTA_EC (1 << 1) // Excess Collisions #define TSTA_LC (1 << 2) // Late Collision #define LSTA_TU (1 << 3) // Transmit Underrun */ // buffers #define E1000_NUM_RX_DESC 32 #define E1000_NUM_TX_DESC 8 // TODO: move all global stuff to some struct so we can run multiple e1000 cards! // struct netdev dev; ///TODO not global! struct e1000_rx_desc { volatile uint32_t addr_lo; // 4 volatile uint32_t addr_hi; // 4 volatile uint16_t length; // 2 volatile uint16_t checksum; // 2 volatile uint8_t status; // 1 volatile uint8_t errors; // 1 volatile uint16_t special; // 2 // sum: 16 byte } __attribute__((packed)); struct e1000_tx_desc { volatile uint32_t addr_lo; // 4 volatile uint32_t addr_hi; // 4 volatile uint16_t length; volatile uint8_t cso; volatile uint8_t cmd; volatile uint8_t status; volatile uint8_t css; volatile uint16_t special; } __attribute__((packed)); uint8_t bar_type; // Type of BOR0 uint16_t io_base; // IO Base Address uint32_t mem_base; // MMIO Base Address bool eerprom_exists; // A flag indicating if eeprom exists uint8_t mac [6]; // A buffer for storing the mack address struct e1000_rx_desc *rx_descs[E1000_NUM_RX_DESC]; // Receive Descriptor Buffers struct e1000_tx_desc *tx_descs[E1000_NUM_TX_DESC]; // Transmit Descriptor Buffers uint16_t rx_cur; // Current Receive Descriptor Buffer uint16_t tx_cur; // Current Transmit Descriptor Buffer void writeCommand( uint16_t p_address, uint32_t p_value) { // if ( bar_type == 0 ) // { (*((volatile uint32_t*)(mem_base+p_address)))=(p_value); // } // else // { //outportl(io_base, p_address); //outportl(io_base + 4, p_value); // } } void writeCommand16( uint16_t p_address, uint16_t p_value) { // if ( bar_type == 0 ) // { (*((volatile uint16_t*)(mem_base+p_address)))=(p_value); // } // else // { //outportl(io_base, p_address); //outportl(io_base + 4, p_value); // } } uint32_t readCommand( uint16_t p_address) { // if ( bar_type == 0 ) // { return *((volatile uint32_t*)(mem_base+p_address)); // } // else // { //outportl(io_base, p_address); //return inportl(io_base + 4); // } } bool detectEEProm() { uint32_t val = 0; writeCommand(REG_EERD, 0x1); for(int i = 0; i < 1000 && ! eerprom_exists; i++) { val = readCommand( REG_EERD); if(val & 0x10) eerprom_exists = true; else eerprom_exists = false; } klog("eeprom %s",eerprom_exists?"YES":"NO"); return eerprom_exists; } uint32_t eepromRead( uint8_t addr) { uint16_t data = 0; uint32_t tmp = 0; if ( eerprom_exists) { writeCommand( REG_EERD, (1) | ((uint32_t)(addr) << 8) ); while( !((tmp = readCommand(REG_EERD)) & (1 << 4)) ); } else { writeCommand( REG_EERD, (1) | ((uint32_t)(addr) << 2) ); while( !((tmp = readCommand(REG_EERD)) & (1 << 1)) ); } data = (uint16_t)((tmp >> 16) & 0xFFFF); return data; } bool readMACAddress() { if ( eerprom_exists) { uint32_t temp; temp = eepromRead( 0); mac[0] = temp &0xff; mac[1] = temp >> 8; temp = eepromRead( 1); mac[2] = temp &0xff; mac[3] = temp >> 8; temp = eepromRead( 2); mac[4] = temp &0xff; mac[5] = temp >> 8; } else { uint8_t * mem_base_mac_8 = (uint8_t *) (mem_base+0x5400); uint32_t * mem_base_mac_32 = (uint32_t *) (mem_base+0x5400); if ( mem_base_mac_32[0] != 0 ) { for(int i = 0; i < 6; i++) { mac[i] = mem_base_mac_8[i]; } } else return false; } return true; } void rxinit() { uint8_t * ptr; struct e1000_rx_desc *descs; // Allocate buffer for receive descriptors. For simplicity, in my case khmalloc returns a virtual address that is identical to it physical mapped address. // In your case you should handle virtual and physical addresses as the addresses passed to the NIC should be physical onesk ptr = kballoc(1); // one page can hold up to 256 descriptors. descs = (struct e1000_rx_desc *)ptr; for(int i = 0; i < E1000_NUM_RX_DESC; i++) { rx_descs[i] = descs; rx_descs[i]->addr_lo = kballoc(1); //TODO: do not waste! rx_descs[i]->addr_hi = 0; rx_descs[i]->status = 0; descs++; } /* writeCommand(REG_TXDESCLO, (uint32_t)((uint64_t)ptr >> 32) ); writeCommand(REG_TXDESCHI, (uint32_t)((uint64_t)ptr & 0xFFFFFFFF)); writeCommand(REG_TXDESCLO, (uint32_t)(0) ); writeCommand(REG_TXDESCHI, (uint32_t)(ptr)); */ writeCommand(REG_RDBAL, ptr); writeCommand(REG_RDBAH, 0); writeCommand(REG_RDLEN, E1000_NUM_RX_DESC * 16); // size of ALL descirptors in bytes writeCommand(REG_RDT, E1000_NUM_RX_DESC); // index of one after last descirptor writeCommand(REG_RDH, 0); // first valid descirptor index rx_cur = 0; //enable receiving /* uint32_t flags = (2 << 16) | (1 << 25) | (1 << 26) | (1 << 15) | (1 << 5) | (0 << 8) | (0 << 4) | (0 << 3) | ( 1 << 2) | 1; */ // writeCommand(REG_RCTRL, flags); // writeCommand(REG_RCTRL, RCTL_EN| RCTL_SBP| RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_2048); writeCommand(REG_RCTL, 1<<1 // receive enable // RCTL.EN |1<<3 // take all unicast |1<<4 // take all multicast |1<<15 // take broadcast // RCTL.BAM |2<<16 // 512 bytes rcv buffers // TCTL.BSIZE // RDTMS // MO // ); /* for(int i = 0; i < E1000_NUM_RX_DESC; i++) { klog("rx_desc %d (0x%08X) at addr %08X %08X, status= %d",i,rx_descs[i],rx_descs[i]->addr_hi,rx_descs[i]->addr_lo, rx_descs[i]->status); } */ } void txinit() { uint8_t * ptr; // Allocate buffer for receive descriptors. For simplicity, in my case khmalloc returns a virtual address that is identical to it physical mapped address. // In your case you should handle virtual and physical addresses as the addresses passed to the NIC should be physical ones ptr = kballoc(1); struct e1000_tx_desc *descs=ptr; for(int i = 0; i < E1000_NUM_TX_DESC; i++) { tx_descs[i] = descs++; tx_descs[i]->addr_lo = 0; tx_descs[i]->addr_hi = 0; tx_descs[i]->cmd = 0; tx_descs[i]->status = TSTA_DD; } writeCommand(REG_TDBAL, ptr); writeCommand(REG_TDBAH, 0); //now setup total length of descriptors writeCommand(REG_TDLEN, E1000_NUM_TX_DESC * 16); // //setup head and tail writeCommand( REG_TDH, 0); writeCommand( REG_TDT, 0); tx_cur = 0; /* writeCommand(REG_TCTRL, TCTL_EN | TCTL_PSP | (15 << TCTL_CT_SHIFT) | (64 << TCTL_COLD_SHIFT) | TCTL_RTLC); */ writeCommand(REG_TCTL, 1<<1 // enable transmitter |1<<3 // pad short packets // ,64<<12 // collision distance ); // This line of code overrides the one before it but I left both to // highlight that the previous one works with e1000 cards, but for the // e1000e cards you should set the TCTRL register as follows. For detailed // description of each bit, please refer to the Intel Manual. In the case // of I217 and 82577LM packets will not be sent if the TCTRL is not // configured using the following bits. // TODO: programm tipg // //writeCommand(REG_TCTRL, 0b0110000000000111111000011111010); //writeCommand(REG_TIPG, 0x0060200A); } void e1000_handleReceive() { uint16_t old_cur; bool got_packet = false; while((rx_descs[rx_cur]->status !=0)) { got_packet = true; klog("GOT FIRST PACKET WOOOOW"); uint8_t *buf = (uint8_t *)(uint32_t)rx_descs[rx_cur]->addr_lo; uint16_t len = rx_descs[rx_cur]->length; // Here you should inject the received packet into your network stack arp_incoming(&dev,buf); rx_descs[rx_cur]->status = 0; old_cur = rx_cur; rx_cur = (rx_cur + 1) % E1000_NUM_RX_DESC; writeCommand(REG_RDT, old_cur ); } } int e1000_sendPacket(const void * p_data, uint16_t p_len) { tx_descs[tx_cur]->addr_lo = p_data; // physical addy of data tx_descs[tx_cur]->length = p_len; // length in bytes //tx_descs[tx_cur]->cmd = CMD_EOP | CMD_IFCS | CMD_RS | CMD_RPS; tx_descs[tx_cur]->cmd = ((1<<3)|3); tx_descs[tx_cur]->status = 0; uint8_t old_cur = tx_cur; tx_cur = (tx_cur + 1) % E1000_NUM_TX_DESC; writeCommand(REG_TDT, tx_cur); while(!(tx_descs[old_cur]->status & 0xff)); klog("SENT FIRST PACKET WOOOOW"); return 0; } void enableInterrupt() { // uint32_t status = readCommand(REG_ICR); // check interrupt cause register. // klog("status before enabling e1000 interrupts=%d",status); // writeCommand(REG_IMASK ,0x1F6DC); // writeCommand(REG_IMASK ,0xff & ~4); // RXT, RXO, RXDMT,RXSEQ, and LSC suugestef for rec. writeCommand(REG_IMS, 1<<6 // RXO // fifo overrun |1<<7 // RXT0 // receiver timer |1<<4 // RXDMT0 // min. threshold |1<<3 // RXSEQ // sequence error |1<<2 // LSC // link status change ); // readCommand(REG_ICR); // clear cause reg? } // force link up void e1000_linkup() { uint32_t val; val = readCommand(REG_CTRL); writeCommand(REG_CTRL, val | REG_CTRL_SLU); } void e1000_linkdown() { uint32_t val; val = readCommand(REG_CTRL); writeCommand(REG_CTRL, val | REG_CTRL_SLU); } struct netdev e1000_init(uint32_t base) { dev.ip=0; dev.hwaddr[0]=dev.hwaddr[1]=dev.hwaddr[2]=dev.hwaddr[3]=dev.hwaddr[4]=dev.hwaddr[5]=0; klog("init E1000"); klog("interrupt mask: 0x%08X",readCommand(REG_IMS)); klog("device status: 0x%08X",readCommand(REG_STATUS)); klog("control: 0x%08X",readCommand(REG_CTRL)); // uint32_t s=readCommand(REG_CTRL)&~REG_CTRL_LRST; // klog("set control: 0x%08X",s); //writeCommand(REG_CTRL,s); if(base!=0){ mem_base=base;} detectEEProm(); if (! readMACAddress()) return dev; klog("mac : %02x:%02x:%02x:%02x:%02x:%02x",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],mac[6]); memcpy(dev.hwaddr,mac,6); // use mac obtained from device dev.ip=(192<<0)+(168<<8)+(0<<16)+(20<<24); // 192.168.0.20 // TODO: not hardcode! dev.transmit=e1000_sendPacket; // clear multicast table for(int i = 0; i < 0x80; i++)writeCommand(REG_MTA + i*4, 0); //e1000_linkup(); enableInterrupt(); txinit(); rxinit(); klog("E1000 initialized"); return dev; } void e1000_irq (int irq) { // if ( p_interruptContext->getInteruptNumber() == pciConfigHeader->getIntLine()+IRQ0) // { /* This might be needed here if your handler doesn't clear interrupts * from each device and must be done before EOI if using the PIC. Without this, the card will spam interrupts as the int-line will stay high. */ //writeCommand(REG_IMASK, 0x1); //enableInterrupt(); // check interrupt cause register. // this acknowledges the interrupt at the same time. uint32_t status = readCommand(REG_ICR); /* klog("e1000_irq status=%d",status); klog("rx descriptor head is at: %d", readCommand(REG_RDH)); klog("rx descriptor tail is at: %d", readCommand(REG_RDT)); klog("rx descriptor addr lo: 0x%08X", readCommand(REG_RDBAL)); klog("rx descriptor addr hi: 0x%08X", readCommand(REG_RDBAH)); klog("rx descriptor len: 0x%08X", readCommand(REG_RDLEN)); if(status & 0x1) klog ("transmit descriptor written back"); if(status & 0x2) klog ("transmit queue empty"); if(status & 0x4) klog ("link status change"); if(status & 0x8) klog ("receive sequence error"); if(status & 1<<6) klog ("fifo overrun"); */ if(status & 0x80)e1000_handleReceive(); //... }