📄 e1000.c
字号:
/* * QEMU e1000 emulation * * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. * Copyright (c) 2008 Qumranet * Based on work done by: * Copyright (c) 2007 Dan Aloni * Copyright (c) 2004 Antony T Curtis * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "vl.h"#include "e1000_hw.h"#define DEBUG#ifdef DEBUGenum { DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT, DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM, DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR, DEBUG_RXFILTER, DEBUG_NOTYET,};#define DBGBIT(x) (1<<DEBUG_##x)static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);#define DBGOUT(what, fmt, params...) do { \ if (debugflags & DBGBIT(what)) \ fprintf(stderr, "e1000: " fmt, ##params); \ } while (0)#else#define DBGOUT(what, fmt, params...) do {} while (0)#endif#define IOPORT_SIZE 0x40#define PNPMMIO_SIZE 0x20000/* * HW models: * E1000_DEV_ID_82540EM works with Windows and Linux * E1000_DEV_ID_82573L OK with windoze and Linux 2.6.22, * appears to perform better than 82540EM, but breaks with Linux 2.6.18 * E1000_DEV_ID_82544GC_COPPER appears to work; not well tested * Others never tested */enum { E1000_DEVID = E1000_DEV_ID_82540EM };/* * May need to specify additional MAC-to-PHY entries -- * Intel's Windows driver refuses to initialize unless they match */enum { PHY_ID2_INIT = E1000_DEVID == E1000_DEV_ID_82573L ? 0xcc2 : E1000_DEVID == E1000_DEV_ID_82544GC_COPPER ? 0xc30 : /* default to E1000_DEV_ID_82540EM */ 0xc20};typedef struct E1000State_st { PCIDevice dev; VLANClientState *vc; NICInfo *nd; uint32_t instance; uint32_t mmio_base; int mmio_index; uint32_t mac_reg[0x8000]; uint16_t phy_reg[0x20]; uint16_t eeprom_data[64]; uint32_t rxbuf_size; uint32_t rxbuf_min_shift; int check_rxov; struct e1000_tx { unsigned char header[256]; unsigned char data[0x10000]; uint16_t size; unsigned char sum_needed; uint8_t ipcss; uint8_t ipcso; uint16_t ipcse; uint8_t tucss; uint8_t tucso; uint16_t tucse; uint8_t hdr_len; uint16_t mss; uint32_t paylen; uint16_t tso_frames; char tse; char ip; char tcp; char cptse; //current packet tse bit } tx; struct { uint32_t val_in; // shifted in from guest driver uint16_t bitnum_in; uint16_t bitnum_out; uint16_t reading; uint32_t old_eecd; } eecd_state;} E1000State;#define defreg(x) x = (E1000_##x>>2)enum { defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC), defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC), defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC), defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH), defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT), defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH), defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT), defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL), defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC), defreg(RA), defreg(MTA), defreg(CRCERRS),};enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W };static char phy_regcap[0x20] = { [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW, [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW, [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW, [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R, [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R, [PHY_ID2] = PHY_R,};static voidioport_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type){ DBGOUT(IO, "e1000_ioport_map addr=0x%04x size=0x%08x\n", addr, size);}static voidset_interrupt_cause(E1000State *s, int index, uint32_t val){ if (val) val |= E1000_ICR_INT_ASSERTED; s->mac_reg[ICR] = val; pci_set_irq(&s->dev, 0, (s->mac_reg[IMS] & s->mac_reg[ICR]) != 0);}static voidset_ics(E1000State *s, int index, uint32_t val){ DBGOUT(INTERRUPT, "set_ics %x, ICR %x, IMR %x\n", val, s->mac_reg[ICR], s->mac_reg[IMS]); set_interrupt_cause(s, 0, val | s->mac_reg[ICR]);}static intrxbufsize(uint32_t v){ v &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 | E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 | E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256; switch (v) { case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384: return 16384; case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192: return 8192; case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096: return 4096; case E1000_RCTL_SZ_1024: return 1024; case E1000_RCTL_SZ_512: return 512; case E1000_RCTL_SZ_256: return 256; } return 2048;}static voidset_rx_control(E1000State *s, int index, uint32_t val){ s->mac_reg[RCTL] = val; s->rxbuf_size = rxbufsize(val); s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1; DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT], s->mac_reg[RCTL]);}static voidset_mdic(E1000State *s, int index, uint32_t val){ uint32_t data = val & E1000_MDIC_DATA_MASK; uint32_t addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT); if ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) // phy # val = s->mac_reg[MDIC] | E1000_MDIC_ERROR; else if (val & E1000_MDIC_OP_READ) { DBGOUT(MDIC, "MDIC read reg 0x%x\n", addr); if (!(phy_regcap[addr] & PHY_R)) { DBGOUT(MDIC, "MDIC read reg %x unhandled\n", addr); val |= E1000_MDIC_ERROR; } else val = (val ^ data) | s->phy_reg[addr]; } else if (val & E1000_MDIC_OP_WRITE) { DBGOUT(MDIC, "MDIC write reg 0x%x, value 0x%x\n", addr, data); if (!(phy_regcap[addr] & PHY_W)) { DBGOUT(MDIC, "MDIC write reg %x unhandled\n", addr); val |= E1000_MDIC_ERROR; } else s->phy_reg[addr] = data; } s->mac_reg[MDIC] = val | E1000_MDIC_READY; set_ics(s, 0, E1000_ICR_MDAC);}static uint32_tget_eecd(E1000State *s, int index){ uint32_t ret = E1000_EECD_PRES|E1000_EECD_GNT | s->eecd_state.old_eecd; DBGOUT(EEPROM, "reading eeprom bit %d (reading %d)\n", s->eecd_state.bitnum_out, s->eecd_state.reading); if (!s->eecd_state.reading || ((s->eeprom_data[(s->eecd_state.bitnum_out >> 4) & 0x3f] >> ((s->eecd_state.bitnum_out & 0xf) ^ 0xf))) & 1) ret |= E1000_EECD_DO; return ret;}static voidset_eecd(E1000State *s, int index, uint32_t val){ uint32_t oldval = s->eecd_state.old_eecd; s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS | E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ); if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge return; if (!(E1000_EECD_SK & val)) { // falling edge s->eecd_state.bitnum_out++; return; } if (!(val & E1000_EECD_CS)) { // rising, no CS (EEPROM reset) memset(&s->eecd_state, 0, sizeof s->eecd_state); return; } s->eecd_state.val_in <<= 1; if (val & E1000_EECD_DI) s->eecd_state.val_in |= 1; if (++s->eecd_state.bitnum_in == 9 && !s->eecd_state.reading) { s->eecd_state.bitnum_out = ((s->eecd_state.val_in & 0x3f)<<4)-1; s->eecd_state.reading = (((s->eecd_state.val_in >> 6) & 7) == EEPROM_READ_OPCODE_MICROWIRE); } DBGOUT(EEPROM, "eeprom bitnum in %d out %d, reading %d\n", s->eecd_state.bitnum_in, s->eecd_state.bitnum_out, s->eecd_state.reading);}static uint32_tflash_eerd_read(E1000State *s, int x){ unsigned int index, r = s->mac_reg[EERD] & ~E1000_EEPROM_RW_REG_START; if ((index = r >> E1000_EEPROM_RW_ADDR_SHIFT) > EEPROM_CHECKSUM_REG) return 0; return (s->eeprom_data[index] << E1000_EEPROM_RW_REG_DATA) | E1000_EEPROM_RW_REG_DONE | r;}static unsigned intdo_cksum(uint8_t *dp, uint8_t *de){ unsigned int bsum[2] = {0, 0}, i, sum; for (i = 1; dp < de; bsum[i^=1] += *dp++) ; sum = (bsum[0] << 8) + bsum[1]; sum = (sum >> 16) + (sum & 0xffff); return ~(sum + (sum >> 16));}static voidputsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse){ if (cse && cse < n) n = cse + 1; if (sloc < n-1) cpu_to_be16wu((uint16_t *)(data + sloc), do_cksum(data + css, data + n));}static voidxmit_seg(E1000State *s){ uint16_t len, *sp; unsigned int frames = s->tx.tso_frames, css, sofar, n; struct e1000_tx *tp = &s->tx; if (tp->tse && tp->cptse) { css = tp->ipcss; DBGOUT(TXSUM, "frames %d size %d ipcss %d\n", frames, tp->size, css); if (tp->ip) { // IPv4 cpu_to_be16wu((uint16_t *)(tp->data+css+2), tp->size - css); cpu_to_be16wu((uint16_t *)(tp->data+css+4), be16_to_cpup((uint16_t *)(tp->data+css+4))+frames); } else // IPv6 cpu_to_be16wu((uint16_t *)(tp->data+css+4), tp->size - css); css = tp->tucss; len = tp->size - css; DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len); if (tp->tcp) { sofar = frames * tp->mss; cpu_to_be32wu((uint32_t *)(tp->data+css+4), // seq be32_to_cpup((uint32_t *)(tp->data+css+4))+sofar); if (tp->paylen - sofar > tp->mss) tp->data[css + 13] &= ~9; // PSH, FIN } else // UDP cpu_to_be16wu((uint16_t *)(tp->data+css+4), len); if (tp->sum_needed & E1000_TXD_POPTS_TXSM) { // add pseudo-header length before checksum calculation sp = (uint16_t *)(tp->data + tp->tucso); cpu_to_be16wu(sp, be16_to_cpup(sp) + len); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -