--- upstream/dynamips-0.2.6-RC5/dev_am79c971.c 2007/10/06 16:09:07 6 +++ upstream/dynamips-0.2.7/dev_am79c971.c 2007/10/06 16:29:14 10 @@ -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" @@ -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; @@ -492,7 +512,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 +524,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 +551,7 @@ break; } + AM79C971_UNLOCK(d); return NULL; } @@ -617,7 +640,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 +722,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); } @@ -723,6 +745,8 @@ mem_dump(log_file,pkt,pkt_len); #endif + AM79C971_LOCK(d); + /* * Receive only multicast/broadcast trafic + unicast traffic * for this virtual machine. @@ -731,6 +755,7 @@ if (am79c971_handle_mac_addr(d,pkt)) am79c971_receive_pkt(d,pkt,pkt_len); + AM79C971_UNLOCK(d); return(TRUE); } @@ -824,6 +849,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; @@ -870,8 +896,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 +905,13 @@ { int i; + AM79C971_LOCK(d); + for(i=0;ipriv_data; @@ -918,7 +946,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 +984,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,