📄 dev_am79c971.c
字号:
/* * Cisco C7200 (Predator) AMD Am79c971 Module. * Copyright (C) 2006 Christophe Fillot. All rights reserved. * * AMD Am79c971 FastEthernet chip emulation. */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <unistd.h>#include <time.h>#include <errno.h>#include <assert.h>#include "utils.h"#include "mips64.h"#include "dynamips.h"#include "memory.h"#include "device.h"#include "net.h"#include "net_io.h"#include "ptask.h"#include "dev_am79c971.h"/* Debugging flags */#define DEBUG_CSR_REGS 0#define DEBUG_BCR_REGS 0#define DEBUG_PCI_REGS 0#define DEBUG_ACCESS 0#define DEBUG_TRANSMIT 0#define DEBUG_RECEIVE 0#define DEBUG_UNKNOWN 0/* AMD Am79c971 PCI vendor/product codes */#define AM79C971_PCI_VENDOR_ID 0x1022#define AM79C971_PCI_PRODUCT_ID 0x2000/* Maximum packet size */#define AM79C971_MAX_PKT_SIZE 2048/* Send up to 16 packets in a TX ring scan pass */#define AM79C971_TXRING_PASS_COUNT 16/* CSR0: Controller Status and Control Register */#define AM79C971_CSR0_ERR 0x00008000 /* Error (BABL,CERR,MISS,MERR) */#define AM79C971_CSR0_BABL 0x00004000 /* Transmitter Timeout Error */#define AM79C971_CSR0_CERR 0x00002000 /* Collision Error */#define AM79C971_CSR0_MISS 0x00001000 /* Missed Frame */#define AM79C971_CSR0_MERR 0x00000800 /* Memory Error */#define AM79C971_CSR0_RINT 0x00000400 /* Receive Interrupt */#define AM79C971_CSR0_TINT 0x00000200 /* Transmit Interrupt */#define AM79C971_CSR0_IDON 0x00000100 /* Initialization Done */#define AM79C971_CSR0_INTR 0x00000080 /* Interrupt Flag */#define AM79C971_CSR0_IENA 0x00000040 /* Interrupt Enable */#define AM79C971_CSR0_RXON 0x00000020 /* Receive On */#define AM79C971_CSR0_TXON 0x00000010 /* Transmit On */#define AM79C971_CSR0_TDMD 0x00000008 /* Transmit Demand */#define AM79C971_CSR0_STOP 0x00000004 /* Stop */#define AM79C971_CSR0_STRT 0x00000002 /* Start */#define AM79C971_CSR0_INIT 0x00000001 /* Initialization *//* CSR3: Interrupt Masks and Deferral Control */#define AM79C971_CSR3_BABLM 0x00004000 /* Transmit. Timeout Int. Mask */#define AM79C971_CSR3_CERRM 0x00002000 /* Collision Error Int. Mask*/#define AM79C971_CSR3_MISSM 0x00001000 /* Missed Frame Interrupt Mask */#define AM79C971_CSR3_MERRM 0x00000800 /* Memory Error Interrupt Mask */#define AM79C971_CSR3_RINTM 0x00000400 /* Receive Interrupt Mask */#define AM79C971_CSR3_TINTM 0x00000200 /* Transmit Interrupt Mask */#define AM79C971_CSR3_IDONM 0x00000100 /* Initialization Done Mask */#define AM79C971_CSR3_BSWP 0x00000004 /* Byte Swap */#define AM79C971_CSR3_IM_MASK 0x00007F00 /* Interrupt Masks for CSR3 *//* CSR5: Extended Control and Interrupt 1 */#define AM79C971_CSR5_TOKINTD 0x00008000 /* Receive Interrupt Mask */#define AM79C971_CSR5_SPND 0x00000001 /* Suspend *//* CSR15: Mode */#define AM79C971_CSR15_PROM 0x00008000 /* Promiscous Mode */#define AM79C971_CSR15_DRCVBC 0x00004000 /* Disable Receive Broadcast */#define AM79C971_CSR15_DRCVPA 0x00002000 /* Disable Receive PHY address */#define AM79C971_CSR15_DTX 0x00000002 /* Disable Transmit */#define AM79C971_CSR15_DRX 0x00000001 /* Disable Receive *//* AMD 79C971 Initialization block length */#define AM79C971_INIT_BLOCK_LEN 0x1c/* RX descriptors */#define AM79C971_RMD1_OWN 0x80000000 /* OWN=1: owned by Am79c971 */#define AM79C971_RMD1_ERR 0x40000000 /* Error */#define AM79C971_RMD1_FRAM 0x20000000 /* Framing Error */#define AM79C971_RMD1_OFLO 0x10000000 /* Overflow Error */#define AM79C971_RMD1_CRC 0x08000000 /* Invalid CRC */#define AM79C971_RMD1_BUFF 0x08000000 /* Buffer Error (chaining) */#define AM79C971_RMD1_STP 0x02000000 /* Start of Packet */#define AM79C971_RMD1_ENP 0x01000000 /* End of Packet */#define AM79C971_RMD1_BPE 0x00800000 /* Bus Parity Error */#define AM79C971_RMD1_PAM 0x00400000 /* Physical Address Match */#define AM79C971_RMD1_LAFM 0x00200000 /* Logical Addr. Filter Match */#define AM79C971_RMD1_BAM 0x00100000 /* Broadcast Address Match */#define AM79C971_RMD1_LEN 0x00000FFF /* Buffer Length */#define AM79C971_RMD2_LEN 0x00000FFF /* Received byte count *//* TX descriptors */#define AM79C971_TMD1_OWN 0x80000000 /* OWN=1: owned by Am79c971 */#define AM79C971_TMD1_ERR 0x40000000 /* Error */#define AM79C971_TMD1_ADD_FCS 0x20000000 /* FCS generation */#define AM79C971_TMD1_STP 0x02000000 /* Start of Packet */#define AM79C971_TMD1_ENP 0x01000000 /* End of Packet */#define AM79C971_TMD1_LEN 0x00000FFF /* Buffer Length *//* RX Descriptor */struct rx_desc { m_uint32_t rmd[4];};/* TX Descriptor */struct tx_desc { m_uint32_t tmd[4];};/* AMD 79C971 Data */struct am79c971_data { char *name; /* Interface type (10baseT or 100baseTX) */ int type; /* Current RAP (Register Address Pointer) value */ m_uint8_t rap; /* CSR and BCR registers */ m_uint32_t csr[256],bcr[256]; /* RX/TX rings start addresses */ m_uint32_t rx_start,tx_start; /* RX/TX number of descriptors (log2) */ m_uint32_t rx_l2len,tx_l2len; /* RX/TX number of descriptors */ m_uint32_t rx_len,tx_len; /* RX/TX ring positions */ m_uint32_t rx_pos,tx_pos; /* MII registers */ m_uint16_t mii_regs[32][32]; /* Physical (MAC) address */ n_eth_addr_t mac_addr; /* Device information */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev; /* Virtual machine */ vm_instance_t *vm; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanner task id */ ptask_id_t tx_tid;};/* Log an am79c971 message */#define AM79C971_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg)static m_uint16_t mii_reg_values[32] = { 0x1000, 0x782D, 0x2000, 0x5C01, 0x01E1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x8060, 0x8020, 0x0820, 0x0000, 0x3800, 0xA3B9, 0x0000, 0x0000, 0x0000,};/* Read a MII register */static m_uint16_t mii_reg_read(struct am79c971_data *d,u_int phy,u_int reg){ if ((phy >= 32) || (reg >= 32)) return(0); return(d->mii_regs[phy][reg]);}/* Write a MII register */static void mii_reg_write(struct am79c971_data *d,u_int phy,u_int reg, m_uint16_t value){ if ((phy < 32) && (reg < 32)) d->mii_regs[phy][reg] = value;}/* Check if a packet must be delivered to the emulated chip */static inline int am79c971_handle_mac_addr(struct am79c971_data *d, m_uint8_t *pkt){ n_eth_hdr_t *hdr = (n_eth_hdr_t *)pkt; /* Accept systematically frames if we are running is promiscuous mode */ if (d->csr[15] & AM79C971_CSR15_PROM) return(TRUE); /* Accept systematically all multicast frames */ if (eth_addr_is_mcast(&hdr->daddr)) return(TRUE); /* Accept frames directly for us, discard others */ if (!memcmp(&d->mac_addr,&hdr->daddr,N_ETH_ALEN)) return(TRUE); return(FALSE);}/* Update the Interrupt Flag bit of csr0 */static void am79c971_update_intr_flag(struct am79c971_data *d){ m_uint32_t mask; mask = d->csr[3] & AM79C971_CSR3_IM_MASK; if (d->csr[0] & mask) d->csr[0] |= AM79C971_CSR0_INTR;}/* Trigger an interrupt */static int am79c971_trigger_irq(struct am79c971_data *d){ if (d->csr[0] & (AM79C971_CSR0_INTR|AM79C971_CSR0_IENA)) { pci_dev_trigger_irq(d->vm,d->pci_dev); return(TRUE); } return(FALSE);}/* Update RX/TX ON bits of csr0 */static void am79c971_update_rx_tx_on_bits(struct am79c971_data *d){ /* * Set RX ON if DRX in csr15 is cleared, and set TX on if DTX * in csr15 is cleared. The START bit must be set. */ d->csr[0] &= ~(AM79C971_CSR0_RXON|AM79C971_CSR0_TXON); if (d->csr[0] & AM79C971_CSR0_STRT) { if (!(d->csr[15] & AM79C971_CSR15_DRX)) d->csr[0] |= AM79C971_CSR0_RXON; if (!(d->csr[15] & AM79C971_CSR15_DTX)) d->csr[0] |= AM79C971_CSR0_TXON; }}/* Update RX/TX descriptor lengths */static void am79c971_update_rx_tx_len(struct am79c971_data *d){ d->rx_len = 1 << d->rx_l2len; d->tx_len = 1 << d->tx_l2len; /* Normalize ring sizes */ if (d->rx_len > 512) d->rx_len = 512; if (d->tx_len > 512) d->tx_len = 512;}/* Fetch the initialization block from memory */static int am79c971_fetch_init_block(struct am79c971_data *d){ m_uint32_t ib[AM79C971_INIT_BLOCK_LEN]; m_uint32_t ib_addr,ib_tmp; /* The init block address is contained in csr1 (low) and csr2 (high) */ ib_addr = (d->csr[2] << 16) | d->csr[1]; if (!ib_addr) { AM79C971_LOG(d,"trying to fetch init block at address 0...\n"); return(-1); } AM79C971_LOG(d,"fetching init block at address 0x%8.8x\n",ib_addr); physmem_copy_from_vm(d->vm,ib,ib_addr,sizeof(ib)); /* Extract RX/TX ring addresses */ d->rx_start = vmtoh32(ib[5]); d->tx_start = vmtoh32(ib[6]); /* Set csr15 from mode field */ ib_tmp = vmtoh32(ib[0]); d->csr[15] = ib_tmp & 0xffff; /* Extract RX/TX ring sizes */ d->rx_l2len = (ib_tmp >> 20) & 0x0F; d->tx_l2len = (ib_tmp >> 28) & 0x0F; am79c971_update_rx_tx_len(d); AM79C971_LOG(d,"rx_ring = 0x%8.8x (%u), tx_ring = 0x%8.8x (%u)\n", d->rx_start,d->rx_len,d->tx_start,d->tx_len); /* Get the physical MAC address */ ib_tmp = vmtoh32(ib[1]); d->csr[12] = ib_tmp & 0xFFFF; d->csr[13] = ib_tmp >> 16; d->mac_addr.eth_addr_byte[3] = (ib_tmp >> 24) & 0xFF; d->mac_addr.eth_addr_byte[2] = (ib_tmp >> 16) & 0xFF; d->mac_addr.eth_addr_byte[1] = (ib_tmp >> 8) & 0xFF; d->mac_addr.eth_addr_byte[0] = ib_tmp & 0xFF; ib_tmp = vmtoh32(ib[2]); d->csr[14] = ib_tmp & 0xFFFF; d->mac_addr.eth_addr_byte[5] = (ib_tmp >> 8) & 0xFF; d->mac_addr.eth_addr_byte[4] = ib_tmp & 0xFF; /* * Mark the initialization as done is csr0. */ d->csr[0] |= AM79C971_CSR0_IDON; /* Update RX/TX ON bits of csr0 since csr15 has been modified */ am79c971_update_rx_tx_on_bits(d); AM79C971_LOG(d,"CSR0 = 0x%4.4x\n",d->csr[0]); am79c971_update_intr_flag(d); if (am79c971_trigger_irq(d)) AM79C971_LOG(d,"triggering IDON interrupt\n"); return(0);}/* RDP (Register Data Port) access */static void am79c971_rdp_access(cpu_mips_t *cpu,struct am79c971_data *d, u_int op_type,m_uint64_t *data){ m_uint32_t mask;#if DEBUG_CSR_REGS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to CSR %d\n",d->rap); } else { cpu_log(cpu,d->name,"write access to CSR %d, value=0x%x\n",d->rap,*data); }#endif switch(d->rap) { case 0: /* CSR0: Controller Status and Control Register */ if (op_type == MTS_READ) { //AM79C971_LOG(d,"reading CSR0 (val=0x%4.4x)\n",d->csr[0]); *data = d->csr[0]; } else { /* * The STOP bit clears other bits. * It has precedence over INIT and START bits. */ if (*data & AM79C971_CSR0_STOP) { //AM79C971_LOG(d,"stopping interface!\n"); d->csr[0] = AM79C971_CSR0_STOP; d->tx_pos = d->rx_pos = 0; break; } /* These bits are cleared when set to 1 */ mask = AM79C971_CSR0_BABL | AM79C971_CSR0_CERR; mask |= AM79C971_CSR0_MISS | AM79C971_CSR0_MERR; mask |= AM79C971_CSR0_RINT | AM79C971_CSR0_TINT; mask |= AM79C971_CSR0_IDON; d->csr[0] &= ~(*data & mask); /* Save the Interrupt Enable bit */ d->csr[0] |= *data & AM79C971_CSR0_IENA; /* If INIT bit is set, fetch the initialization block */ if (*data & AM79C971_CSR0_INIT) { d->csr[0] |= AM79C971_CSR0_INIT; d->csr[0] &= ~AM79C971_CSR0_STOP; am79c971_fetch_init_block(d); } /* If STRT bit is set, clear the stop bit */ if (*data & AM79C971_CSR0_STRT) { //AM79C971_LOG(d,"enabling interface!\n"); d->csr[0] |= AM79C971_CSR0_STRT; d->csr[0] &= ~AM79C971_CSR0_STOP; am79c971_update_rx_tx_on_bits(d); } } break; case 6: /* CSR6: RX/TX Descriptor Table Length */ if (op_type == MTS_WRITE) { d->rx_l2len = (*data >> 8) & 0x0F; d->tx_l2len = (*data >> 12) & 0x0F; am79c971_update_rx_tx_len(d); } else { *data = (d->tx_l2len << 12) | (d->rx_l2len << 8); } break; case 15: /* CSR15: Mode */ if (op_type == MTS_WRITE) { d->csr[15] = *data; am79c971_update_rx_tx_on_bits(d); } else { *data = d->csr[15]; } break; case 88: if (op_type == MTS_READ) { switch(d->type) { case AM79C971_TYPE_100BASE_TX: *data = 0x2623003; break; default: *data = 0; break; } } break; default: if (op_type == MTS_READ) { *data = d->csr[d->rap]; } else { d->csr[d->rap] = *data; }#if DEBUG_UNKNOWN if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to unknown CSR %d\n",d->rap); } else { cpu_log(cpu,d->name,"write access to unknown CSR %d, value=0x%x\n", d->rap,*data); }#endif }}/* BDP (BCR Data Port) access */static void am79c971_bdp_access(cpu_mips_t *cpu,struct am79c971_data *d, u_int op_type,m_uint64_t *data){ u_int mii_phy,mii_reg;#if DEBUG_BCR_REGS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to BCR %d\n",d->rap); } else { cpu_log(cpu,d->name,"write access to BCR %d, value=0x%x\n",d->rap,*data); }#endif switch(d->rap) { case 9: if (op_type == MTS_READ) *data = 1; break; case 34: /* BCR34: MII Management Data Register */ mii_phy = (d->bcr[33] >> 5) & 0x1F; mii_reg = (d->bcr[33] >> 0) & 0x1F; if (op_type == MTS_READ) *data = mii_reg_read(d,mii_phy,mii_reg); //else //mii_reg_write(d,mii_phy,mii_reg,*data); break; default: if (op_type == MTS_READ) { *data = d->bcr[d->rap]; } else { d->bcr[d->rap] = *data; }#if DEBUG_UNKNOWN if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to unknown BCR %d\n",d->rap); } else { cpu_log(cpu,d->name,"write access to unknown BCR %d, value=0x%x\n", d->rap,*data); }#endif }}/* * dev_am79c971_access() */void *dev_am79c971_access(cpu_mips_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data){ struct am79c971_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0;#if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", offset,cpu->pc,op_size); } else { cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " "val=0x%llx, size=%u\n",offset,cpu->pc,*data,op_size); }#endif switch(offset) { case 0x14: /* RAP (Register Address Pointer) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -