X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/3b46e6242767a2c770c0aba0a6595e9511623c92..93f43c48887a4e5b651965955eedd8e838e53db3:/hw/ne2000.c diff --git a/hw/ne2000.c b/hw/ne2000.c index 689216c6ab..62e082f8a9 100644 --- a/hw/ne2000.c +++ b/hw/ne2000.c @@ -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 */ - qemu_irq 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; @@ -209,9 +189,9 @@ static int ne2000_buffer_full(NE2000State *s) return 0; } -static int ne2000_can_receive(void *opaque) +int ne2000_can_receive(VLANClientState *nc) { - NE2000State *s = opaque; + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; if (s->cmd & E8390_STOP) return 1; @@ -220,9 +200,10 @@ static int ne2000_can_receive(void *opaque) #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; unsigned int total_len, next, avail, len, index, mcast_idx; uint8_t buf1[60]; @@ -234,7 +215,7 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size) #endif if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) - return; + return -1; /* XXX: check this */ if (s->rxcr & 0x10) { @@ -243,14 +224,14 @@ 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] && @@ -259,7 +240,7 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size) s->mem[10] == buf[5]) { /* match */ } else { - return; + return size; } } @@ -312,6 +293,8 @@ static void ne2000_receive(void *opaque, const uint8_t *buf, int size) /* 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) @@ -341,7 +324,7 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) 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 transfer */ s->tsr = ENTSR_PTX; @@ -641,185 +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; - int tmp; - - if (s->pci_dev) - pci_device_save(s->pci_dev, f); - - qemu_put_8s(f, &s->rxcr); - - 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); - tmp = 0; - qemu_put_be32s(f, &tmp); /* ignored, was irq */ - qemu_put_buffer(f, s->mem, NE2000_MEM_SIZE); + NE2000State* s = opaque; + + if (version_id < 2) { + s->rxcr = 0x0c; + } + return 0; } -static int ne2000_load(QEMUFile* f,void* opaque,int version_id) -{ - NE2000State* s=(NE2000State*)opaque; - int ret; - int tmp; +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() + } +}; - if (version_id > 3) - return -EINVAL; +static uint64_t ne2000_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + NE2000State *s = opaque; - if (s->pci_dev && version_id >= 3) { - ret = pci_device_load(s->pci_dev, f); - if (ret < 0) - return ret; + 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; +} + +static void ne2000_write(void *opaque, target_phys_addr_t addr, + uint64_t data, unsigned size) +{ + NE2000State *s = opaque; - if (version_id >= 2) { - qemu_get_8s(f, &s->rxcr); + 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 { - s->rxcr = 0x0c; + return ne2000_asic_ioport_writel(s, addr, data); } + } else if (addr == 0x1f && size == 1) { + return ne2000_reset_ioport_write(s, addr, data); + } +} - 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, &tmp); /* ignored */ - qemu_get_buffer(f, s->mem, NE2000_MEM_SIZE); - - return 0; +static const MemoryRegionOps ne2000_ops = { + .read = ne2000_read, + .write = ne2000_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/***********************************************************/ +/* PCI NE2000 definitions */ + +void ne2000_setup_io(NE2000State *s, unsigned size) +{ + memory_region_init_io(&s->io, &ne2000_ops, s, "ne2000", size); } -void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd) +static void ne2000_cleanup(VLANClientState *nc) { - NE2000State *s; + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; - s = qemu_mallocz(sizeof(NE2000State)); - if (!s) - return; + s->nic = NULL; +} - register_ioport_write(base, 16, 1, ne2000_ioport_write, s); - register_ioport_read(base, 16, 1, ne2000_ioport_read, s); +static NetClientInfo net_ne2000_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = ne2000_can_receive, + .receive = ne2000_receive, + .cleanup = ne2000_cleanup, +}; - 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 int pci_ne2000_init(PCIDevice *pci_dev) +{ + PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); + NE2000State *s; + uint8_t *pci_conf; + + pci_conf = d->dev.config; + pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */ - 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); + s = &d->ne2000; + 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, - ne2000_can_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]); + 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); - register_savevm("ne2000", 0, 2, ne2000_save, ne2000_load, s); -} + if (!pci_dev->qdev.hotplugged) { + static int loaded = 0; + if (!loaded) { + rom_add_option("pxe-ne2k_pci.rom", -1); + loaded = 1; + } + } -/***********************************************************/ -/* PCI NE2000 definitions */ + add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); -typedef struct PCINE2000State { - PCIDevice dev; - NE2000State ne2000; -} PCINE2000State; + return 0; +} -static void ne2000_map(PCIDevice *pci_dev, int region_num, - uint32_t addr, uint32_t size, int type) +static int pci_ne2000_exit(PCIDevice *pci_dev) { - PCINE2000State *d = (PCINE2000State *)pci_dev; + PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, 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); - - 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); - - register_ioport_write(addr + 0x1f, 1, 1, ne2000_reset_ioport_write, s); - register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s); + memory_region_destroy(&s->io); + qemu_del_vlan_client(&s->nic->nc); + return 0; } -void pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn) -{ - PCINE2000State *d; - NE2000State *s; - uint8_t *pci_conf; +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(), + } +}; - d = (PCINE2000State *)pci_register_device(bus, - "NE2000", sizeof(PCINE2000State), - devfn, - 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); - s = &d->ne2000; - s->irq = d->dev.irq[0]; - s->pci_dev = (PCIDevice *)d; - memcpy(s->macaddr, nd->macaddr, 6); - ne2000_reset(s); - s->vc = qemu_new_vlan_client(nd->vlan, ne2000_receive, - ne2000_can_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, 3, ne2000_save, ne2000_load, s); +static void ne2000_register_devices(void) +{ + pci_qdev_register(&ne2000_info); } + +device_init(ne2000_register_devices)