diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/core/loader.c | 30 | ||||
-rw-r--r-- | hw/core/qdev.c | 20 | ||||
-rw-r--r-- | hw/i386/pc_piix.c | 8 | ||||
-rw-r--r-- | hw/intc/openpic_kvm.c | 2 | ||||
-rw-r--r-- | hw/intc/xics.c | 15 | ||||
-rw-r--r-- | hw/intc/xics_kvm.c | 10 | ||||
-rw-r--r-- | hw/moxie/moxiesim.c | 2 | ||||
-rw-r--r-- | hw/net/Makefile.objs | 3 | ||||
-rw-r--r-- | hw/net/fsl_etsec/etsec.c | 465 | ||||
-rw-r--r-- | hw/net/fsl_etsec/etsec.h | 174 | ||||
-rw-r--r-- | hw/net/fsl_etsec/miim.c | 146 | ||||
-rw-r--r-- | hw/net/fsl_etsec/registers.c | 295 | ||||
-rw-r--r-- | hw/net/fsl_etsec/registers.h | 320 | ||||
-rw-r--r-- | hw/net/fsl_etsec/rings.c | 650 | ||||
-rw-r--r-- | hw/net/spapr_llan.c | 2 | ||||
-rw-r--r-- | hw/ppc/e500.c | 7 | ||||
-rw-r--r-- | hw/ppc/ppc.c | 22 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 56 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 87 | ||||
-rw-r--r-- | hw/ppc/spapr_iommu.c | 37 | ||||
-rw-r--r-- | hw/ppc/spapr_pci.c | 15 | ||||
-rw-r--r-- | hw/ppc/spapr_rtas.c | 14 | ||||
-rw-r--r-- | hw/ppc/virtex_ml507.c | 34 | ||||
-rw-r--r-- | hw/s390x/ipl.c | 4 | ||||
-rw-r--r-- | hw/scsi/spapr_vscsi.c | 60 |
25 files changed, 2393 insertions, 85 deletions
diff --git a/hw/core/loader.c b/hw/core/loader.c index e1c3f3a860..b323c0c7b8 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -284,12 +284,30 @@ static void *load_at(int fd, int offset, int size) #define SZ 64 #include "hw/elf_ops.h" +const char *load_elf_strerror(int error) +{ + switch (error) { + case 0: + return "No error"; + case ELF_LOAD_FAILED: + return "Failed to load ELF"; + case ELF_LOAD_NOT_ELF: + return "The image is not ELF"; + case ELF_LOAD_WRONG_ARCH: + return "The image is from incompatible architecture"; + case ELF_LOAD_WRONG_ENDIAN: + return "The image has incorrect endianness"; + default: + return "Unknown error"; + } +} + /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb) { - int fd, data_order, target_data_order, must_swab, ret; + int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; fd = open(filename, O_RDONLY | O_BINARY); @@ -302,8 +320,10 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), if (e_ident[0] != ELFMAG0 || e_ident[1] != ELFMAG1 || e_ident[2] != ELFMAG2 || - e_ident[3] != ELFMAG3) + e_ident[3] != ELFMAG3) { + ret = ELF_LOAD_NOT_ELF; goto fail; + } #ifdef HOST_WORDS_BIGENDIAN data_order = ELFDATA2MSB; #else @@ -317,6 +337,7 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), } if (target_data_order != e_ident[EI_DATA]) { + ret = ELF_LOAD_WRONG_ENDIAN; goto fail; } @@ -329,12 +350,9 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), pentry, lowaddr, highaddr, elf_machine, clear_lsb); } - close(fd); - return ret; - fail: close(fd); - return -1; + return ret; } static void bswap_uboot_header(uboot_image_header_t *hdr) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index c0b857fbd4..380976a066 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -440,27 +440,33 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id) static void qbus_realize(BusState *bus, DeviceState *parent, const char *name) { const char *typename = object_get_typename(OBJECT(bus)); + BusClass *bc; char *buf; - int i,len; + int i, len, bus_id; bus->parent = parent; if (name) { bus->name = g_strdup(name); } else if (bus->parent && bus->parent->id) { - /* parent device has id -> use it for bus name */ + /* parent device has id -> use it plus parent-bus-id for bus name */ + bus_id = bus->parent->num_child_bus; + len = strlen(bus->parent->id) + 16; buf = g_malloc(len); - snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus); + snprintf(buf, len, "%s.%d", bus->parent->id, bus_id); bus->name = buf; } else { - /* no id -> use lowercase bus type for bus name */ + /* no id -> use lowercase bus type plus global bus-id for bus name */ + bc = BUS_GET_CLASS(bus); + bus_id = bc->automatic_ids++; + len = strlen(typename) + 16; buf = g_malloc(len); - len = snprintf(buf, len, "%s.%d", typename, - bus->parent ? bus->parent->num_child_bus : 0); - for (i = 0; i < len; i++) + len = snprintf(buf, len, "%s.%d", typename, bus_id); + for (i = 0; i < len; i++) { buf[i] = qemu_tolower(buf[i]); + } bus->name = buf; } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d5dc1ef336..ae1699d6db 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -221,10 +221,16 @@ static void pc_init1(QEMUMachineInitArgs *args, } else { for(i = 0; i < MAX_IDE_BUS; i++) { ISADevice *dev; + char busname[] = "ide.0"; dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - idebus[i] = qdev_get_child_bus(DEVICE(dev), "ide.0"); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); } } diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index c7f7b8406c..87fdb126cf 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -228,7 +228,7 @@ int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs) encap.cap = KVM_CAP_IRQ_MPIC; encap.args[0] = opp->fd; - encap.args[1] = cs->cpu_index; + encap.args[1] = kvm_arch_vcpu_id(cs); return kvm_vcpu_ioctl(cs, KVM_ENABLE_CAP, &encap); } diff --git a/hw/intc/xics.c b/hw/intc/xics.c index b437563fb9..64aabe753d 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -33,6 +33,17 @@ #include "qemu/error-report.h" #include "qapi/visitor.h" +static int get_cpu_index_by_dt_id(int cpu_dt_id) +{ + PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); + + if (cpu) { + return cpu->parent_obj.cpu_index; + } + + return -1; +} + void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -659,7 +670,7 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr, static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { - target_ulong server = args[0]; + target_ulong server = get_cpu_index_by_dt_id(args[0]); target_ulong mfrr = args[1]; if (server >= spapr->icp->nr_servers) { @@ -728,7 +739,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, } nr = rtas_ld(args, 0); - server = rtas_ld(args, 1); + server = get_cpu_index_by_dt_id(rtas_ld(args, 1)); priority = rtas_ld(args, 2); if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index c203646bd6..a5bbc2406d 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -65,7 +65,7 @@ static void icp_get_kvm_state(ICPState *ss) ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, ®); if (ret != 0) { error_report("Unable to retrieve KVM interrupt controller state" - " for CPU %d: %s", ss->cs->cpu_index, strerror(errno)); + " for CPU %ld: %s", kvm_arch_vcpu_id(ss->cs), strerror(errno)); exit(1); } @@ -97,7 +97,7 @@ static int icp_set_kvm_state(ICPState *ss, int version_id) ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, ®); if (ret != 0) { error_report("Unable to restore KVM interrupt controller state (0x%" - PRIx64 ") for CPU %d: %s", state, ss->cs->cpu_index, + PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(ss->cs), strerror(errno)); return ret; } @@ -325,15 +325,15 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) struct kvm_enable_cap xics_enable_cap = { .cap = KVM_CAP_IRQ_XICS, .flags = 0, - .args = {icpkvm->kernel_xics_fd, cs->cpu_index, 0, 0}, + .args = {icpkvm->kernel_xics_fd, kvm_arch_vcpu_id(cs), 0, 0}, }; ss->cs = cs; ret = kvm_vcpu_ioctl(ss->cs, KVM_ENABLE_CAP, &xics_enable_cap); if (ret < 0) { - error_report("Unable to connect CPU%d to kernel XICS: %s", - cs->cpu_index, strerror(errno)); + error_report("Unable to connect CPU%ld to kernel XICS: %s", + kvm_arch_vcpu_id(cs), strerror(errno)); exit(1); } } diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index ef4f3a84d7..a87ca6ddcc 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -55,7 +55,7 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) &entry, &kernel_low, &kernel_high, 1, ELF_MACHINE, 0); - if (!kernel_size) { + if (kernel_size <= 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loader_params->kernel_filename); exit(1); diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 75e80c2c48..ea93293122 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -32,3 +32,6 @@ obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o obj-$(CONFIG_VIRTIO) += virtio-net.o obj-y += vhost_net.o + +obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ + fsl_etsec/rings.o fsl_etsec/miim.o diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c new file mode 100644 index 0000000000..d4b4429446 --- /dev/null +++ b/hw/net/fsl_etsec/etsec.c @@ -0,0 +1,465 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * This implementation doesn't include ring priority, TCP/IP Off-Load, QoS. + */ + +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "hw/ptimer.h" +#include "etsec.h" +#include "registers.h" + +/* #define HEX_DUMP */ +/* #define DEBUG_REGISTER */ + +#ifdef DEBUG_REGISTER +static const int debug_etsec = 1; +#else +static const int debug_etsec; +#endif + +#define DPRINTF(fmt, ...) do { \ + if (debug_etsec) { \ + qemu_log(fmt , ## __VA_ARGS__); \ + } \ + } while (0) + +static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size) +{ + eTSEC *etsec = opaque; + uint32_t reg_index = addr / 4; + eTSEC_Register *reg = NULL; + uint32_t ret = 0x0; + + assert(reg_index < ETSEC_REG_NUMBER); + + reg = &etsec->regs[reg_index]; + + + switch (reg->access) { + case ACC_WO: + ret = 0x00000000; + break; + + case ACC_RW: + case ACC_W1C: + case ACC_RO: + default: + ret = reg->value; + break; + } + + DPRINTF("Read 0x%08x @ 0x" TARGET_FMT_plx + " : %s (%s)\n", + ret, addr, reg->name, reg->desc); + + return ret; +} + +static void write_tstat(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + int i = 0; + + for (i = 0; i < 8; i++) { + /* Check THLTi flag in TSTAT */ + if (value & (1 << (31 - i))) { + etsec_walk_tx_ring(etsec, i); + } + } + + /* Write 1 to clear */ + reg->value &= ~value; +} + +static void write_rstat(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + int i = 0; + + for (i = 0; i < 8; i++) { + /* Check QHLTi flag in RSTAT */ + if (value & (1 << (23 - i)) && !(reg->value & (1 << (23 - i)))) { + etsec_walk_rx_ring(etsec, i); + } + } + + /* Write 1 to clear */ + reg->value &= ~value; +} + +static void write_tbasex(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value & ~0x7; + + /* Copy this value in the ring's TxBD pointer */ + etsec->regs[TBPTR0 + (reg_index - TBASE0)].value = value & ~0x7; +} + +static void write_rbasex(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value & ~0x7; + + /* Copy this value in the ring's RxBD pointer */ + etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7; +} + +static void write_ievent(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + /* Write 1 to clear */ + reg->value &= ~value; + + if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) { + qemu_irq_lower(etsec->tx_irq); + } + if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) { + qemu_irq_lower(etsec->rx_irq); + } + + if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | + IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | + IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | + IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | + IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | + IEVENT_MMRW))) { + qemu_irq_lower(etsec->err_irq); + } +} + +static void write_dmactrl(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + reg->value = value; + + if (value & DMACTRL_GRS) { + + if (etsec->rx_buffer_len != 0) { + /* Graceful receive stop delayed until end of frame */ + } else { + /* Graceful receive stop now */ + etsec->regs[IEVENT].value |= IEVENT_GRSC; + if (etsec->regs[IMASK].value & IMASK_GRSCEN) { + qemu_irq_raise(etsec->err_irq); + } + } + } + + if (value & DMACTRL_GTS) { + + if (etsec->tx_buffer_len != 0) { + /* Graceful transmit stop delayed until end of frame */ + } else { + /* Graceful transmit stop now */ + etsec->regs[IEVENT].value |= IEVENT_GTSC; + if (etsec->regs[IMASK].value & IMASK_GTSCEN) { + qemu_irq_raise(etsec->err_irq); + } + } + } + + if (!(value & DMACTRL_WOP)) { + /* Start polling */ + ptimer_stop(etsec->ptimer); + ptimer_set_count(etsec->ptimer, 1); + ptimer_run(etsec->ptimer, 1); + } +} + +static void etsec_write(void *opaque, + hwaddr addr, + uint64_t value, + unsigned size) +{ + eTSEC *etsec = opaque; + uint32_t reg_index = addr / 4; + eTSEC_Register *reg = NULL; + uint32_t before = 0x0; + + assert(reg_index < ETSEC_REG_NUMBER); + + reg = &etsec->regs[reg_index]; + before = reg->value; + + switch (reg_index) { + case IEVENT: + write_ievent(etsec, reg, reg_index, value); + break; + + case DMACTRL: + write_dmactrl(etsec, reg, reg_index, value); + break; + + case TSTAT: + write_tstat(etsec, reg, reg_index, value); + break; + + case RSTAT: + write_rstat(etsec, reg, reg_index, value); + break; + + case TBASE0 ... TBASE7: + write_tbasex(etsec, reg, reg_index, value); + break; + + case RBASE0 ... RBASE7: + write_rbasex(etsec, reg, reg_index, value); + break; + + case MIIMCFG ... MIIMIND: + etsec_write_miim(etsec, reg, reg_index, value); + break; + + default: + /* Default handling */ + switch (reg->access) { + + case ACC_RW: + case ACC_WO: + reg->value = value; + break; + + case ACC_W1C: + reg->value &= ~value; + break; + + case ACC_RO: + default: + /* Read Only or Unknown register */ + break; + } + } + + DPRINTF("Write 0x%08x @ 0x" TARGET_FMT_plx + " val:0x%08x->0x%08x : %s (%s)\n", + (unsigned int)value, addr, before, reg->value, + reg->name, reg->desc); +} + +static const MemoryRegionOps etsec_ops = { + .read = etsec_read, + .write = etsec_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void etsec_timer_hit(void *opaque) +{ + eTSEC *etsec = opaque; + + ptimer_stop(etsec->ptimer); + + if (!(etsec->regs[DMACTRL].value & DMACTRL_WOP)) { + + if (!(etsec->regs[DMACTRL].value & DMACTRL_GTS)) { + etsec_walk_tx_ring(etsec, 0); + } + ptimer_set_count(etsec->ptimer, 1); + ptimer_run(etsec->ptimer, 1); + } +} + +static void etsec_reset(DeviceState *d) +{ + eTSEC *etsec = ETSEC_COMMON(d); + int i = 0; + int reg_index = 0; + + /* Default value for all registers */ + for (i = 0; i < ETSEC_REG_NUMBER; i++) { + etsec->regs[i].name = "Reserved"; + etsec->regs[i].desc = ""; + etsec->regs[i].access = ACC_UNKNOWN; + etsec->regs[i].value = 0x00000000; + } + + /* Set-up known registers */ + for (i = 0; eTSEC_registers_def[i].name != NULL; i++) { + + reg_index = eTSEC_registers_def[i].offset / 4; + + etsec->regs[reg_index].name = eTSEC_registers_def[i].name; + etsec->regs[reg_index].desc = eTSEC_registers_def[i].desc; + etsec->regs[reg_index].access = eTSEC_registers_def[i].access; + etsec->regs[reg_index].value = eTSEC_registers_def[i].reset; + } + + etsec->tx_buffer = NULL; + etsec->tx_buffer_len = 0; + etsec->rx_buffer = NULL; + etsec->rx_buffer_len = 0; + + etsec->phy_status = + MII_SR_EXTENDED_CAPS | MII_SR_LINK_STATUS | MII_SR_AUTONEG_CAPS | + MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS | + MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS | + MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS | + MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; +} + +static void etsec_cleanup(NetClientState *nc) +{ + /* qemu_log("eTSEC cleanup\n"); */ +} + +static int etsec_can_receive(NetClientState *nc) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + + return etsec->rx_buffer_len == 0; +} + +static ssize_t etsec_receive(NetClientState *nc, + const uint8_t *buf, + size_t size) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + +#if defined(HEX_DUMP) + fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size); + qemu_hexdump(buf, stderr, "", size); +#endif + etsec_rx_ring_write(etsec, buf, size); + return size; +} + + +static void etsec_set_link_status(NetClientState *nc) +{ + eTSEC *etsec = qemu_get_nic_opaque(nc); + + etsec_miim_link_status(etsec, nc); +} + +static NetClientInfo net_etsec_info = { + .type = NET_CLIENT_OPTIONS_KIND_NIC, + .size = sizeof(NICState), + .can_receive = etsec_can_receive, + .receive = etsec_receive, + .cleanup = etsec_cleanup, + .link_status_changed = etsec_set_link_status, +}; + +static void etsec_realize(DeviceState *dev, Error **errp) +{ + eTSEC *etsec = ETSEC_COMMON(dev); + + etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf, + object_get_typename(OBJECT(dev)), dev->id, etsec); + qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a); + + + etsec->bh = qemu_bh_new(etsec_timer_hit, etsec); + etsec->ptimer = ptimer_init(etsec->bh); + ptimer_set_freq(etsec->ptimer, 100); +} + +static void etsec_instance_init(Object *obj) +{ + eTSEC *etsec = ETSEC_COMMON(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec, + "eTSEC", 0x1000); + sysbus_init_mmio(sbd, &etsec->io_area); + + sysbus_init_irq(sbd, &etsec->tx_irq); + sysbus_init_irq(sbd, &etsec->rx_irq); + sysbus_init_irq(sbd, &etsec->err_irq); +} + +static Property etsec_properties[] = { + DEFINE_NIC_PROPERTIES(eTSEC, conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void etsec_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = etsec_realize; + dc->reset = etsec_reset; + dc->props = etsec_properties; +} + +static TypeInfo etsec_info = { + .name = "eTSEC", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(eTSEC), + .class_init = etsec_class_init, + .instance_init = etsec_instance_init, +}; + +static void etsec_register_types(void) +{ + type_register_static(&etsec_info); +} + +type_init(etsec_register_types) + +DeviceState *etsec_create(hwaddr base, + MemoryRegion * mr, + NICInfo * nd, + qemu_irq tx_irq, + qemu_irq rx_irq, + qemu_irq err_irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "eTSEC"); + qdev_set_nic_properties(dev, nd); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq); + + memory_region_add_subregion(mr, base, + SYS_BUS_DEVICE(dev)->mmio[0].memory); + + return dev; +} diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h new file mode 100644 index 0000000000..78d2c57ed3 --- /dev/null +++ b/hw/net/fsl_etsec/etsec.h @@ -0,0 +1,174 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _ETSEC_H_ +#define _ETSEC_H_ + +#include "hw/qdev.h" +#include "hw/sysbus.h" +#include "net/net.h" +#include "hw/ptimer.h" + +/* Buffer Descriptors */ + +typedef struct eTSEC_rxtx_bd { + uint16_t flags; + uint16_t length; + uint32_t bufptr; +} eTSEC_rxtx_bd; + +#define BD_WRAP (1 << 13) +#define BD_INTERRUPT (1 << 12) +#define BD_LAST (1 << 11) + +#define BD_TX_READY (1 << 15) +#define BD_TX_PADCRC (1 << 14) +#define BD_TX_TC (1 << 10) +#define BD_TX_PREDEF (1 << 9) +#define BD_TX_HFELC (1 << 7) +#define BD_TX_CFRL (1 << 6) +#define BD_TX_RC_MASK 0xF +#define BD_TX_RC_OFFSET 0x2 +#define BD_TX_TOEUN (1 << 1) +#define BD_TX_TR (1 << 0) + +#define BD_RX_EMPTY (1 << 15) +#define BD_RX_RO1 (1 << 14) +#define BD_RX_FIRST (1 << 10) +#define BD_RX_MISS (1 << 8) +#define BD_RX_BROADCAST (1 << 7) +#define BD_RX_MULTICAST (1 << 6) +#define BD_RX_LG (1 << 5) +#define BD_RX_NO (1 << 4) +#define BD_RX_SH (1 << 3) +#define BD_RX_CR (1 << 2) +#define BD_RX_OV (1 << 1) +#define BD_RX_TR (1 << 0) + +/* Tx FCB flags */ +#define FCB_TX_VLN (1 << 7) +#define FCB_TX_IP (1 << 6) +#define FCB_TX_IP6 (1 << 5) +#define FCB_TX_TUP (1 << 4) +#define FCB_TX_UDP (1 << 3) +#define FCB_TX_CIP (1 << 2) +#define FCB_TX_CTU (1 << 1) +#define FCB_TX_NPH (1 << 0) + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* eTSEC */ + +/* Number of register in the device */ +#define ETSEC_REG_NUMBER 1024 + +typedef struct eTSEC_Register { + const char *name; + const char *desc; + uint32_t access; + uint32_t value; +} eTSEC_Register; + +typedef struct eTSEC { + SysBusDevice busdev; + + MemoryRegion io_area; + + eTSEC_Register regs[ETSEC_REG_NUMBER]; + + NICState *nic; + NICConf conf; + + /* Tx */ + + uint8_t *tx_buffer; + uint32_t tx_buffer_len; + eTSEC_rxtx_bd first_bd; + + /* Rx */ + + uint8_t *rx_buffer; + uint32_t rx_buffer_len; + uint32_t rx_remaining_data; + uint8_t rx_first_in_frame; + uint8_t rx_fcb_size; + eTSEC_rxtx_bd rx_first_bd; + uint8_t rx_fcb[10]; + uint32_t rx_padding; + + /* IRQs */ + qemu_irq tx_irq; + qemu_irq rx_irq; + qemu_irq err_irq; + + + uint16_t phy_status; + uint16_t phy_control; + + /* Polling */ + QEMUBH *bh; + struct ptimer_state *ptimer; + +} eTSEC; + +#define TYPE_ETSEC_COMMON "eTSEC" +#define ETSEC_COMMON(obj) \ + OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON) + +#define eTSEC_TRANSMIT 1 +#define eTSEC_RECEIVE 2 + +DeviceState *etsec_create(hwaddr base, + MemoryRegion *mr, + NICInfo *nd, + qemu_irq tx_irq, + qemu_irq rx_irq, + qemu_irq err_irq); + +void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr); +void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr); +void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size); + +void etsec_write_miim(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value); + +void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc); + +#endif /* ! _ETSEC_H_ */ diff --git a/hw/net/fsl_etsec/miim.c b/hw/net/fsl_etsec/miim.c new file mode 100644 index 0000000000..1931b74e6c --- /dev/null +++ b/hw/net/fsl_etsec/miim.c @@ -0,0 +1,146 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "etsec.h" +#include "registers.h" + +/* #define DEBUG_MIIM */ + +#define MIIM_CONTROL 0 +#define MIIM_STATUS 1 +#define MIIM_PHY_ID_1 2 +#define MIIM_PHY_ID_2 3 +#define MIIM_T2_STATUS 10 +#define MIIM_EXT_STATUS 15 + +static void miim_read_cycle(eTSEC *etsec) +{ + uint8_t phy; + uint8_t addr; + uint16_t value; + + phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F; + (void)phy; /* Unreferenced */ + addr = etsec->regs[MIIMADD].value & 0x1F; + + switch (addr) { + case MIIM_CONTROL: + value = etsec->phy_control; + break; + case MIIM_STATUS: + value = etsec->phy_status; + break; + case MIIM_T2_STATUS: + value = 0x1800; /* Local and remote receivers OK */ + break; + default: + value = 0x0; + break; + }; + +#ifdef DEBUG_MIIM + qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); +#endif + + etsec->regs[MIIMSTAT].value = value; +} + +static void miim_write_cycle(eTSEC *etsec) +{ + uint8_t phy; + uint8_t addr; + uint16_t value; + + phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F; + (void)phy; /* Unreferenced */ + addr = etsec->regs[MIIMADD].value & 0x1F; + value = etsec->regs[MIIMCON].value & 0xffff; + +#ifdef DEBUG_MIIM + qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value); +#endif + + switch (addr) { + case MIIM_CONTROL: + etsec->phy_control = value & ~(0x8100); + break; + default: + break; + }; +} + +void etsec_write_miim(eTSEC *etsec, + eTSEC_Register *reg, + uint32_t reg_index, + uint32_t value) +{ + + switch (reg_index) { + + case MIIMCOM: + /* Read and scan cycle */ + + if ((!(reg->value & MIIMCOM_READ)) && (value & MIIMCOM_READ)) { + /* Read */ + miim_read_cycle(etsec); + } + reg->value = value; + break; + + case MIIMCON: + reg->value = value & 0xffff; + miim_write_cycle(etsec); + break; + + default: + /* Default handling */ + switch (reg->access) { + + case ACC_RW: + case ACC_WO: + reg->value = value; + break; + + case ACC_W1C: + reg->value &= ~value; + break; + + case ACC_RO: + default: + /* Read Only or Unknown register */ + break; + } + } + +} + +void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc) +{ + /* Set link status */ + if (nc->link_down) { + etsec->phy_status &= ~MII_SR_LINK_STATUS; + } else { + etsec->phy_status |= MII_SR_LINK_STATUS; + } +} diff --git a/hw/net/fsl_etsec/registers.c b/hw/net/fsl_etsec/registers.c new file mode 100644 index 0000000000..a7bbfa113f --- /dev/null +++ b/hw/net/fsl_etsec/registers.c @@ -0,0 +1,295 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "registers.h" + +const eTSEC_Register_Definition eTSEC_registers_def[] = { +{0x000, "TSEC_ID", "Controller ID register", ACC_RO, 0x01240000}, +{0x004, "TSEC_ID2", "Controller ID register 2", ACC_RO, 0x003000F0}, +{0x010, "IEVENT", "Interrupt event register", ACC_W1C, 0x00000000}, +{0x014, "IMASK", "Interrupt mask register", ACC_RW, 0x00000000}, +{0x018, "EDIS", "Error disabled register", ACC_RW, 0x00000000}, +{0x020, "ECNTRL", "Ethernet control register", ACC_RW, 0x00000040}, +{0x028, "PTV", "Pause time value register", ACC_RW, 0x00000000}, +{0x02C, "DMACTRL", "DMA control register", ACC_RW, 0x00000000}, +{0x030, "TBIPA", "TBI PHY address register", ACC_RW, 0x00000000}, + +/* eTSEC FIFO Control and Status Registers */ + +{0x058, "FIFO_RX_ALARM", "FIFO receive alarm start threshold register", ACC_RW, 0x00000040}, +{0x05C, "FIFO_RX_ALARM_SHUTOFF", "FIFO receive alarm shut-off threshold register", ACC_RW, 0x00000080}, +{0x08C, "FIFO_TX_THR", "FIFO transmit threshold register", ACC_RW, 0x00000080}, +{0x098, "FIFO_TX_STARVE", "FIFO transmit starve register", ACC_RW, 0x00000040}, +{0x09C, "FIFO_TX_STARVE_SHUTOFF", "FIFO transmit starve shut-off register", ACC_RW, 0x00000080}, + +/* eTSEC Transmit Control and Status Registers */ + +{0x100, "TCTRL", "Transmit control register", ACC_RW, 0x00000000}, +{0x104, "TSTAT", "Transmit status register", ACC_W1C, 0x00000000}, +{0x108, "DFVLAN", "Default VLAN control word", ACC_RW, 0x81000000}, +{0x110, "TXIC", "Transmit interrupt coalescing register", ACC_RW, 0x00000000}, +{0x114, "TQUEUE", "Transmit queue control register", ACC_RW, 0x00008000}, +{0x140, "TR03WT", "TxBD Rings 0-3 round-robin weightings", ACC_RW, 0x00000000}, +{0x144, "TR47WT", "TxBD Rings 4-7 round-robin weightings", ACC_RW, 0x00000000}, +{0x180, "TBDBPH", "Tx data buffer pointer high bits", ACC_RW, 0x00000000}, +{0x184, "TBPTR0", "TxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0x18C, "TBPTR1", "TxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0x194, "TBPTR2", "TxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0x19C, "TBPTR3", "TxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0x1A4, "TBPTR4", "TxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0x1AC, "TBPTR5", "TxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0x1B4, "TBPTR6", "TxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0x1BC, "TBPTR7", "TxBD pointer for ring 7", ACC_RW, 0x00000000}, +{0x200, "TBASEH", "TxBD base address high bits", ACC_RW, 0x00000000}, +{0x204, "TBASE0", "TxBD base address of ring 0", ACC_RW, 0x00000000}, +{0x20C, "TBASE1", "TxBD base address of ring 1", ACC_RW, 0x00000000}, +{0x214, "TBASE2", "TxBD base address of ring 2", ACC_RW, 0x00000000}, +{0x21C, "TBASE3", "TxBD base address of ring 3", ACC_RW, 0x00000000}, +{0x224, "TBASE4", "TxBD base address of ring 4", ACC_RW, 0x00000000}, +{0x22C, "TBASE5", "TxBD base address of ring 5", ACC_RW, 0x00000000}, +{0x234, "TBASE6", "TxBD base address of ring 6", ACC_RW, 0x00000000}, +{0x23C, "TBASE7", "TxBD base address of ring 7", ACC_RW, 0x00000000}, +{0x280, "TMR_TXTS1_ID", "Tx time stamp identification tag (set 1)", ACC_RO, 0x00000000}, +{0x284, "TMR_TXTS2_ID", "Tx time stamp identification tag (set 2)", ACC_RO, 0x00000000}, +{0x2C0, "TMR_TXTS1_H", "Tx time stamp high (set 1)", ACC_RO, 0x00000000}, +{0x2C4, "TMR_TXTS1_L", "Tx time stamp high (set 1)", ACC_RO, 0x00000000}, +{0x2C8, "TMR_TXTS2_H", "Tx time stamp high (set 2)", ACC_RO, 0x00000000}, +{0x2CC, "TMR_TXTS2_L", "Tx time stamp high (set 2)", ACC_RO, 0x00000000}, + +/* eTSEC Receive Control and Status Registers */ + +{0x300, "RCTRL", "Receive control register", ACC_RW, 0x00000000}, +{0x304, "RSTAT", "Receive status register", ACC_W1C, 0x00000000}, +{0x310, "RXIC", "Receive interrupt coalescing register", ACC_RW, 0x00000000}, +{0x314, "RQUEUE", "Receive queue control register.", ACC_RW, 0x00800080}, +{0x330, "RBIFX", "Receive bit field extract control register", ACC_RW, 0x00000000}, +{0x334, "RQFAR", "Receive queue filing table address register", ACC_RW, 0x00000000}, +{0x338, "RQFCR", "Receive queue filing table control register", ACC_RW, 0x00000000}, +{0x33C, "RQFPR", "Receive queue filing table property register", ACC_RW, 0x00000000}, +{0x340, "MRBLR", "Maximum receive buffer length register", ACC_RW, 0x00000000}, +{0x380, "RBDBPH", "Rx data buffer pointer high bits", ACC_RW, 0x00000000}, +{0x384, "RBPTR0", "RxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0x38C, "RBPTR1", "RxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0x394, "RBPTR2", "RxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0x39C, "RBPTR3", "RxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0x3A4, "RBPTR4", "RxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0x3AC, "RBPTR5", "RxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0x3B4, "RBPTR6", "RxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0x3BC, "RBPTR7", "RxBD pointer for ring 7", ACC_RW, 0x00000000}, +{0x400, "RBASEH", "RxBD base address high bits", ACC_RW, 0x00000000}, +{0x404, "RBASE0", "RxBD base address of ring 0", ACC_RW, 0x00000000}, +{0x40C, "RBASE1", "RxBD base address of ring 1", ACC_RW, 0x00000000}, +{0x414, "RBASE2", "RxBD base address of ring 2", ACC_RW, 0x00000000}, +{0x41C, "RBASE3", "RxBD base address of ring 3", ACC_RW, 0x00000000}, +{0x424, "RBASE4", "RxBD base address of ring 4", ACC_RW, 0x00000000}, +{0x42C, "RBASE5", "RxBD base address of ring 5", ACC_RW, 0x00000000}, +{0x434, "RBASE6", "RxBD base address of ring 6", ACC_RW, 0x00000000}, +{0x43C, "RBASE7", "RxBD base address of ring 7", ACC_RW, 0x00000000}, +{0x4C0, "TMR_RXTS_H", "Rx timer time stamp register high", ACC_RW, 0x00000000}, +{0x4C4, "TMR_RXTS_L", "Rx timer time stamp register low", ACC_RW, 0x00000000}, + +/* eTSEC MAC Registers */ + +{0x500, "MACCFG1", "MAC configuration register 1", ACC_RW, 0x00000000}, +{0x504, "MACCFG2", "MAC configuration register 2", ACC_RW, 0x00007000}, +{0x508, "IPGIFG", "Inter-packet/inter-frame gap register", ACC_RW, 0x40605060}, +{0x50C, "HAFDUP", "Half-duplex control", ACC_RW, 0x00A1F037}, +{0x510, "MAXFRM", "Maximum frame length", ACC_RW, 0x00000600}, +{0x520, "MIIMCFG", "MII management configuration", ACC_RW, 0x00000007}, +{0x524, "MIIMCOM", "MII management command", ACC_RW, 0x00000000}, +{0x528, "MIIMADD", "MII management address", ACC_RW, 0x00000000}, +{0x52C, "MIIMCON", "MII management control", ACC_WO, 0x00000000}, +{0x530, "MIIMSTAT", "MII management status", ACC_RO, 0x00000000}, +{0x534, "MIIMIND", "MII management indicator", ACC_RO, 0x00000000}, +{0x53C, "IFSTAT", "Interface status", ACC_RO, 0x00000000}, +{0x540, "MACSTNADDR1", "MAC station address register 1", ACC_RW, 0x00000000}, +{0x544, "MACSTNADDR2", "MAC station address register 2", ACC_RW, 0x00000000}, +{0x548, "MAC01ADDR1", "MAC exact match address 1, part 1", ACC_RW, 0x00000000}, +{0x54C, "MAC01ADDR2", "MAC exact match address 1, part 2", ACC_RW, 0x00000000}, +{0x550, "MAC02ADDR1", "MAC exact match address 2, part 1", ACC_RW, 0x00000000}, +{0x554, "MAC02ADDR2", "MAC exact match address 2, part 2", ACC_RW, 0x00000000}, +{0x558, "MAC03ADDR1", "MAC exact match address 3, part 1", ACC_RW, 0x00000000}, +{0x55C, "MAC03ADDR2", "MAC exact match address 3, part 2", ACC_RW, 0x00000000}, +{0x560, "MAC04ADDR1", "MAC exact match address 4, part 1", ACC_RW, 0x00000000}, +{0x564, "MAC04ADDR2", "MAC exact match address 4, part 2", ACC_RW, 0x00000000}, +{0x568, "MAC05ADDR1", "MAC exact match address 5, part 1", ACC_RW, 0x00000000}, +{0x56C, "MAC05ADDR2", "MAC exact match address 5, part 2", ACC_RW, 0x00000000}, +{0x570, "MAC06ADDR1", "MAC exact match address 6, part 1", ACC_RW, 0x00000000}, +{0x574, "MAC06ADDR2", "MAC exact match address 6, part 2", ACC_RW, 0x00000000}, +{0x578, "MAC07ADDR1", "MAC exact match address 7, part 1", ACC_RW, 0x00000000}, +{0x57C, "MAC07ADDR2", "MAC exact match address 7, part 2", ACC_RW, 0x00000000}, +{0x580, "MAC08ADDR1", "MAC exact match address 8, part 1", ACC_RW, 0x00000000}, +{0x584, "MAC08ADDR2", "MAC exact match address 8, part 2", ACC_RW, 0x00000000}, +{0x588, "MAC09ADDR1", "MAC exact match address 9, part 1", ACC_RW, 0x00000000}, +{0x58C, "MAC09ADDR2", "MAC exact match address 9, part 2", ACC_RW, 0x00000000}, +{0x590, "MAC10ADDR1", "MAC exact match address 10, part 1", ACC_RW, 0x00000000}, +{0x594, "MAC10ADDR2", "MAC exact match address 10, part 2", ACC_RW, 0x00000000}, +{0x598, "MAC11ADDR1", "MAC exact match address 11, part 1", ACC_RW, 0x00000000}, +{0x59C, "MAC11ADDR2", "MAC exact match address 11, part 2", ACC_RW, 0x00000000}, +{0x5A0, "MAC12ADDR1", "MAC exact match address 12, part 1", ACC_RW, 0x00000000}, +{0x5A4, "MAC12ADDR2", "MAC exact match address 12, part 2", ACC_RW, 0x00000000}, +{0x5A8, "MAC13ADDR1", "MAC exact match address 13, part 1", ACC_RW, 0x00000000}, +{0x5AC, "MAC13ADDR2", "MAC exact match address 13, part 2", ACC_RW, 0x00000000}, +{0x5B0, "MAC14ADDR1", "MAC exact match address 14, part 1", ACC_RW, 0x00000000}, +{0x5B4, "MAC14ADDR2", "MAC exact match address 14, part 2", ACC_RW, 0x00000000}, +{0x5B8, "MAC15ADDR1", "MAC exact match address 15, part 1", ACC_RW, 0x00000000}, +{0x5BC, "MAC15ADDR2", "MAC exact match address 15, part 2", ACC_RW, 0x00000000}, + +/* eTSEC, "Transmit", "and", Receive, Counters */ + +{0x680, "TR64", "Transmit and receive 64-byte frame counter ", ACC_RW, 0x00000000}, +{0x684, "TR127", "Transmit and receive 65- to 127-byte frame counter", ACC_RW, 0x00000000}, +{0x688, "TR255", "Transmit and receive 128- to 255-byte frame counter", ACC_RW, 0x00000000}, +{0x68C, "TR511", "Transmit and receive 256- to 511-byte frame counter", ACC_RW, 0x00000000}, +{0x690, "TR1K", "Transmit and receive 512- to 1023-byte frame counter", ACC_RW, 0x00000000}, +{0x694, "TRMAX", "Transmit and receive 1024- to 1518-byte frame counter", ACC_RW, 0x00000000}, +{0x698, "TRMGV", "Transmit and receive 1519- to 1522-byte good VLAN frame count", ACC_RW, 0x00000000}, + +/* eTSEC Receive Counters */ + +{0x69C, "RBYT", "Receive byte counter", ACC_RW, 0x00000000}, +{0x6A0, "RPKT", "Receive packet counter", ACC_RW, 0x00000000}, +{0x6A4, "RFCS", "Receive FCS error counter", ACC_RW, 0x00000000}, +{0x6A8, "RMCA", "Receive multicast packet counter", ACC_RW, 0x00000000}, +{0x6AC, "RBCA", "Receive broadcast packet counter", ACC_RW, 0x00000000}, +{0x6B0, "RXCF", "Receive control frame packet counter ", ACC_RW, 0x00000000}, +{0x6B4, "RXPF", "Receive PAUSE frame packet counter", ACC_RW, 0x00000000}, +{0x6B8, "RXUO", "Receive unknown OP code counter ", ACC_RW, 0x00000000}, +{0x6BC, "RALN", "Receive alignment error counter ", ACC_RW, 0x00000000}, +{0x6C0, "RFLR", "Receive frame length error counter ", ACC_RW, 0x00000000}, +{0x6C4, "RCDE", "Receive code error counter ", ACC_RW, 0x00000000}, +{0x6C8, "RCSE", "Receive carrier sense error counter", ACC_RW, 0x00000000}, +{0x6CC, "RUND", "Receive undersize packet counter", ACC_RW, 0x00000000}, +{0x6D0, "ROVR", "Receive oversize packet counter ", ACC_RW, 0x00000000}, +{0x6D4, "RFRG", "Receive fragments counter", ACC_RW, 0x00000000}, +{0x6D8, "RJBR", "Receive jabber counter ", ACC_RW, 0x00000000}, +{0x6DC, "RDRP", "Receive drop counter", ACC_RW, 0x00000000}, + +/* eTSEC Transmit Counters */ + +{0x6E0, "TBYT", "Transmit byte counter", ACC_RW, 0x00000000}, +{0x6E4, "TPKT", "Transmit packet counter", ACC_RW, 0x00000000}, +{0x6E8, "TMCA", "Transmit multicast packet counter ", ACC_RW, 0x00000000}, +{0x6EC, "TBCA", "Transmit broadcast packet counter ", ACC_RW, 0x00000000}, +{0x6F0, "TXPF", "Transmit PAUSE control frame counter ", ACC_RW, 0x00000000}, +{0x6F4, "TDFR", "Transmit deferral packet counter ", ACC_RW, 0x00000000}, +{0x6F8, "TEDF", "Transmit excessive deferral packet counter ", ACC_RW, 0x00000000}, +{0x6FC, "TSCL", "Transmit single collision packet counter", ACC_RW, 0x00000000}, +{0x700, "TMCL", "Transmit multiple collision packet counter", ACC_RW, 0x00000000}, +{0x704, "TLCL", "Transmit late collision packet counter", ACC_RW, 0x00000000}, +{0x708, "TXCL", "Transmit excessive collision packet counter", ACC_RW, 0x00000000}, +{0x70C, "TNCL", "Transmit total collision counter ", ACC_RW, 0x00000000}, +{0x714, "TDRP", "Transmit drop frame counter", ACC_RW, 0x00000000}, +{0x718, "TJBR", "Transmit jabber frame counter ", ACC_RW, 0x00000000}, +{0x71C, "TFCS", "Transmit FCS error counter", ACC_RW, 0x00000000}, +{0x720, "TXCF", "Transmit control frame counter ", ACC_RW, 0x00000000}, +{0x724, "TOVR", "Transmit oversize frame counter", ACC_RW, 0x00000000}, +{0x728, "TUND", "Transmit undersize frame counter ", ACC_RW, 0x00000000}, +{0x72C, "TFRG", "Transmit fragments frame counter ", ACC_RW, 0x00000000}, + +/* eTSEC Counter Control and TOE Statistics Registers */ + +{0x730, "CAR1", "Carry register one register", ACC_W1C, 0x00000000}, +{0x734, "CAR2", "Carry register two register ", ACC_W1C, 0x00000000}, +{0x738, "CAM1", "Carry register one mask register ", ACC_RW, 0xFE03FFFF}, +{0x73C, "CAM2", "Carry register two mask register ", ACC_RW, 0x000FFFFD}, +{0x740, "RREJ", "Receive filer rejected packet counter", ACC_RW, 0x00000000}, + +/* Hash Function Registers */ + +{0x800, "IGADDR0", "Individual/group address register 0", ACC_RW, 0x00000000}, +{0x804, "IGADDR1", "Individual/group address register 1", ACC_RW, 0x00000000}, +{0x808, "IGADDR2", "Individual/group address register 2", ACC_RW, 0x00000000}, +{0x80C, "IGADDR3", "Individual/group address register 3", ACC_RW, 0x00000000}, +{0x810, "IGADDR4", "Individual/group address register 4", ACC_RW, 0x00000000}, +{0x814, "IGADDR5", "Individual/group address register 5", ACC_RW, 0x00000000}, +{0x818, "IGADDR6", "Individual/group address register 6", ACC_RW, 0x00000000}, +{0x81C, "IGADDR7", "Individual/group address register 7", ACC_RW, 0x00000000}, +{0x880, "GADDR0", "Group address register 0", ACC_RW, 0x00000000}, +{0x884, "GADDR1", "Group address register 1", ACC_RW, 0x00000000}, +{0x888, "GADDR2", "Group address register 2", ACC_RW, 0x00000000}, +{0x88C, "GADDR3", "Group address register 3", ACC_RW, 0x00000000}, +{0x890, "GADDR4", "Group address register 4", ACC_RW, 0x00000000}, +{0x894, "GADDR5", "Group address register 5", ACC_RW, 0x00000000}, +{0x898, "GADDR6", "Group address register 6", ACC_RW, 0x00000000}, +{0x89C, "GADDR7", "Group address register 7", ACC_RW, 0x00000000}, + +/* eTSEC DMA Attribute Registers */ + +{0xBF8, "ATTR", "Attribute register", ACC_RW, 0x00000000}, +{0xBFC, "ATTRELI", "Attribute extract length and extract index register", ACC_RW, 0x00000000}, + + +/* eTSEC Lossless Flow Control Registers */ + +{0xC00, "RQPRM0", "Receive Queue Parameters register 0 ", ACC_RW, 0x00000000}, +{0xC04, "RQPRM1", "Receive Queue Parameters register 1 ", ACC_RW, 0x00000000}, +{0xC08, "RQPRM2", "Receive Queue Parameters register 2 ", ACC_RW, 0x00000000}, +{0xC0C, "RQPRM3", "Receive Queue Parameters register 3 ", ACC_RW, 0x00000000}, +{0xC10, "RQPRM4", "Receive Queue Parameters register 4 ", ACC_RW, 0x00000000}, +{0xC14, "RQPRM5", "Receive Queue Parameters register 5 ", ACC_RW, 0x00000000}, +{0xC18, "RQPRM6", "Receive Queue Parameters register 6 ", ACC_RW, 0x00000000}, +{0xC1C, "RQPRM7", "Receive Queue Parameters register 7 ", ACC_RW, 0x00000000}, +{0xC44, "RFBPTR0", "Last Free RxBD pointer for ring 0", ACC_RW, 0x00000000}, +{0xC4C, "RFBPTR1", "Last Free RxBD pointer for ring 1", ACC_RW, 0x00000000}, +{0xC54, "RFBPTR2", "Last Free RxBD pointer for ring 2", ACC_RW, 0x00000000}, +{0xC5C, "RFBPTR3", "Last Free RxBD pointer for ring 3", ACC_RW, 0x00000000}, +{0xC64, "RFBPTR4", "Last Free RxBD pointer for ring 4", ACC_RW, 0x00000000}, +{0xC6C, "RFBPTR5", "Last Free RxBD pointer for ring 5", ACC_RW, 0x00000000}, +{0xC74, "RFBPTR6", "Last Free RxBD pointer for ring 6", ACC_RW, 0x00000000}, +{0xC7C, "RFBPTR7", "Last Free RxBD pointer for ring 7", ACC_RW, 0x00000000}, + +/* eTSEC Future Expansion Space */ + +/* Reserved*/ + +/* eTSEC IEEE 1588 Registers */ + +{0xE00, "TMR_CTRL", "Timer control register", ACC_RW, 0x00010001}, +{0xE04, "TMR_TEVENT", "time stamp event register", ACC_W1C, 0x00000000}, +{0xE08, "TMR_TEMASK", "Timer event mask register", ACC_RW, 0x00000000}, +{0xE0C, "TMR_PEVENT", "time stamp event register", ACC_RW, 0x00000000}, +{0xE10, "TMR_PEMASK", "Timer event mask register", ACC_RW, 0x00000000}, +{0xE14, "TMR_STAT", "time stamp status register", ACC_RW, 0x00000000}, +{0xE18, "TMR_CNT_H", "timer counter high register", ACC_RW, 0x00000000}, +{0xE1C, "TMR_CNT_L", "timer counter low register", ACC_RW, 0x00000000}, +{0xE20, "TMR_ADD", "Timer drift compensation addend register", ACC_RW, 0x00000000}, +{0xE24, "TMR_ACC", "Timer accumulator register", ACC_RW, 0x00000000}, +{0xE28, "TMR_PRSC", "Timer prescale", ACC_RW, 0x00000002}, +{0xE30, "TMROFF_H", "Timer offset high", ACC_RW, 0x00000000}, +{0xE34, "TMROFF_L", "Timer offset low", ACC_RW, 0x00000000}, +{0xE40, "TMR_ALARM1_H", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF}, +{0xE44, "TMR_ALARM1_L", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF}, +{0xE48, "TMR_ALARM2_H", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF}, +{0xE4C, "TMR_ALARM2_L", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF}, +{0xE80, "TMR_FIPER1", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xE84, "TMR_FIPER2", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xE88, "TMR_FIPER3", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF}, +{0xEA0, "TMR_ETTS1_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000}, +{0xEA4, "TMR_ETTS1_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000}, +{0xEA8, "TMR_ETTS2_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000}, +{0xEAC, "TMR_ETTS2_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000}, + +/* End Of Table */ +{0x0, 0x0, 0x0, 0x0, 0x0} +}; diff --git a/hw/net/fsl_etsec/registers.h b/hw/net/fsl_etsec/registers.h new file mode 100644 index 0000000000..7ad7686470 --- /dev/null +++ b/hw/net/fsl_etsec/registers.h @@ -0,0 +1,320 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _ETSEC_REGISTERS_H_ +#define _ETSEC_REGISTERS_H_ + +#include <stdint.h> + +enum eTSEC_Register_Access_Type { + ACC_RW = 1, /* Read/Write */ + ACC_RO = 2, /* Read Only */ + ACC_WO = 3, /* Write Only */ + ACC_W1C = 4, /* Write 1 to clear */ + ACC_UNKNOWN = 5 /* Unknown register*/ +}; + +typedef struct eTSEC_Register_Definition { + uint32_t offset; + const char *name; + const char *desc; + enum eTSEC_Register_Access_Type access; + uint32_t reset; +} eTSEC_Register_Definition; + +extern const eTSEC_Register_Definition eTSEC_registers_def[]; + +#define DMACTRL_LE (1 << 15) +#define DMACTRL_GRS (1 << 4) +#define DMACTRL_GTS (1 << 3) +#define DMACTRL_WOP (1 << 0) + +#define IEVENT_PERR (1 << 0) +#define IEVENT_DPE (1 << 1) +#define IEVENT_FIQ (1 << 2) +#define IEVENT_FIR (1 << 3) +#define IEVENT_FGPI (1 << 4) +#define IEVENT_RXF (1 << 7) +#define IEVENT_GRSC (1 << 8) +#define IEVENT_MMRW (1 << 9) +#define IEVENT_MMRD (1 << 10) +#define IEVENT_MAG (1 << 11) +#define IEVENT_RXB (1 << 15) +#define IEVENT_XFUN (1 << 16) +#define IEVENT_CRL (1 << 17) +#define IEVENT_LC (1 << 18) +#define IEVENT_TXF (1 << 20) +#define IEVENT_TXB (1 << 21) +#define IEVENT_TXE (1 << 22) +#define IEVENT_TXC (1 << 23) +#define IEVENT_BABT (1 << 24) +#define IEVENT_GTSC (1 << 25) +#define IEVENT_MSRO (1 << 26) +#define IEVENT_EBERR (1 << 28) +#define IEVENT_BSY (1 << 29) +#define IEVENT_RXC (1 << 30) +#define IEVENT_BABR (1 << 31) + +#define IMASK_RXFEN (1 << 7) +#define IMASK_GRSCEN (1 << 8) +#define IMASK_RXBEN (1 << 15) +#define IMASK_TXFEN (1 << 20) +#define IMASK_TXBEN (1 << 21) +#define IMASK_GTSCEN (1 << 25) + +#define MACCFG1_TX_EN (1 << 0) +#define MACCFG1_RX_EN (1 << 2) + +#define MACCFG2_CRC_EN (1 << 1) +#define MACCFG2_PADCRC (1 << 2) + +#define MIIMCOM_READ (1 << 0) +#define MIIMCOM_SCAN (1 << 1) + +#define RCTRL_PRSDEP_MASK (0x3) +#define RCTRL_PRSDEP_OFFSET (6) +#define RCTRL_RSF (1 << 2) + +/* Index of each register */ + +#define TSEC_ID (0x000 / 4) +#define TSEC_ID2 (0x004 / 4) +#define IEVENT (0x010 / 4) +#define IMASK (0x014 / 4) +#define EDIS (0x018 / 4) +#define ECNTRL (0x020 / 4) +#define PTV (0x028 / 4) +#define DMACTRL (0x02C / 4) +#define TBIPA (0x030 / 4) +#define TCTRL (0x100 / 4) +#define TSTAT (0x104 / 4) +#define DFVLAN (0x108 / 4) +#define TXIC (0x110 / 4) +#define TQUEUE (0x114 / 4) +#define TR03WT (0x140 / 4) +#define TR47WT (0x144 / 4) +#define TBDBPH (0x180 / 4) +#define TBPTR0 (0x184 / 4) +#define TBPTR1 (0x18C / 4) +#define TBPTR2 (0x194 / 4) +#define TBPTR3 (0x19C / 4) +#define TBPTR4 (0x1A4 / 4) +#define TBPTR5 (0x1AC / 4) +#define TBPTR6 (0x1B4 / 4) +#define TBPTR7 (0x1BC / 4) +#define TBASEH (0x200 / 4) +#define TBASE0 (0x204 / 4) +#define TBASE1 (0x20C / 4) +#define TBASE2 (0x214 / 4) +#define TBASE3 (0x21C / 4) +#define TBASE4 (0x224 / 4) +#define TBASE5 (0x22C / 4) +#define TBASE6 (0x234 / 4) +#define TBASE7 (0x23C / 4) +#define TMR_TXTS1_ID (0x280 / 4) +#define TMR_TXTS2_ID (0x284 / 4) +#define TMR_TXTS1_H (0x2C0 / 4) +#define TMR_TXTS1_L (0x2C4 / 4) +#define TMR_TXTS2_H (0x2C8 / 4) +#define TMR_TXTS2_L (0x2CC / 4) +#define RCTRL (0x300 / 4) +#define RSTAT (0x304 / 4) +#define RXIC (0x310 / 4) +#define RQUEUE (0x314 / 4) +#define RBIFX (0x330 / 4) +#define RQFAR (0x334 / 4) +#define RQFCR (0x338 / 4) +#define RQFPR (0x33C / 4) +#define MRBLR (0x340 / 4) +#define RBDBPH (0x380 / 4) +#define RBPTR0 (0x384 / 4) +#define RBPTR1 (0x38C / 4) +#define RBPTR2 (0x394 / 4) +#define RBPTR3 (0x39C / 4) +#define RBPTR4 (0x3A4 / 4) +#define RBPTR5 (0x3AC / 4) +#define RBPTR6 (0x3B4 / 4) +#define RBPTR7 (0x3BC / 4) +#define RBASEH (0x400 / 4) +#define RBASE0 (0x404 / 4) +#define RBASE1 (0x40C / 4) +#define RBASE2 (0x414 / 4) +#define RBASE3 (0x41C / 4) +#define RBASE4 (0x424 / 4) +#define RBASE5 (0x42C / 4) +#define RBASE6 (0x434 / 4) +#define RBASE7 (0x43C / 4) +#define TMR_RXTS_H (0x4C0 / 4) +#define TMR_RXTS_L (0x4C4 / 4) +#define MACCFG1 (0x500 / 4) +#define MACCFG2 (0x504 / 4) +#define IPGIFG (0x508 / 4) +#define HAFDUP (0x50C / 4) +#define MAXFRM (0x510 / 4) +#define MIIMCFG (0x520 / 4) +#define MIIMCOM (0x524 / 4) +#define MIIMADD (0x528 / 4) +#define MIIMCON (0x52C / 4) +#define MIIMSTAT (0x530 / 4) +#define MIIMIND (0x534 / 4) +#define IFSTAT (0x53C / 4) +#define MACSTNADDR1 (0x540 / 4) +#define MACSTNADDR2 (0x544 / 4) +#define MAC01ADDR1 (0x548 / 4) +#define MAC01ADDR2 (0x54C / 4) +#define MAC02ADDR1 (0x550 / 4) +#define MAC02ADDR2 (0x554 / 4) +#define MAC03ADDR1 (0x558 / 4) +#define MAC03ADDR2 (0x55C / 4) +#define MAC04ADDR1 (0x560 / 4) +#define MAC04ADDR2 (0x564 / 4) +#define MAC05ADDR1 (0x568 / 4) +#define MAC05ADDR2 (0x56C / 4) +#define MAC06ADDR1 (0x570 / 4) +#define MAC06ADDR2 (0x574 / 4) +#define MAC07ADDR1 (0x578 / 4) +#define MAC07ADDR2 (0x57C / 4) +#define MAC08ADDR1 (0x580 / 4) +#define MAC08ADDR2 (0x584 / 4) +#define MAC09ADDR1 (0x588 / 4) +#define MAC09ADDR2 (0x58C / 4) +#define MAC10ADDR1 (0x590 / 4) +#define MAC10ADDR2 (0x594 / 4) +#define MAC11ADDR1 (0x598 / 4) +#define MAC11ADDR2 (0x59C / 4) +#define MAC12ADDR1 (0x5A0 / 4) +#define MAC12ADDR2 (0x5A4 / 4) +#define MAC13ADDR1 (0x5A8 / 4) +#define MAC13ADDR2 (0x5AC / 4) +#define MAC14ADDR1 (0x5B0 / 4) +#define MAC14ADDR2 (0x5B4 / 4) +#define MAC15ADDR1 (0x5B8 / 4) +#define MAC15ADDR2 (0x5BC / 4) +#define TR64 (0x680 / 4) +#define TR127 (0x684 / 4) +#define TR255 (0x688 / 4) +#define TR511 (0x68C / 4) +#define TR1K (0x690 / 4) +#define TRMAX (0x694 / 4) +#define TRMGV (0x698 / 4) +#define RBYT (0x69C / 4) +#define RPKT (0x6A0 / 4) +#define RFCS (0x6A4 / 4) +#define RMCA (0x6A8 / 4) +#define RBCA (0x6AC / 4) +#define RXCF (0x6B0 / 4) +#define RXPF (0x6B4 / 4) +#define RXUO (0x6B8 / 4) +#define RALN (0x6BC / 4) +#define RFLR (0x6C0 / 4) +#define RCDE (0x6C4 / 4) +#define RCSE (0x6C8 / 4) +#define RUND (0x6CC / 4) +#define ROVR (0x6D0 / 4) +#define RFRG (0x6D4 / 4) +#define RJBR (0x6D8 / 4) +#define RDRP (0x6DC / 4) +#define TBYT (0x6E0 / 4) +#define TPKT (0x6E4 / 4) +#define TMCA (0x6E8 / 4) +#define TBCA (0x6EC / 4) +#define TXPF (0x6F0 / 4) +#define TDFR (0x6F4 / 4) +#define TEDF (0x6F8 / 4) +#define TSCL (0x6FC / 4) +#define TMCL (0x700 / 4) +#define TLCL (0x704 / 4) +#define TXCL (0x708 / 4) +#define TNCL (0x70C / 4) +#define TDRP (0x714 / 4) +#define TJBR (0x718 / 4) +#define TFCS (0x71C / 4) +#define TXCF (0x720 / 4) +#define TOVR (0x724 / 4) +#define TUND (0x728 / 4) +#define TFRG (0x72C / 4) +#define CAR1 (0x730 / 4) +#define CAR2 (0x734 / 4) +#define CAM1 (0x738 / 4) +#define CAM2 (0x73C / 4) +#define RREJ (0x740 / 4) +#define IGADDR0 (0x800 / 4) +#define IGADDR1 (0x804 / 4) +#define IGADDR2 (0x808 / 4) +#define IGADDR3 (0x80C / 4) +#define IGADDR4 (0x810 / 4) +#define IGADDR5 (0x814 / 4) +#define IGADDR6 (0x818 / 4) +#define IGADDR7 (0x81C / 4) +#define GADDR0 (0x880 / 4) +#define GADDR1 (0x884 / 4) +#define GADDR2 (0x888 / 4) +#define GADDR3 (0x88C / 4) +#define GADDR4 (0x890 / 4) +#define GADDR5 (0x894 / 4) +#define GADDR6 (0x898 / 4) +#define GADDR7 (0x89C / 4) +#define ATTR (0xBF8 / 4) +#define ATTRELI (0xBFC / 4) +#define RQPRM0 (0xC00 / 4) +#define RQPRM1 (0xC04 / 4) +#define RQPRM2 (0xC08 / 4) +#define RQPRM3 (0xC0C / 4) +#define RQPRM4 (0xC10 / 4) +#define RQPRM5 (0xC14 / 4) +#define RQPRM6 (0xC18 / 4) +#define RQPRM7 (0xC1C / 4) +#define RFBPTR0 (0xC44 / 4) +#define RFBPTR1 (0xC4C / 4) +#define RFBPTR2 (0xC54 / 4) +#define RFBPTR3 (0xC5C / 4) +#define RFBPTR4 (0xC64 / 4) +#define RFBPTR5 (0xC6C / 4) +#define RFBPTR6 (0xC74 / 4) +#define RFBPTR7 (0xC7C / 4) +#define TMR_CTRL (0xE00 / 4) +#define TMR_TEVENT (0xE04 / 4) +#define TMR_TEMASK (0xE08 / 4) +#define TMR_PEVENT (0xE0C / 4) +#define TMR_PEMASK (0xE10 / 4) +#define TMR_STAT (0xE14 / 4) +#define TMR_CNT_H (0xE18 / 4) +#define TMR_CNT_L (0xE1C / 4) +#define TMR_ADD (0xE20 / 4) +#define TMR_ACC (0xE24 / 4) +#define TMR_PRSC (0xE28 / 4) +#define TMROFF_H (0xE30 / 4) +#define TMROFF_L (0xE34 / 4) +#define TMR_ALARM1_H (0xE40 / 4) +#define TMR_ALARM1_L (0xE44 / 4) +#define TMR_ALARM2_H (0xE48 / 4) +#define TMR_ALARM2_L (0xE4C / 4) +#define TMR_FIPER1 (0xE80 / 4) +#define TMR_FIPER2 (0xE84 / 4) +#define TMR_FIPER3 (0xE88 / 4) +#define TMR_ETTS1_H (0xEA0 / 4) +#define TMR_ETTS1_L (0xEA4 / 4) +#define TMR_ETTS2_H (0xEA8 / 4) +#define TMR_ETTS2_L (0xEAC / 4) + +#endif /* ! _ETSEC_REGISTERS_H_ */ diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c new file mode 100644 index 0000000000..77602722b3 --- /dev/null +++ b/hw/net/fsl_etsec/rings.c @@ -0,0 +1,650 @@ +/* + * QEMU Freescale eTSEC Emulator + * + * Copyright (c) 2011-2013 AdaCore + * + * 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 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "net/checksum.h" + +#include "etsec.h" +#include "registers.h" + +/* #define ETSEC_RING_DEBUG */ +/* #define HEX_DUMP */ +/* #define DEBUG_BD */ + +#ifdef ETSEC_RING_DEBUG +static const int debug_etsec = 1; +#else +static const int debug_etsec; +#endif + +#define RING_DEBUG(fmt, ...) do { \ + if (debug_etsec) { \ + qemu_log(fmt , ## __VA_ARGS__); \ + } \ + } while (0) + +#ifdef DEBUG_BD + +static void print_tx_bd_flags(uint16_t flags) +{ + qemu_log(" Ready: %d\n", !!(flags & BD_TX_READY)); + qemu_log(" PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC)); + qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP)); + qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT)); + qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST)); + qemu_log(" Tx CRC: %d\n", !!(flags & BD_TX_TC)); + qemu_log(" User-defined preamble / defer: %d\n", + !!(flags & BD_TX_PREDEF)); + qemu_log(" Huge frame enable / Late collision: %d\n", + !!(flags & BD_TX_HFELC)); + qemu_log(" Control frame / Retransmission Limit: %d\n", + !!(flags & BD_TX_CFRL)); + qemu_log(" Retry count: %d\n", + (flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK); + qemu_log(" Underrun / TCP/IP off-load enable: %d\n", + !!(flags & BD_TX_TOEUN)); + qemu_log(" Truncation: %d\n", !!(flags & BD_TX_TR)); +} + +static void print_rx_bd_flags(uint16_t flags) +{ + qemu_log(" Empty: %d\n", !!(flags & BD_RX_EMPTY)); + qemu_log(" Receive software ownership: %d\n", !!(flags & BD_RX_RO1)); + qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP)); + qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT)); + qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST)); + qemu_log(" First in frame: %d\n", !!(flags & BD_RX_FIRST)); + qemu_log(" Miss: %d\n", !!(flags & BD_RX_MISS)); + qemu_log(" Broadcast: %d\n", !!(flags & BD_RX_BROADCAST)); + qemu_log(" Multicast: %d\n", !!(flags & BD_RX_MULTICAST)); + qemu_log(" Rx frame length violation: %d\n", !!(flags & BD_RX_LG)); + qemu_log(" Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO)); + qemu_log(" Short frame: %d\n", !!(flags & BD_RX_SH)); + qemu_log(" Rx CRC Error: %d\n", !!(flags & BD_RX_CR)); + qemu_log(" Overrun: %d\n", !!(flags & BD_RX_OV)); + qemu_log(" Truncation: %d\n", !!(flags & BD_RX_TR)); +} + + +static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index) +{ + qemu_log("eTSEC %s Data Buffer Descriptor (%u)\n", + mode == eTSEC_TRANSMIT ? "Transmit" : "Receive", + index); + qemu_log(" Flags : 0x%04x\n", bd.flags); + if (mode == eTSEC_TRANSMIT) { + print_tx_bd_flags(bd.flags); + } else { + print_rx_bd_flags(bd.flags); + } + qemu_log(" Length : 0x%04x\n", bd.length); + qemu_log(" Pointer : 0x%08x\n", bd.bufptr); +} + +#endif /* DEBUG_BD */ + +static void read_buffer_descriptor(eTSEC *etsec, + hwaddr addr, + eTSEC_rxtx_bd *bd) +{ + assert(bd != NULL); + + RING_DEBUG("READ Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); + cpu_physical_memory_read(addr, + bd, + sizeof(eTSEC_rxtx_bd)); + + if (etsec->regs[DMACTRL].value & DMACTRL_LE) { + bd->flags = lduw_le_p(&bd->flags); + bd->length = lduw_le_p(&bd->length); + bd->bufptr = ldl_le_p(&bd->bufptr); + } else { + bd->flags = lduw_be_p(&bd->flags); + bd->length = lduw_be_p(&bd->length); + bd->bufptr = ldl_be_p(&bd->bufptr); + } +} + +static void write_buffer_descriptor(eTSEC *etsec, + hwaddr addr, + eTSEC_rxtx_bd *bd) +{ + assert(bd != NULL); + + if (etsec->regs[DMACTRL].value & DMACTRL_LE) { + stw_le_p(&bd->flags, bd->flags); + stw_le_p(&bd->length, bd->length); + stl_le_p(&bd->bufptr, bd->bufptr); + } else { + stw_be_p(&bd->flags, bd->flags); + stw_be_p(&bd->length, bd->length); + stl_be_p(&bd->bufptr, bd->bufptr); + } + + RING_DEBUG("Write Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr); + cpu_physical_memory_write(addr, + bd, + sizeof(eTSEC_rxtx_bd)); +} + +static void ievent_set(eTSEC *etsec, + uint32_t flags) +{ + etsec->regs[IEVENT].value |= flags; + + if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN) + || (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) { + qemu_irq_raise(etsec->tx_irq); + RING_DEBUG("%s Raise Tx IRQ\n", __func__); + } + + if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN) + || (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) { + qemu_irq_pulse(etsec->rx_irq); + RING_DEBUG("%s Raise Rx IRQ\n", __func__); + } +} + +static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len) +{ + int add = min_frame_len - etsec->tx_buffer_len; + + /* Padding */ + if (add > 0) { + RING_DEBUG("pad:%u\n", add); + etsec->tx_buffer = g_realloc(etsec->tx_buffer, + etsec->tx_buffer_len + add); + + memset(etsec->tx_buffer + etsec->tx_buffer_len, 0x0, add); + etsec->tx_buffer_len += add; + } + + /* Never add CRC in QEMU */ +} + +static void process_tx_fcb(eTSEC *etsec) +{ + uint8_t flags = (uint8_t)(*etsec->tx_buffer); + /* L3 header offset from start of frame */ + uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3); + /* L4 header offset from start of L3 header */ + uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2); + /* L3 header */ + uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset; + /* L4 header */ + uint8_t *l4_header = l3_header + l4_header_offset; + + /* if packet is IP4 and IP checksum is requested */ + if (flags & FCB_TX_IP && flags & FCB_TX_CIP) { + /* do IP4 checksum (TODO This funtion does TCP/UDP checksum but not sure + * if it also does IP4 checksum. */ + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } + /* TODO Check the correct usage of the PHCS field of the FCB in case the NPH + * flag is on */ + + /* if packet is IP4 and TCP or UDP */ + if (flags & FCB_TX_IP && flags & FCB_TX_TUP) { + /* if UDP */ + if (flags & FCB_TX_UDP) { + /* if checksum is requested */ + if (flags & FCB_TX_CTU) { + /* do UDP checksum */ + + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } else { + /* set checksum field to 0 */ + l4_header[6] = 0; + l4_header[7] = 0; + } + } else if (flags & FCB_TX_CTU) { /* if TCP and checksum is requested */ + /* do TCP checksum */ + net_checksum_calculate(etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } + } +} + +static void process_tx_bd(eTSEC *etsec, + eTSEC_rxtx_bd *bd) +{ + uint8_t *tmp_buff = NULL; + hwaddr tbdbth = (hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32; + + if (bd->length == 0) { + /* ERROR */ + return; + } + + if (etsec->tx_buffer_len == 0) { + /* It's the first BD */ + etsec->first_bd = *bd; + } + + /* TODO: if TxBD[TOE/UN] skip the Tx Frame Control Block*/ + + /* Load this Data Buffer */ + etsec->tx_buffer = g_realloc(etsec->tx_buffer, + etsec->tx_buffer_len + bd->length); + tmp_buff = etsec->tx_buffer + etsec->tx_buffer_len; + cpu_physical_memory_read(bd->bufptr + tbdbth, tmp_buff, bd->length); + + /* Update buffer length */ + etsec->tx_buffer_len += bd->length; + + + if (etsec->tx_buffer_len != 0 && (bd->flags & BD_LAST)) { + if (etsec->regs[MACCFG1].value & MACCFG1_TX_EN) { + /* MAC Transmit enabled */ + + /* Process offload Tx FCB */ + if (etsec->first_bd.flags & BD_TX_TOEUN) { + process_tx_fcb(etsec); + } + + if (etsec->first_bd.flags & BD_TX_PADCRC + || etsec->regs[MACCFG2].value & MACCFG2_PADCRC) { + + /* Padding and CRC (Padding implies CRC) */ + tx_padding_and_crc(etsec, 64); + + } else if (etsec->first_bd.flags & BD_TX_TC + || etsec->regs[MACCFG2].value & MACCFG2_CRC_EN) { + + /* Only CRC */ + /* Never add CRC in QEMU */ + } + +#if defined(HEX_DUMP) + qemu_log("eTSEC Send packet size:%d\n", etsec->tx_buffer_len); + qemu_hexdump(etsec->tx_buffer, stderr, "", etsec->tx_buffer_len); +#endif /* ETSEC_RING_DEBUG */ + + if (etsec->first_bd.flags & BD_TX_TOEUN) { + qemu_send_packet(qemu_get_queue(etsec->nic), + etsec->tx_buffer + 8, + etsec->tx_buffer_len - 8); + } else { + qemu_send_packet(qemu_get_queue(etsec->nic), + etsec->tx_buffer, + etsec->tx_buffer_len); + } + + } + + etsec->tx_buffer_len = 0; + + if (bd->flags & BD_INTERRUPT) { + ievent_set(etsec, IEVENT_TXF); + } + } else { + if (bd->flags & BD_INTERRUPT) { + ievent_set(etsec, IEVENT_TXB); + } + } + + /* Update DB flags */ + + /* Clear Ready */ + bd->flags &= ~BD_TX_READY; + + /* Clear Defer */ + bd->flags &= ~BD_TX_PREDEF; + + /* Clear Late Collision */ + bd->flags &= ~BD_TX_HFELC; + + /* Clear Retransmission Limit */ + bd->flags &= ~BD_TX_CFRL; + + /* Clear Retry Count */ + bd->flags &= ~(BD_TX_RC_MASK << BD_TX_RC_OFFSET); + + /* Clear Underrun */ + bd->flags &= ~BD_TX_TOEUN; + + /* Clear Truncation */ + bd->flags &= ~BD_TX_TR; +} + +void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr) +{ + hwaddr ring_base = 0; + hwaddr bd_addr = 0; + eTSEC_rxtx_bd bd; + uint16_t bd_flags; + + if (!(etsec->regs[MACCFG1].value & MACCFG1_TX_EN)) { + RING_DEBUG("%s: MAC Transmit not enabled\n", __func__); + return; + } + + ring_base = (hwaddr)(etsec->regs[TBASEH].value & 0xF) << 32; + ring_base += etsec->regs[TBASE0 + ring_nbr].value & ~0x7; + bd_addr = etsec->regs[TBPTR0 + ring_nbr].value & ~0x7; + + do { + read_buffer_descriptor(etsec, bd_addr, &bd); + +#ifdef DEBUG_BD + print_bd(bd, + eTSEC_TRANSMIT, + (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); + +#endif /* DEBUG_BD */ + + /* Save flags before BD update */ + bd_flags = bd.flags; + + if (bd_flags & BD_TX_READY) { + process_tx_bd(etsec, &bd); + + /* Write back BD after update */ + write_buffer_descriptor(etsec, bd_addr, &bd); + } + + /* Wrap or next BD */ + if (bd_flags & BD_WRAP) { + bd_addr = ring_base; + } else { + bd_addr += sizeof(eTSEC_rxtx_bd); + } + + } while (bd_addr != ring_base); + + bd_addr = ring_base; + + /* Save the Buffer Descriptor Pointers to current bd */ + etsec->regs[TBPTR0 + ring_nbr].value = bd_addr; + + /* Set transmit halt THLTx */ + etsec->regs[TSTAT].value |= 1 << (31 - ring_nbr); +} + +static void fill_rx_bd(eTSEC *etsec, + eTSEC_rxtx_bd *bd, + const uint8_t **buf, + size_t *size) +{ + uint16_t to_write; + hwaddr bufptr = bd->bufptr + + ((hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32); + uint8_t padd[etsec->rx_padding]; + uint8_t rem; + + RING_DEBUG("eTSEC fill Rx buffer @ 0x%016" HWADDR_PRIx + " size:%zu(padding + crc:%u) + fcb:%u\n", + bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size); + + bd->length = 0; + + /* This operation will only write FCB */ + if (etsec->rx_fcb_size != 0) { + + cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size); + + bufptr += etsec->rx_fcb_size; + bd->length += etsec->rx_fcb_size; + etsec->rx_fcb_size = 0; + + } + + /* We remove padding from the computation of to_write because it is not + * allocated in the buffer. + */ + to_write = MIN(*size - etsec->rx_padding, + etsec->regs[MRBLR].value - etsec->rx_fcb_size); + + /* This operation can only write packet data and no padding */ + if (to_write > 0) { + cpu_physical_memory_write(bufptr, *buf, to_write); + + *buf += to_write; + bufptr += to_write; + *size -= to_write; + + bd->flags &= ~BD_RX_EMPTY; + bd->length += to_write; + } + + if (*size == etsec->rx_padding) { + /* The remaining bytes are only for padding which is not actually + * allocated in the data buffer. + */ + + rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding); + + if (rem > 0) { + memset(padd, 0x0, sizeof(padd)); + etsec->rx_padding -= rem; + *size -= rem; + bd->length += rem; + cpu_physical_memory_write(bufptr, padd, rem); + } + } +} + +static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size) +{ + uint32_t fcb_size = 0; + uint8_t prsdep = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET) + & RCTRL_PRSDEP_MASK; + + if (prsdep != 0) { + /* Prepend FCB (FCB size + RCTRL[PAL]) */ + fcb_size = 8 + ((etsec->regs[RCTRL].value >> 16) & 0x1F); + + etsec->rx_fcb_size = fcb_size; + + /* TODO: fill_FCB(etsec); */ + memset(etsec->rx_fcb, 0x0, sizeof(etsec->rx_fcb)); + + } else { + etsec->rx_fcb_size = 0; + } + + if (etsec->rx_buffer != NULL) { + g_free(etsec->rx_buffer); + } + + /* Do not copy the frame for now */ + etsec->rx_buffer = (uint8_t *)buf; + etsec->rx_buffer_len = size; + + /* CRC padding (We don't have to compute the CRC) */ + etsec->rx_padding = 4; + + etsec->rx_first_in_frame = 1; + etsec->rx_remaining_data = etsec->rx_buffer_len; + RING_DEBUG("%s: rx_buffer_len:%u rx_padding+crc:%u\n", __func__, + etsec->rx_buffer_len, etsec->rx_padding); +} + +void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size) +{ + int ring_nbr = 0; /* Always use ring0 (no filer) */ + + if (etsec->rx_buffer_len != 0) { + RING_DEBUG("%s: We can't receive now," + " a buffer is already in the pipe\n", __func__); + return; + } + + if (etsec->regs[RSTAT].value & 1 << (23 - ring_nbr)) { + RING_DEBUG("%s: The ring is halted\n", __func__); + return; + } + + if (etsec->regs[DMACTRL].value & DMACTRL_GRS) { + RING_DEBUG("%s: Graceful receive stop\n", __func__); + return; + } + + if (!(etsec->regs[MACCFG1].value & MACCFG1_RX_EN)) { + RING_DEBUG("%s: MAC Receive not enabled\n", __func__); + return; + } + + if ((etsec->regs[RCTRL].value & RCTRL_RSF) && (size < 60)) { + /* CRC is not in the packet yet, so short frame is below 60 bytes */ + RING_DEBUG("%s: Drop short frame\n", __func__); + return; + } + + rx_init_frame(etsec, buf, size); + + etsec_walk_rx_ring(etsec, ring_nbr); +} + +void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr) +{ + hwaddr ring_base = 0; + hwaddr bd_addr = 0; + hwaddr start_bd_addr = 0; + eTSEC_rxtx_bd bd; + uint16_t bd_flags; + size_t remaining_data; + const uint8_t *buf; + uint8_t *tmp_buf; + size_t size; + + if (etsec->rx_buffer_len == 0) { + /* No frame to send */ + RING_DEBUG("No frame to send\n"); + return; + } + + remaining_data = etsec->rx_remaining_data + etsec->rx_padding; + buf = etsec->rx_buffer + + (etsec->rx_buffer_len - etsec->rx_remaining_data); + size = etsec->rx_buffer_len + etsec->rx_padding; + + ring_base = (hwaddr)(etsec->regs[RBASEH].value & 0xF) << 32; + ring_base += etsec->regs[RBASE0 + ring_nbr].value & ~0x7; + start_bd_addr = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7; + + do { + read_buffer_descriptor(etsec, bd_addr, &bd); + +#ifdef DEBUG_BD + print_bd(bd, + eTSEC_RECEIVE, + (bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd)); + +#endif /* DEBUG_BD */ + + /* Save flags before BD update */ + bd_flags = bd.flags; + + if (bd_flags & BD_RX_EMPTY) { + fill_rx_bd(etsec, &bd, &buf, &remaining_data); + + if (etsec->rx_first_in_frame) { + bd.flags |= BD_RX_FIRST; + etsec->rx_first_in_frame = 0; + etsec->rx_first_bd = bd; + } + + /* Last in frame */ + if (remaining_data == 0) { + + /* Clear flags */ + + bd.flags &= ~0x7ff; + + bd.flags |= BD_LAST; + + /* NOTE: non-octet aligned frame is impossible in qemu */ + + if (size >= etsec->regs[MAXFRM].value) { + /* frame length violation */ + qemu_log("%s frame length violation: size:%zu MAXFRM:%d\n", + __func__, size, etsec->regs[MAXFRM].value); + + bd.flags |= BD_RX_LG; + } + + if (size < 64) { + /* Short frame */ + bd.flags |= BD_RX_SH; + } + + /* TODO: Broadcast and Multicast */ + + if (bd.flags | BD_INTERRUPT) { + /* Set RXFx */ + etsec->regs[RSTAT].value |= 1 << (7 - ring_nbr); + + /* Set IEVENT */ + ievent_set(etsec, IEVENT_RXF); + } + + } else { + if (bd.flags | BD_INTERRUPT) { + /* Set IEVENT */ + ievent_set(etsec, IEVENT_RXB); + } + } + + /* Write back BD after update */ + write_buffer_descriptor(etsec, bd_addr, &bd); + } + + /* Wrap or next BD */ + if (bd_flags & BD_WRAP) { + bd_addr = ring_base; + } else { + bd_addr += sizeof(eTSEC_rxtx_bd); + } + } while (remaining_data != 0 + && (bd_flags & BD_RX_EMPTY) + && bd_addr != start_bd_addr); + + /* Reset ring ptr */ + etsec->regs[RBPTR0 + ring_nbr].value = bd_addr; + + /* The frame is too large to fit in the Rx ring */ + if (remaining_data > 0) { + + /* Set RSTAT[QHLTx] */ + etsec->regs[RSTAT].value |= 1 << (23 - ring_nbr); + + /* Save remaining data to send the end of the frame when the ring will + * be restarted + */ + etsec->rx_remaining_data = remaining_data; + + /* Copy the frame */ + tmp_buf = g_malloc(size); + memcpy(tmp_buf, etsec->rx_buffer, size); + etsec->rx_buffer = tmp_buf; + + RING_DEBUG("no empty RxBD available any more\n"); + } else { + etsec->rx_buffer_len = 0; + etsec->rx_buffer = NULL; + } + + RING_DEBUG("eTSEC End of ring_write: remaining_data:%zu\n", remaining_data); +} diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 1bd6f50aaa..f6fbcb56bf 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -405,6 +405,8 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, dev->rx_bufs++; + qemu_flush_queued_packets(qemu_get_queue(dev->nic)); + DPRINTF("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d" " bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs, (unsigned long long)buf); diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index b37ce9d633..8a08752613 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -238,6 +238,7 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { CPUState *cpu; + PowerPCCPU *pcpu; char cpu_name[128]; uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20); @@ -246,14 +247,16 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args, continue; } env = cpu->env_ptr; + pcpu = POWERPC_CPU(cpu); snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", - cpu->cpu_index); + ppc_get_vcpu_dt_id(pcpu)); qemu_fdt_add_subnode(fdt, cpu_name); qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu"); - qemu_fdt_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index); + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", + ppc_get_vcpu_dt_id(pcpu)); qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size", env->dcache_line_size); qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size", diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 114be64480..0e82719b69 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -26,6 +26,7 @@ #include "hw/ppc/ppc_e500.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/cpus.h" #include "hw/timer/m48t59.h" #include "qemu/log.h" #include "hw/loader.h" @@ -1362,3 +1363,24 @@ int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size, return 0; } + +/* CPU device-tree ID helpers */ +int ppc_get_vcpu_dt_id(PowerPCCPU *cpu) +{ + return cpu->cpu_dt_id; +} + +PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + if (cpu->cpu_dt_id == cpu_dt_id) { + return cpu; + } + } + + return NULL; +} diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 93d02c1e50..bf46c380ec 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -49,6 +49,7 @@ #include "exec/address-spaces.h" #include "hw/usb.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include <libfdt.h> @@ -206,19 +207,20 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) CPU_FOREACH(cpu) { DeviceClass *dc = DEVICE_GET_CLASS(cpu); + int index = ppc_get_vcpu_dt_id(POWERPC_CPU(cpu)); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(cpu->numa_node), - cpu_to_be32(cpu->cpu_index)}; + cpu_to_be32(index)}; - if ((cpu->cpu_index % smt) != 0) { + if ((index % smt) != 0) { continue; } snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name, - cpu->cpu_index); + index); offset = fdt_path_offset(fdt, cpu_model); if (offset < 0) { @@ -367,7 +369,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, CPUPPCState *env = &cpu->env; DeviceClass *dc = DEVICE_GET_CLASS(cs); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); - int index = cs->cpu_index; + int index = ppc_get_vcpu_dt_id(cpu); uint32_t servers_prop[smp_threads]; uint32_t gservers_prop[smp_threads * 2]; char *nodename; @@ -685,6 +687,7 @@ static void spapr_reset_htab(sPAPREnvironment *spapr) if (shift > 0) { /* Kernel handles htab, we don't need to allocate one */ spapr->htab_shift = shift; + kvmppc_kern_htab = true; } else { if (!spapr->htab) { /* Allocate an htab if we don't yet have one */ @@ -740,8 +743,21 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; env->external_htab = (uint8_t *)spapr->htab; + if (kvm_enabled() && !env->external_htab) { + /* + * HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte* + * functions do the right thing. + */ + env->external_htab = (void *)1; + } env->htab_base = -1; - env->htab_mask = HTAB_SIZE(spapr) - 1; + /* + * htab_mask is the mask used to normalize hash value to PTEG index. + * htab_shift is log2 of hash table size. + * We have 8 hpte per group, and each hpte is 16 bytes. + * ie have 128 bytes per hpte entry. + */ + env->htab_mask = (1ULL << ((spapr)->htab_shift - 7)) - 1; env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab | (spapr->htab_shift - 18); } @@ -1305,20 +1321,15 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); - if (kernel_size < 0) { + if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0); kernel_le = kernel_size > 0; } if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, - KERNEL_LOAD_ADDR, - load_limit - KERNEL_LOAD_ADDR); - } - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); + fprintf(stderr, "qemu: error loading %s: %s\n", + kernel_filename, load_elf_strerror(kernel_size)); exit(1); } @@ -1366,6 +1377,24 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) assert(spapr->fdt_skel != NULL); } +static int spapr_kvm_type(const char *vm_type) +{ + if (!vm_type) { + return 0; + } + + if (!strcmp(vm_type, "HV")) { + return 1; + } + + if (!strcmp(vm_type, "PR")) { + return 2; + } + + error_report("Unknown kvm-type specified '%s'", vm_type); + exit(1); +} + static QEMUMachine spapr_machine = { .name = "pseries", .desc = "pSeries Logical Partition (PAPR compliant)", @@ -1376,6 +1405,7 @@ static QEMUMachine spapr_machine = { .max_cpus = MAX_CPUS, .no_parallel = 1, .default_boot_order = NULL, + .kvm_type = spapr_kvm_type, }; static void spapr_machine_init(void) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 3ffcc65f03..d918780746 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -40,6 +40,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, return rb; } +static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) +{ + /* + * hash value/pteg group index is normalized by htab_mask + */ + if (((pte_index & ~7ULL) / HPTES_PER_GROUP) & ~env->htab_mask) { + return false; + } + return true; +} + static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -50,8 +61,8 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong ptel = args[3]; target_ulong page_shift = 12; target_ulong raddr; - target_ulong i; - hwaddr hpte; + target_ulong index; + uint64_t token; /* only handle 4k and 16M pages for now */ if (pteh & HPTE64_V_LARGE) { @@ -91,33 +102,37 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, pteh &= ~0x60ULL; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } + + index = 0; if (likely((flags & H_EXACT) == 0)) { pte_index &= ~7ULL; - hpte = pte_index * HASH_PTE_SIZE_64; - for (i = 0; ; ++i) { - if (i == 8) { + token = ppc_hash64_start_access(cpu, pte_index); + do { + if (index == 8) { + ppc_hash64_stop_access(token); return H_PTEG_FULL; } - if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) { + if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) { break; } - hpte += HASH_PTE_SIZE_64; - } + } while (index++); + ppc_hash64_stop_access(token); } else { - i = 0; - hpte = pte_index * HASH_PTE_SIZE_64; - if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) { + token = ppc_hash64_start_access(cpu, pte_index); + if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) { + ppc_hash64_stop_access(token); return H_PTEG_FULL; } + ppc_hash64_stop_access(token); } - ppc_hash64_store_hpte1(env, hpte, ptel); - /* eieio(); FIXME: need some sort of barrier for smp? */ - ppc_hash64_store_hpte0(env, hpte, pteh | HPTE64_V_HPTE_DIRTY); - args[0] = pte_index + i; + ppc_hash64_store_hpte(env, pte_index + index, + pteh | HPTE64_V_HPTE_DIRTY, ptel); + + args[0] = pte_index + index; return H_SUCCESS; } @@ -133,17 +148,17 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, target_ulong flags, target_ulong *vp, target_ulong *rp) { - hwaddr hpte; + uint64_t token; target_ulong v, r, rb; - if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, ptex)) { return REMOVE_PARM; } - hpte = ptex * HASH_PTE_SIZE_64; - - v = ppc_hash64_load_hpte0(env, hpte); - r = ppc_hash64_load_hpte1(env, hpte); + token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex); + v = ppc_hash64_load_hpte0(env, token, 0); + r = ppc_hash64_load_hpte1(env, token, 0); + ppc_hash64_stop_access(token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || @@ -152,7 +167,7 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, } *vp = v; *rp = r; - ppc_hash64_store_hpte0(env, hpte, HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0); rb = compute_tlbie_rb(v, r, ptex); ppc_tlb_invalidate_one(env, rb); return REMOVE_SUCCESS; @@ -259,17 +274,17 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; - hwaddr hpte; + uint64_t token; target_ulong v, r, rb; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } - hpte = pte_index * HASH_PTE_SIZE_64; - - v = ppc_hash64_load_hpte0(env, hpte); - r = ppc_hash64_load_hpte1(env, hpte); + token = ppc_hash64_start_access(cpu, pte_index); + v = ppc_hash64_load_hpte0(env, token, 0); + r = ppc_hash64_load_hpte1(env, token, 0); + ppc_hash64_stop_access(token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { @@ -282,11 +297,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, r |= (flags << 48) & HPTE64_R_KEY_HI; r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); rb = compute_tlbie_rb(v, r, pte_index); - ppc_hash64_store_hpte0(env, hpte, (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, pte_index, + (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); ppc_tlb_invalidate_one(env, rb); - ppc_hash64_store_hpte1(env, hpte, r); /* Don't need a memory barrier, due to qemu's global lock */ - ppc_hash64_store_hpte0(env, hpte, v | HPTE64_V_HPTE_DIRTY); + ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r); return H_SUCCESS; } @@ -299,7 +314,7 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint8_t *hpte; int i, ridx, n_entries = 1; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { + if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } @@ -467,13 +482,13 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; CPUPPCState *tenv; - CPUState *tcpu; + PowerPCCPU *tcpu; - tcpu = qemu_get_cpu(procno); + tcpu = ppc_get_vcpu_by_dt_id(procno); if (!tcpu) { return H_PARAMETER; } - tenv = tcpu->env_ptr; + tenv = &tcpu->env; switch (flags) { case FLAGS_REGISTER_VPA: diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index ef45f4f0cc..d9fe946818 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -243,6 +243,42 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, return ret; } +static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, + target_ulong *tce) +{ + if (ioba >= tcet->window_size) { + hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x" + TARGET_FMT_lx "\n", ioba); + return H_PARAMETER; + } + + *tce = tcet->table[ioba >> SPAPR_TCE_PAGE_SHIFT]; + + return H_SUCCESS; +} + +static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong liobn = args[0]; + target_ulong ioba = args[1]; + target_ulong tce = 0; + target_ulong ret = H_PARAMETER; + sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + + ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1); + + if (tcet) { + ret = get_tce_emu(tcet, ioba, &tce); + if (!ret) { + args[0] = tce; + } + } + trace_spapr_iommu_get(liobn, ioba, ret, tce); + + return ret; +} + int spapr_dma_dt(void *fdt, int node_off, const char *propname, uint32_t liobn, uint64_t window, uint32_t size) { @@ -295,6 +331,7 @@ static void spapr_tce_table_class_init(ObjectClass *klass, void *data) /* hcall-tce */ spapr_register_hypercall(H_PUT_TCE, h_put_tce); + spapr_register_hypercall(H_GET_TCE, h_get_tce); } static TypeInfo spapr_tce_table_info = { diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 4c7c3aec12..cea9469872 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -469,6 +469,8 @@ static const MemoryRegionOps spapr_msi_ops = { void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr) { + uint64_t window_size = 4096; + /* * As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors, * we need to allocate some memory to catch those writes coming @@ -476,10 +478,19 @@ void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr) * As MSIMessage:addr is going to be the same and MSIMessage:data * is going to be a VIRQ number, 4 bytes of the MSI MR will only * be used. + * + * For KVM we want to ensure that this memory is a full page so that + * our memory slot is of page size granularity. */ +#ifdef CONFIG_KVM + if (kvm_enabled()) { + window_size = getpagesize(); + } +#endif + spapr->msi_win_addr = addr; memory_region_init_io(&spapr->msiwindow, NULL, &spapr_msi_ops, spapr, - "msi", getpagesize()); + "msi", window_size); memory_region_add_subregion(get_system_memory(), spapr->msi_win_addr, &spapr->msiwindow); } @@ -728,6 +739,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) dc->props = spapr_phb_properties; dc->reset = spapr_phb_reset; dc->vmsd = &vmstate_spapr_pci; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->cannot_instantiate_with_device_add_yet = false; } static const TypeInfo spapr_phb_info = { diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 1cb276de05..73860d0486 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -131,7 +131,7 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, uint32_t nret, target_ulong rets) { target_ulong id; - CPUState *cpu; + PowerPCCPU *cpu; if (nargs != 1 || nret != 2) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -139,9 +139,9 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, } id = rtas_ld(args, 0); - cpu = qemu_get_cpu(id); + cpu = ppc_get_vcpu_by_dt_id(id); if (cpu != NULL) { - if (cpu->halted) { + if (CPU(cpu)->halted) { rtas_st(rets, 1, 0); } else { rtas_st(rets, 1, 2); @@ -161,7 +161,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, uint32_t nret, target_ulong rets) { target_ulong id, start, r3; - CPUState *cs; + PowerPCCPU *cpu; if (nargs != 3 || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -172,9 +172,9 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr, start = rtas_ld(args, 1); r3 = rtas_ld(args, 2); - cs = qemu_get_cpu(id); - if (cs != NULL) { - PowerPCCPU *cpu = POWERPC_CPU(cs); + cpu = ppc_get_vcpu_by_dt_id(id); + if (cpu != NULL) { + CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; if (!cs->halted) { diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 85a0e537b9..ce8ea91e8b 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -174,6 +174,19 @@ static int xilinx_load_device_tree(hwaddr addr, if (!fdt) { return 0; } + + r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (r < 0) { + error_report("couldn't set /chosen/linux,initrd-start"); + } + + r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (r < 0) { + error_report("couldn't set /chosen/linux,initrd-end"); + } + r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); if (r < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); @@ -187,6 +200,8 @@ static void virtex_init(QEMUMachineInitArgs *args) const char *cpu_model = args->cpu_model; const char *kernel_filename = args->kernel_filename; const char *kernel_cmdline = args->kernel_cmdline; + hwaddr initrd_base = 0; + int initrd_size = 0; MemoryRegion *address_space_mem = get_system_memory(); DeviceState *dev; PowerPCCPU *cpu; @@ -259,10 +274,27 @@ static void virtex_init(QEMUMachineInitArgs *args) boot_info.ima_size = kernel_size; + /* Load initrd. */ + if (args->initrd_filename) { + initrd_base = high = ROUND_UP(high, 4); + initrd_size = load_image_targphys(args->initrd_filename, + high, ram_size - high); + + if (initrd_size < 0) { + error_report("couldn't load ram disk '%s'", + args->initrd_filename); + exit(1); + } + high = ROUND_UP(high + initrd_size, 4); + } + /* Provide a device-tree. */ boot_info.fdt = high + (8192 * 2); boot_info.fdt &= ~8191; - xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline); + + xilinx_load_device_tree(boot_info.fdt, ram_size, + initrd_base, initrd_size, + kernel_cmdline); } env->load_info = &boot_info; } diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 04fb1a8e05..32d38a08f6 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -98,10 +98,10 @@ static int s390_ipl_init(SysBusDevice *dev) uint64_t pentry = KERN_IMAGE_START; kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL, NULL, 1, ELF_MACHINE, 0); - if (kernel_size == -1) { + if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); } - if (kernel_size == -1) { + if (kernel_size < 0) { fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel); return -1; } diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index e8bca390dd..b3835c821d 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -62,6 +62,8 @@ #define SRP_RSP_SENSE_DATA_LEN 18 +#define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL + typedef union vscsi_crq { struct viosrp_crq s; uint8_t raw[16]; @@ -719,12 +721,70 @@ static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req) } } +static void vscsi_report_luns(VSCSIState *s, vscsi_req *req) +{ + BusChild *kid; + int i, len, n, rc; + uint8_t *resp_data; + bool found_lun0; + + n = 0; + found_lun0 = false; + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + SCSIDevice *dev = SCSI_DEVICE(kid->child); + + n += 8; + if (dev->channel == 0 && dev->id == 0 && dev->lun == 0) { + found_lun0 = true; + } + } + if (!found_lun0) { + n += 8; + } + len = n+8; + + resp_data = g_malloc0(len); + memset(resp_data, 0, len); + stl_be_p(resp_data, n); + i = found_lun0 ? 8 : 16; + QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) { + DeviceState *qdev = kid->child; + SCSIDevice *dev = SCSI_DEVICE(qdev); + + if (dev->id == 0 && dev->channel == 0) { + resp_data[i] = 0; /* Use simple LUN for 0 (SAM5 4.7.7.1) */ + } else { + resp_data[i] = (2 << 6); /* Otherwise LUN addressing (4.7.7.4) */ + } + resp_data[i] |= dev->id; + resp_data[i+1] = (dev->channel << 5); + resp_data[i+1] |= dev->lun; + i += 8; + } + + vscsi_preprocess_desc(req); + rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len); + g_free(resp_data); + if (rc < 0) { + vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0); + vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); + } else { + vscsi_send_rsp(s, req, 0, len - rc, 0); + } +} + static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) { union srp_iu *srp = &req->iu.srp; SCSIDevice *sdev; int n, lun; + if ((srp->cmd.lun == 0 || be64_to_cpu(srp->cmd.lun) == SRP_REPORT_LUNS_WLUN) + && srp->cmd.cdb[0] == REPORT_LUNS) { + vscsi_report_luns(s, req); + return 0; + } + sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun); if (!sdev) { DPRINTF("VSCSI: Command for lun %08" PRIx64 " with no drive\n", |