X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/089af991182fb44915c30075ea4e202ad268afcb..430ee2f26f01f146f3757467b3f0b802ad309ff8:/hw/ne2000.c diff --git a/hw/ne2000.c b/hw/ne2000.c index 2940abd35c..62e082f8a9 100644 --- a/hw/ne2000.c +++ b/hw/ne2000.c @@ -1,8 +1,8 @@ /* * QEMU NE2000 emulation - * + * * Copyright (c) 2003-2004 Fabrice Bellard - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -21,7 +21,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "vl.h" +#include "hw.h" +#include "pci.h" +#include "net.h" +#include "ne2000.h" +#include "loader.h" +#include "sysemu.h" /* debug NE2000 card */ //#define DEBUG_NE2000 @@ -113,42 +118,17 @@ #define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ #define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ -#define NE2000_PMEM_SIZE (32*1024) -#define NE2000_PMEM_START (16*1024) -#define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START) -#define NE2000_MEM_SIZE NE2000_PMEM_END - -typedef struct NE2000State { - uint8_t cmd; - uint32_t start; - uint32_t stop; - uint8_t boundary; - uint8_t tsr; - uint8_t tpsr; - uint16_t tcnt; - uint16_t rcnt; - uint32_t rsar; - uint8_t rsr; - uint8_t rxcr; - uint8_t isr; - uint8_t dcfg; - uint8_t imr; - uint8_t phys[6]; /* mac address */ - uint8_t curpag; - uint8_t mult[8]; /* multicast mask array */ - int irq; - PCIDevice *pci_dev; - VLANClientState *vc; - uint8_t macaddr[6]; - uint8_t mem[NE2000_MEM_SIZE]; -} NE2000State; - -static void ne2000_reset(NE2000State *s) +typedef struct PCINE2000State { + PCIDevice dev; + NE2000State ne2000; +} PCINE2000State; + +void ne2000_reset(NE2000State *s) { int i; s->isr = ENISR_RESET; - memcpy(s->mem, s->macaddr, 6); + memcpy(s->mem, &s->c.macaddr, 6); s->mem[14] = 0x57; s->mem[15] = 0x57; @@ -164,16 +144,10 @@ static void ne2000_update_irq(NE2000State *s) int isr; isr = (s->isr & s->imr) & 0x7f; #if defined(DEBUG_NE2000) - printf("NE2000: Set IRQ line %d to %d (%02x %02x)\n", - s->irq, isr ? 1 : 0, s->isr, s->imr); + printf("NE2000: Set IRQ to %d (%02x %02x)\n", + isr ? 1 : 0, s->isr, s->imr); #endif - if (s->irq == 16) { - /* PCI irq */ - pci_set_irq(s->pci_dev, 0, (isr != 0)); - } else { - /* ISA irq */ - pic_set_irq(s->irq, (isr != 0)); - } + qemu_set_irq(s->irq, (isr != 0)); } #define POLYNOMIAL 0x04c11db6 @@ -200,14 +174,10 @@ static int compute_mcast_idx(const uint8_t *ep) return (crc >> 26); } -/* return the max buffer size if the NE2000 can receive more data */ -static int ne2000_can_receive(void *opaque) +static int ne2000_buffer_full(NE2000State *s) { - NE2000State *s = opaque; int avail, index, boundary; - - if (s->cmd & E8390_STOP) - return 0; + index = s->curpag << 8; boundary = s->boundary << 8; if (index < boundary) @@ -215,28 +185,38 @@ static int ne2000_can_receive(void *opaque) else avail = (s->stop - s->start) - (index - boundary); if (avail < (MAX_ETH_FRAME_SIZE + 4)) - return 0; - return MAX_ETH_FRAME_SIZE; + return 1; + return 0; +} + +int ne2000_can_receive(VLANClientState *nc) +{ + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + + if (s->cmd & E8390_STOP) + return 1; + return !ne2000_buffer_full(s); } #define MIN_BUF_SIZE 60 -static void ne2000_receive(void *opaque, const uint8_t *buf, int size) +ssize_t ne2000_receive(VLANClientState *nc, const uint8_t *buf, size_t size_) { - NE2000State *s = opaque; + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; + int size = size_; uint8_t *p; - int total_len, next, avail, len, index, mcast_idx; + unsigned int total_len, next, avail, len, index, mcast_idx; uint8_t buf1[60]; - static const uint8_t broadcast_macaddr[6] = + static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - + #if defined(DEBUG_NE2000) printf("NE2000: received len=%d\n", size); #endif - if (!ne2000_can_receive(s)) - return; - + if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) + return -1; + /* XXX: check this */ if (s->rxcr & 0x10) { /* promiscuous: receive all */ @@ -244,23 +224,23 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size) if (!memcmp(buf, broadcast_macaddr, 6)) { /* broadcast address */ if (!(s->rxcr & 0x04)) - return; + return size; } else if (buf[0] & 0x01) { /* multicast */ if (!(s->rxcr & 0x08)) - return; + return size; mcast_idx = compute_mcast_idx(buf); if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) - return; + return size; } else if (s->mem[0] == buf[0] && - s->mem[2] == buf[1] && - s->mem[4] == buf[2] && - s->mem[6] == buf[3] && - s->mem[8] == buf[4] && + s->mem[2] == buf[1] && + s->mem[4] == buf[2] && + s->mem[6] == buf[3] && + s->mem[8] == buf[4] && s->mem[10] == buf[5]) { /* match */ } else { - return; + return size; } } @@ -294,7 +274,10 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size) /* write packet data */ while (size > 0) { - avail = s->stop - index; + if (index <= s->stop) + avail = s->stop - index; + else + avail = 0; len = size; if (len > avail) len = avail; @@ -307,9 +290,11 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size) } s->curpag = next >> 8; - /* now we can signal we have receive something */ + /* now we can signal we have received something */ s->isr |= ENISR_RX; ne2000_update_irq(s); + + return size_; } static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) @@ -326,7 +311,7 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->cmd = val; if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */ s->isr &= ~ENISR_RESET; - /* test specific case: zero length transfert */ + /* test specific case: zero length transfer */ if ((val & (E8390_RREAD | E8390_RWRITE)) && s->rcnt == 0) { s->isr |= ENISR_RDC; @@ -334,17 +319,17 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) } if (val & E8390_TRANS) { index = (s->tpsr << 8); - /* XXX: next 2 lines are a hack to make netware 3.11 work */ + /* XXX: next 2 lines are a hack to make netware 3.11 work */ if (index >= NE2000_PMEM_END) index -= NE2000_PMEM_SIZE; /* fail safe: check range on the transmitted length */ if (index + s->tcnt <= NE2000_PMEM_END) { - qemu_send_packet(s->vc, s->mem + index, s->tcnt); + qemu_send_packet(&s->nic->nc, s->mem + index, s->tcnt); } - /* signal end of transfert */ + /* signal end of transfer */ s->tsr = ENTSR_PTX; s->isr |= ENISR_TX; - s->cmd &= ~E8390_TRANS; + s->cmd &= ~E8390_TRANS; ne2000_update_irq(s); } } @@ -480,30 +465,30 @@ static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) return ret; } -static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr, +static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr, uint32_t val) { - if (addr < 32 || + if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { s->mem[addr] = val; } } -static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr, +static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr, uint32_t val) { addr &= ~1; /* XXX: check exact behaviour if not even */ - if (addr < 32 || + if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { *(uint16_t *)(s->mem + addr) = cpu_to_le16(val); } } -static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, +static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, uint32_t val) { addr &= ~1; /* XXX: check exact behaviour if not even */ - if (addr < 32 || + if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { cpu_to_le32wu((uint32_t *)(s->mem + addr), val); } @@ -511,7 +496,7 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr) { - if (addr < 32 || + if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { return s->mem[addr]; } else { @@ -522,7 +507,7 @@ static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr) static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) { addr &= ~1; /* XXX: check exact behaviour if not even */ - if (addr < 32 || + if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { return le16_to_cpu(*(uint16_t *)(s->mem + addr)); } else { @@ -533,7 +518,7 @@ static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) { addr &= ~1; /* XXX: check exact behaviour if not even */ - if (addr < 32 || + if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { return le32_to_cpupu((uint32_t *)(s->mem + addr)); } else { @@ -551,7 +536,7 @@ static inline void ne2000_dma_update(NE2000State *s, int len) if (s->rcnt <= len) { s->rcnt = 0; - /* signal end of transfert */ + /* signal end of transfer */ s->isr |= ENISR_RDC; ne2000_update_irq(s); } else { @@ -639,164 +624,186 @@ static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr) return 0; } -static void ne2000_save(QEMUFile* f,void* opaque) +static int ne2000_post_load(void* opaque, int version_id) { - NE2000State* s=(NE2000State*)opaque; - - qemu_put_8s(f, &s->cmd); - qemu_put_be32s(f, &s->start); - qemu_put_be32s(f, &s->stop); - qemu_put_8s(f, &s->boundary); - qemu_put_8s(f, &s->tsr); - qemu_put_8s(f, &s->tpsr); - qemu_put_be16s(f, &s->tcnt); - qemu_put_be16s(f, &s->rcnt); - qemu_put_be32s(f, &s->rsar); - qemu_put_8s(f, &s->rsr); - qemu_put_8s(f, &s->isr); - qemu_put_8s(f, &s->dcfg); - qemu_put_8s(f, &s->imr); - qemu_put_buffer(f, s->phys, 6); - qemu_put_8s(f, &s->curpag); - qemu_put_buffer(f, s->mult, 8); - qemu_put_be32s(f, &s->irq); - qemu_put_buffer(f, s->mem, NE2000_MEM_SIZE); -} + NE2000State* s = opaque; -static int ne2000_load(QEMUFile* f,void* opaque,int version_id) -{ - NE2000State* s=(NE2000State*)opaque; - - if (version_id != 1) - return -EINVAL; - - qemu_get_8s(f, &s->cmd); - qemu_get_be32s(f, &s->start); - qemu_get_be32s(f, &s->stop); - qemu_get_8s(f, &s->boundary); - qemu_get_8s(f, &s->tsr); - qemu_get_8s(f, &s->tpsr); - qemu_get_be16s(f, &s->tcnt); - qemu_get_be16s(f, &s->rcnt); - qemu_get_be32s(f, &s->rsar); - qemu_get_8s(f, &s->rsr); - qemu_get_8s(f, &s->isr); - qemu_get_8s(f, &s->dcfg); - qemu_get_8s(f, &s->imr); - qemu_get_buffer(f, s->phys, 6); - qemu_get_8s(f, &s->curpag); - qemu_get_buffer(f, s->mult, 8); - qemu_get_be32s(f, &s->irq); - qemu_get_buffer(f, s->mem, NE2000_MEM_SIZE); - - return 0; + if (version_id < 2) { + s->rxcr = 0x0c; + } + return 0; } -void isa_ne2000_init(int base, int irq, NICInfo *nd) -{ - NE2000State *s; - - s = qemu_mallocz(sizeof(NE2000State)); - if (!s) - return; - - register_ioport_write(base, 16, 1, ne2000_ioport_write, s); - register_ioport_read(base, 16, 1, ne2000_ioport_read, s); +const VMStateDescription vmstate_ne2000 = { + .name = "ne2000", + .version_id = 2, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = ne2000_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT8_V(rxcr, NE2000State, 2), + VMSTATE_UINT8(cmd, NE2000State), + VMSTATE_UINT32(start, NE2000State), + VMSTATE_UINT32(stop, NE2000State), + VMSTATE_UINT8(boundary, NE2000State), + VMSTATE_UINT8(tsr, NE2000State), + VMSTATE_UINT8(tpsr, NE2000State), + VMSTATE_UINT16(tcnt, NE2000State), + VMSTATE_UINT16(rcnt, NE2000State), + VMSTATE_UINT32(rsar, NE2000State), + VMSTATE_UINT8(rsr, NE2000State), + VMSTATE_UINT8(isr, NE2000State), + VMSTATE_UINT8(dcfg, NE2000State), + VMSTATE_UINT8(imr, NE2000State), + VMSTATE_BUFFER(phys, NE2000State), + VMSTATE_UINT8(curpag, NE2000State), + VMSTATE_BUFFER(mult, NE2000State), + VMSTATE_UNUSED(4), /* was irq */ + VMSTATE_BUFFER(mem, NE2000State), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pci_ne2000 = { + .name = "ne2000", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, PCINE2000State), + VMSTATE_STRUCT(ne2000, PCINE2000State, 0, vmstate_ne2000, NE2000State), + VMSTATE_END_OF_LIST() + } +}; - register_ioport_write(base + 0x10, 1, 1, ne2000_asic_ioport_write, s); - register_ioport_read(base + 0x10, 1, 1, ne2000_asic_ioport_read, s); - register_ioport_write(base + 0x10, 2, 2, ne2000_asic_ioport_write, s); - register_ioport_read(base + 0x10, 2, 2, ne2000_asic_ioport_read, s); +static uint64_t ne2000_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + NE2000State *s = opaque; - register_ioport_write(base + 0x1f, 1, 1, ne2000_reset_ioport_write, s); - register_ioport_read(base + 0x1f, 1, 1, ne2000_reset_ioport_read, s); - s->irq = irq; - memcpy(s->macaddr, nd->macaddr, 6); + if (addr < 0x10 && size == 1) { + return ne2000_ioport_read(s, addr); + } else if (addr == 0x10) { + if (size <= 2) { + return ne2000_asic_ioport_read(s, addr); + } else { + return ne2000_asic_ioport_readl(s, addr); + } + } else if (addr == 0x1f && size == 1) { + return ne2000_reset_ioport_read(s, addr); + } + return ((uint64_t)1 << (size * 8)) - 1; +} - ne2000_reset(s); +static void ne2000_write(void *opaque, target_phys_addr_t addr, + uint64_t data, unsigned size) +{ + NE2000State *s = opaque; - s->vc = qemu_new_vlan_client(nd->vlan, ne2000_receive, s); - - snprintf(s->vc->info_str, sizeof(s->vc->info_str), - "ne2000 macaddr=%02x:%02x:%02x:%02x:%02x:%02x", - s->macaddr[0], - s->macaddr[1], - s->macaddr[2], - s->macaddr[3], - s->macaddr[4], - s->macaddr[5]); - - register_savevm("ne2000", 0, 1, ne2000_save, ne2000_load, s); + if (addr < 0x10 && size == 1) { + return ne2000_ioport_write(s, addr, data); + } else if (addr == 0x10) { + if (size <= 2) { + return ne2000_asic_ioport_write(s, addr, data); + } else { + return ne2000_asic_ioport_writel(s, addr, data); + } + } else if (addr == 0x1f && size == 1) { + return ne2000_reset_ioport_write(s, addr, data); + } } +static const MemoryRegionOps ne2000_ops = { + .read = ne2000_read, + .write = ne2000_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + /***********************************************************/ /* PCI NE2000 definitions */ -typedef struct PCINE2000State { - PCIDevice dev; - NE2000State ne2000; -} PCINE2000State; - -static void ne2000_map(PCIDevice *pci_dev, int region_num, - uint32_t addr, uint32_t size, int type) +void ne2000_setup_io(NE2000State *s, unsigned size) { - PCINE2000State *d = (PCINE2000State *)pci_dev; - NE2000State *s = &d->ne2000; - - register_ioport_write(addr, 16, 1, ne2000_ioport_write, s); - register_ioport_read(addr, 16, 1, ne2000_ioport_read, s); + memory_region_init_io(&s->io, &ne2000_ops, s, "ne2000", size); +} - register_ioport_write(addr + 0x10, 1, 1, ne2000_asic_ioport_write, s); - register_ioport_read(addr + 0x10, 1, 1, ne2000_asic_ioport_read, s); - register_ioport_write(addr + 0x10, 2, 2, ne2000_asic_ioport_write, s); - register_ioport_read(addr + 0x10, 2, 2, ne2000_asic_ioport_read, s); - register_ioport_write(addr + 0x10, 4, 4, ne2000_asic_ioport_writel, s); - register_ioport_read(addr + 0x10, 4, 4, ne2000_asic_ioport_readl, s); +static void ne2000_cleanup(VLANClientState *nc) +{ + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; - register_ioport_write(addr + 0x1f, 1, 1, ne2000_reset_ioport_write, s); - register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s); + s->nic = NULL; } -void pci_ne2000_init(PCIBus *bus, NICInfo *nd) +static NetClientInfo net_ne2000_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = ne2000_can_receive, + .receive = ne2000_receive, + .cleanup = ne2000_cleanup, +}; + +static int pci_ne2000_init(PCIDevice *pci_dev) { - PCINE2000State *d; + PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); NE2000State *s; uint8_t *pci_conf; - - d = (PCINE2000State *)pci_register_device(bus, - "NE2000", sizeof(PCINE2000State), - -1, - NULL, NULL); + pci_conf = d->dev.config; - pci_conf[0x00] = 0xec; // Realtek 8029 - pci_conf[0x01] = 0x10; - pci_conf[0x02] = 0x29; - pci_conf[0x03] = 0x80; - pci_conf[0x0a] = 0x00; // ethernet network controller - pci_conf[0x0b] = 0x02; - pci_conf[0x0e] = 0x00; // header_type - pci_conf[0x3d] = 1; // interrupt pin 0 - - pci_register_io_region(&d->dev, 0, 0x100, - PCI_ADDRESS_SPACE_IO, ne2000_map); + pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ + s = &d->ne2000; - s->irq = 16; // PCI interrupt - s->pci_dev = (PCIDevice *)d; - memcpy(s->macaddr, nd->macaddr, 6); + ne2000_setup_io(s, 0x100); + pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); + s->irq = d->dev.irq[0]; + + qemu_macaddr_default_if_unset(&s->c.macaddr); ne2000_reset(s); - s->vc = qemu_new_vlan_client(nd->vlan, ne2000_receive, s); - - snprintf(s->vc->info_str, sizeof(s->vc->info_str), - "ne2000 pci macaddr=%02x:%02x:%02x:%02x:%02x:%02x", - s->macaddr[0], - s->macaddr[1], - s->macaddr[2], - s->macaddr[3], - s->macaddr[4], - s->macaddr[5]); - - /* XXX: instance number ? */ - register_savevm("ne2000", 0, 1, ne2000_save, ne2000_load, s); - register_savevm("ne2000_pci", 0, 1, generic_pci_save, generic_pci_load, - &d->dev); + + s->nic = qemu_new_nic(&net_ne2000_info, &s->c, + pci_dev->qdev.info->name, pci_dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a); + + if (!pci_dev->qdev.hotplugged) { + static int loaded = 0; + if (!loaded) { + rom_add_option("pxe-ne2k_pci.rom", -1); + loaded = 1; + } + } + + add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); + + return 0; } + +static int pci_ne2000_exit(PCIDevice *pci_dev) +{ + PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); + NE2000State *s = &d->ne2000; + + memory_region_destroy(&s->io); + qemu_del_vlan_client(&s->nic->nc); + return 0; +} + +static PCIDeviceInfo ne2000_info = { + .qdev.name = "ne2k_pci", + .qdev.size = sizeof(PCINE2000State), + .qdev.vmsd = &vmstate_pci_ne2000, + .init = pci_ne2000_init, + .exit = pci_ne2000_exit, + .vendor_id = PCI_VENDOR_ID_REALTEK, + .device_id = PCI_DEVICE_ID_REALTEK_8029, + .class_id = PCI_CLASS_NETWORK_ETHERNET, + .qdev.props = (Property[]) { + DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void ne2000_register_devices(void) +{ + pci_qdev_register(&ne2000_info); +} + +device_init(ne2000_register_devices)