--- upstream/dynamips-0.2.6-RC3/dev_am79c971.c 2007/10/06 16:06:49 4 +++ trunk/dev_am79c971.c 2007/10/06 16:45:40 12 @@ -1,5 +1,5 @@ /* - * Cisco C7200 (Predator) AMD Am79c971 Module. + * Cisco router simulation platform. * Copyright (C) 2006 Christophe Fillot. All rights reserved. * * AMD Am79c971 FastEthernet chip emulation. @@ -15,7 +15,8 @@ #include #include "utils.h" -#include "mips64.h" +#include "cpu.h" +#include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" @@ -29,8 +30,8 @@ #define DEBUG_BCR_REGS 0 #define DEBUG_PCI_REGS 0 #define DEBUG_ACCESS 0 -#define DEBUG_TRANSMIT 0 -#define DEBUG_RECEIVE 0 +#define DEBUG_TRANSMIT 1 +#define DEBUG_RECEIVE 1 #define DEBUG_UNKNOWN 0 /* AMD Am79c971 PCI vendor/product codes */ @@ -125,9 +126,15 @@ struct am79c971_data { char *name; + /* Lock */ + pthread_mutex_t lock; + /* Interface type (10baseT or 100baseTX) */ int type; + /* RX/TX clearing count */ + int rx_tx_clear_count; + /* Current RAP (Register Address Pointer) value */ m_uint8_t rap; @@ -171,12 +178,22 @@ /* Log an am79c971 message */ #define AM79C971_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) +/* Lock/Unlock primitives */ +#define AM79C971_LOCK(d) pthread_mutex_lock(&(d)->lock) +#define AM79C971_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) static m_uint16_t mii_reg_values[32] = { - 0x1000, 0x782D, 0x2000, 0x5C01, 0x01E1, 0x0000, 0x0000, 0x0000, + 0x1000, 0x782D, 0x0013, 0x78E2, 0x01E1, 0xC9E1, 0x000F, 0x2001, + 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0x0104, 0x4780, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x00C8, 0x0000, 0xFFFF, 0x0000, 0x0000, 0x0000, + +#if 0 + 0x1000, 0x782D, 0x0013, 0x78e2, 0x01E1, 0xC9E1, 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, + 0x8023, 0x0820, 0x0000, 0x3800, 0xA3B9, 0x0000, 0x0000, 0x0000, +#endif }; /* Read a MII register */ @@ -202,7 +219,7 @@ { n_eth_hdr_t *hdr = (n_eth_hdr_t *)pkt; - /* Accept systematically frames if we are running is promiscuous mode */ + /* Accept systematically frames if we are running in promiscuous mode */ if (d->csr[15] & AM79C971_CSR15_PROM) return(TRUE); @@ -218,25 +235,25 @@ } /* Update the Interrupt Flag bit of csr0 */ -static void am79c971_update_intr_flag(struct am79c971_data *d) +static void am79c971_update_irq_status(struct am79c971_data *d) { m_uint32_t mask; - mask = d->csr[3] & AM79C971_CSR3_IM_MASK; + /* Bits set in CR3 disable the specified interrupts */ + mask = AM79C971_CSR3_IM_MASK & ~(d->csr[3] & AM79C971_CSR3_IM_MASK); if (d->csr[0] & mask) d->csr[0] |= AM79C971_CSR0_INTR; -} + else + 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)) { + if ((d->csr[0] & (AM79C971_CSR0_INTR|AM79C971_CSR0_IENA)) == + (AM79C971_CSR0_INTR|AM79C971_CSR0_IENA)) + { pci_dev_trigger_irq(d->vm,d->pci_dev); - return(TRUE); + } else { + pci_dev_clear_irq(d->vm,d->pci_dev); } - - return(FALSE); } /* Update RX/TX ON bits of csr0 */ @@ -324,17 +341,11 @@ /* 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, +static void am79c971_rdp_access(cpu_gen_t *cpu,struct am79c971_data *d, u_int op_type,m_uint64_t *data) { m_uint32_t mask; @@ -361,14 +372,20 @@ //AM79C971_LOG(d,"stopping interface!\n"); d->csr[0] = AM79C971_CSR0_STOP; d->tx_pos = d->rx_pos = 0; + am79c971_update_irq_status(d); 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; + + if (++d->rx_tx_clear_count == 3) { + mask |= AM79C971_CSR0_RINT | AM79C971_CSR0_TINT; + d->rx_tx_clear_count = 0; + } + d->csr[0] &= ~(*data & mask); /* Save the Interrupt Enable bit */ @@ -388,6 +405,9 @@ d->csr[0] &= ~AM79C971_CSR0_STOP; am79c971_update_rx_tx_on_bits(d); } + + /* Update IRQ status */ + am79c971_update_irq_status(d); } break; @@ -399,7 +419,7 @@ } else { *data = (d->tx_l2len << 12) | (d->rx_l2len << 8); } - break; + break; case 15: /* CSR15: Mode */ if (op_type == MTS_WRITE) { @@ -442,7 +462,7 @@ } /* BDP (BCR Data Port) access */ -static void am79c971_bdp_access(cpu_mips_t *cpu,struct am79c971_data *d, +static void am79c971_bdp_access(cpu_gen_t *cpu,struct am79c971_data *d, u_int op_type,m_uint64_t *data) { u_int mii_phy,mii_reg; @@ -482,7 +502,8 @@ 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", + cpu_log(cpu,d->name, + "write access to unknown BCR %d, value=0x%x\n", d->rap,*data); } #endif @@ -492,7 +513,7 @@ /* * dev_am79c971_access() */ -void *dev_am79c971_access(cpu_mips_t *cpu,struct vdevice *dev, +void *dev_am79c971_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { @@ -504,13 +525,15 @@ #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); + offset,cpu_get_pc(cpu),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); + "val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); } #endif + AM79C971_LOCK(d); + switch(offset) { case 0x14: /* RAP (Register Address Pointer) */ if (op_type == MTS_WRITE) { @@ -529,6 +552,7 @@ break; } + AM79C971_UNLOCK(d); return NULL; } @@ -617,7 +641,7 @@ u_char *pkt_ptr = pkt; m_uint8_t sw_style; int i; - + /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,AM79C971_MAX_PKT_SIZE); @@ -699,8 +723,7 @@ physmem_copy_u32_to_vm(d->vm,rx_start+4,rxd0.rmd[1]); d->csr[0] |= AM79C971_CSR0_RINT; - am79c971_update_intr_flag(d); - am79c971_trigger_irq(d); + am79c971_update_irq_status(d); return(TRUE); } @@ -715,7 +738,7 @@ * Don't start receive if the RX ring address has not been set * and if RX ON is not set. */ - if ((d->rx_start == 0) || !(d->csr[0] & AM79C971_CSR0_TXON)) + if ((d->rx_start == 0) || !(d->csr[0] & AM79C971_CSR0_RXON)) return(FALSE); #if DEBUG_RECEIVE @@ -723,14 +746,18 @@ mem_dump(log_file,pkt,pkt_len); #endif + AM79C971_LOCK(d); + /* * Receive only multicast/broadcast trafic + unicast traffic * for this virtual machine. */ hdr = (n_eth_hdr_t *)pkt; + if (am79c971_handle_mac_addr(d,pkt)) am79c971_receive_pkt(d,pkt,pkt_len); + AM79C971_UNLOCK(d); return(TRUE); } @@ -792,7 +819,7 @@ struct tx_desc txd0,ctxd,ntxd,*ptxd; m_uint32_t tx_start,tx_current; m_uint32_t clen,tot_len; - + if ((d->tx_start == 0) || !(d->csr[0] & AM79C971_CSR0_TXON)) return(FALSE); @@ -800,11 +827,11 @@ tx_start = tx_current = txdesc_get_current(d); ptxd = &txd0; txdesc_read(d,tx_start,ptxd); - + /* If we don't own the first descriptor, we cannot transmit */ if (!(ptxd->tmd[1] & AM79C971_TMD1_OWN)) return(FALSE); - + #if DEBUG_TRANSMIT AM79C971_LOG(d,"am79c971_handle_txring: 1st desc: " "tmd[0]=0x%x, tmd[1]=0x%x, tmd[2]=0x%x, tmd[3]=0x%x\n", @@ -824,6 +851,7 @@ /* Copy packet data */ clen = ~((ptxd->tmd[1] & AM79C971_TMD1_LEN) - 1); clen &= AM79C971_TMD1_LEN; + physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->tmd[0],clen); pkt_ptr += clen; @@ -860,6 +888,9 @@ AM79C971_LOG(d,"sending packet of %u bytes\n",tot_len); mem_dump(log_file,pkt,tot_len); #endif + /* rewrite ISL header if required */ + cisco_isl_rewrite(pkt,tot_len); + /* send it on wire */ netio_send(d->nio,pkt,tot_len); } @@ -870,8 +901,7 @@ /* Generate TX interrupt */ d->csr[0] |= AM79C971_CSR0_TINT; - am79c971_update_intr_flag(d); - am79c971_trigger_irq(d); + am79c971_update_irq_status(d); return(TRUE); } @@ -880,10 +910,13 @@ { int i; + AM79C971_LOCK(d); + for(i=0;ipriv_data; @@ -918,7 +951,7 @@ * * Write a PCI register. */ -static void pci_am79c971_write(cpu_mips_t *cpu,struct pci_device *dev, +static void pci_am79c971_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct am79c971_data *d = dev->priv_data; @@ -956,6 +989,7 @@ memset(d,0,sizeof(*d)); memcpy(d->mii_regs[0],mii_reg_values,sizeof(mii_reg_values)); + pthread_mutex_init(&d->lock,NULL); /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, @@ -1007,7 +1041,7 @@ /* Bind a NIO to an AMD Am79c971 device */ int dev_am79c971_set_nio(struct am79c971_data *d,netio_desc_t *nio) -{ +{ /* check that a NIO is not already bound */ if (d->nio != NULL) return(-1);