diff options
author | Michael S. Tsirkin <mst@redhat.com> | 2018-01-11 22:01:17 +0200 |
---|---|---|
committer | Michael S. Tsirkin <mst@redhat.com> | 2018-01-11 22:03:50 +0200 |
commit | acc95bc85036c443da8bf7159a77edf9f00dcd80 (patch) | |
tree | 21965c6e60a2e29664b7685e52feacdb6a86e0bd /hw/sparc64 | |
parent | 880b1ffe6ec2f0ae25cc4175716227ad275e8b8a (diff) | |
parent | 997eba28a3ed5400a80f754bf3a1c8044b75b9ff (diff) | |
download | qemu-acc95bc85036c443da8bf7159a77edf9f00dcd80.zip qemu-acc95bc85036c443da8bf7159a77edf9f00dcd80.tar.gz qemu-acc95bc85036c443da8bf7159a77edf9f00dcd80.tar.bz2 |
Merge remote-tracking branch 'origin/master' into HEAD
Resolve conflicts around apb.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'hw/sparc64')
-rw-r--r-- | hw/sparc64/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/sparc64/sparc64.c | 2 | ||||
-rw-r--r-- | hw/sparc64/sun4u.c | 193 | ||||
-rw-r--r-- | hw/sparc64/sun4u_iommu.c | 342 | ||||
-rw-r--r-- | hw/sparc64/trace-events | 9 |
5 files changed, 467 insertions, 80 deletions
diff --git a/hw/sparc64/Makefile.objs b/hw/sparc64/Makefile.objs index cf9de21..117e0ff 100644 --- a/hw/sparc64/Makefile.objs +++ b/hw/sparc64/Makefile.objs @@ -1,3 +1,4 @@ obj-y += sparc64.o +obj-y += sun4u_iommu.o obj-y += sun4u.o obj-y += niagara.o
\ No newline at end of file diff --git a/hw/sparc64/sparc64.c b/hw/sparc64/sparc64.c index 9453e2c..95a06f0 100644 --- a/hw/sparc64/sparc64.c +++ b/hw/sparc64/sparc64.c @@ -350,6 +350,8 @@ SPARCCPU *sparc64_cpu_devinit(const char *cpu_type, uint64_t prom_addr) uint32_t hstick_frequency = 100 * 1000000; cpu = SPARC_CPU(cpu_create(cpu_type)); + qdev_init_gpio_in_named(DEVICE(cpu), sparc64_cpu_set_ivec_irq, + "ivec-irq", IVEC_MAX); env = &cpu->env; env->tick = cpu_timer_create("tick", cpu, tick_irq, diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 1672f25..ec45ec2 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -27,7 +27,9 @@ #include "cpu.h" #include "hw/hw.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bridge.h" #include "hw/pci/pci_bus.h" +#include "hw/pci/pci_host.h" #include "hw/pci-host/apb.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" @@ -46,17 +48,9 @@ #include "hw/ide/pci.h" #include "hw/loader.h" #include "elf.h" +#include "trace.h" #include "qemu/cutils.h" -//#define DEBUG_EBUS - -#ifdef DEBUG_EBUS -#define EBUS_DPRINTF(fmt, ...) \ - do { printf("EBUS: " fmt , ## __VA_ARGS__); } while (0) -#else -#define EBUS_DPRINTF(fmt, ...) -#endif - #define KERNEL_LOAD_ADDR 0x00404000 #define CMDLINE_ADDR 0x003ff000 #define PROM_SIZE_MAX (4 * 1024 * 1024) @@ -81,11 +75,19 @@ struct hwdef { }; typedef struct EbusState { - PCIDevice pci_dev; + /*< private >*/ + PCIDevice parent_obj; + + ISABus *isa_bus; + qemu_irq isa_bus_irqs[ISA_NUM_IRQS]; + uint64_t console_serial_base; MemoryRegion bar0; MemoryRegion bar1; } EbusState; +#define TYPE_EBUS "ebus" +#define EBUS(obj) OBJECT_CHECK(EbusState, (obj), TYPE_EBUS) + void DMA_init(ISABus *bus, int high_page_enable) { } @@ -203,48 +205,72 @@ typedef struct ResetData { uint64_t prom_addr; } ResetData; -static void isa_irq_handler(void *opaque, int n, int level) +static void ebus_isa_irq_handler(void *opaque, int n, int level) { - static const int isa_irq_to_ivec[16] = { - [1] = 0x29, /* keyboard */ - [4] = 0x2b, /* serial */ - [6] = 0x27, /* floppy */ - [7] = 0x22, /* parallel */ - [12] = 0x2a, /* mouse */ - }; - qemu_irq *irqs = opaque; - int ivec; - - assert(n < ARRAY_SIZE(isa_irq_to_ivec)); - ivec = isa_irq_to_ivec[n]; - EBUS_DPRINTF("Set ISA IRQ %d level %d -> ivec 0x%x\n", n, level, ivec); - if (ivec) { - qemu_set_irq(irqs[ivec], level); + EbusState *s = EBUS(opaque); + qemu_irq irq = s->isa_bus_irqs[n]; + + /* Pass ISA bus IRQs onto their gpio equivalent */ + trace_ebus_isa_irq_handler(n, level); + if (irq) { + qemu_set_irq(irq, level); } } /* EBUS (Eight bit bus) bridge */ -static ISABus * -pci_ebus_init(PCIDevice *pci_dev, qemu_irq *irqs) +static void ebus_realize(PCIDevice *pci_dev, Error **errp) { + EbusState *s = EBUS(pci_dev); + DeviceState *dev; qemu_irq *isa_irq; - ISABus *isa_bus; + DriveInfo *fd[MAX_FD]; + int i; - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); - isa_irq = qemu_allocate_irqs(isa_irq_handler, irqs, 16); - isa_bus_irqs(isa_bus, isa_irq); - return isa_bus; -} + s->isa_bus = isa_bus_new(DEVICE(pci_dev), get_system_memory(), + pci_address_space_io(pci_dev), errp); + if (!s->isa_bus) { + error_setg(errp, "unable to instantiate EBUS ISA bus"); + return; + } -static void pci_ebus_realize(PCIDevice *pci_dev, Error **errp) -{ - EbusState *s = DO_UPCAST(EbusState, pci_dev, pci_dev); + /* ISA bus */ + isa_irq = qemu_allocate_irqs(ebus_isa_irq_handler, s, ISA_NUM_IRQS); + isa_bus_irqs(s->isa_bus, isa_irq); + qdev_init_gpio_out_named(DEVICE(s), s->isa_bus_irqs, "isa-irq", + ISA_NUM_IRQS); - if (!isa_bus_new(DEVICE(pci_dev), get_system_memory(), - pci_address_space_io(pci_dev), errp)) { - return; + /* Serial ports */ + i = 0; + if (s->console_serial_base) { + serial_mm_init(pci_address_space(pci_dev), s->console_serial_base, + 0, NULL, 115200, serial_hds[i], DEVICE_BIG_ENDIAN); + i++; } + serial_hds_isa_init(s->isa_bus, i, MAX_SERIAL_PORTS); + + /* Parallel ports */ + parallel_hds_isa_init(s->isa_bus, MAX_PARALLEL_PORTS); + /* Keyboard */ + isa_create_simple(s->isa_bus, "i8042"); + + /* Floppy */ + for (i = 0; i < MAX_FD; i++) { + fd[i] = drive_get(IF_FLOPPY, 0, i); + } + dev = DEVICE(isa_create(s->isa_bus, TYPE_ISA_FDC)); + if (fd[0]) { + qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fd[0]), + &error_abort); + } + if (fd[1]) { + qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fd[1]), + &error_abort); + } + qdev_prop_set_uint32(dev, "dma", -1); + qdev_init_nofail(dev); + + /* PCI */ pci_dev->config[0x04] = 0x06; // command = bus master, pci mem pci_dev->config[0x05] = 0x00; pci_dev->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error @@ -260,22 +286,30 @@ static void pci_ebus_realize(PCIDevice *pci_dev, Error **errp) pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); } +static Property ebus_properties[] = { + DEFINE_PROP_UINT64("console-serial-base", EbusState, + console_serial_base, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void ebus_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - k->realize = pci_ebus_realize; + k->realize = ebus_realize; k->vendor_id = PCI_VENDOR_ID_SUN; k->device_id = PCI_DEVICE_ID_SUN_EBUS; k->revision = 0x01; k->class_id = PCI_CLASS_BRIDGE_OTHER; + dc->props = ebus_properties; } static const TypeInfo ebus_info = { - .name = "ebus", + .name = TYPE_EBUS, .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EbusState), .class_init = ebus_class_init, + .instance_size = sizeof(EbusState), .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, @@ -431,14 +465,12 @@ static void sun4uv_init(MemoryRegion *address_space_mem, Nvram *nvram; unsigned int i; uint64_t initrd_addr, initrd_size, kernel_addr, kernel_size, kernel_entry; + APBState *apb; PCIBus *pci_bus, *pci_busA, *pci_busB; PCIDevice *ebus, *pci_dev; - ISABus *isa_bus; SysBusDevice *s; - qemu_irq *ivec_irqs, *pbm_irqs; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - DriveInfo *fd[MAX_FD]; - DeviceState *dev; + DeviceState *iommu, *dev; FWCfgState *fw_cfg; NICInfo *nd; MACAddr macaddr; @@ -447,14 +479,31 @@ static void sun4uv_init(MemoryRegion *address_space_mem, /* init CPUs */ cpu = sparc64_cpu_devinit(machine->cpu_type, hwdef->prom_addr); + /* IOMMU */ + iommu = qdev_create(NULL, TYPE_SUN4U_IOMMU); + qdev_init_nofail(iommu); + /* set up devices */ ram_init(0, machine->ram_size); prom_init(hwdef->prom_addr, bios_name); - ivec_irqs = qemu_allocate_irqs(sparc64_cpu_set_ivec_irq, cpu, IVEC_MAX); - pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, ivec_irqs, &pci_busA, - &pci_busB, &pbm_irqs); + /* Init APB (PCI host bridge) */ + apb = APB_DEVICE(qdev_create(NULL, TYPE_APB)); + qdev_prop_set_uint64(DEVICE(apb), "special-base", APB_SPECIAL_BASE); + qdev_prop_set_uint64(DEVICE(apb), "mem-base", APB_MEM_BASE); + object_property_set_link(OBJECT(apb), OBJECT(iommu), "iommu", &error_abort); + qdev_init_nofail(DEVICE(apb)); + + /* Wire up PCI interrupts to CPU */ + for (i = 0; i < IVEC_MAX; i++) { + qdev_connect_gpio_out_named(DEVICE(apb), "ivec-irq", i, + qdev_get_gpio_in_named(DEVICE(cpu), "ivec-irq", i)); + } + + pci_bus = PCI_HOST_BRIDGE(apb)->bus; + pci_busA = pci_bridge_get_sec_bus(apb->bridgeA); + pci_busB = pci_bridge_get_sec_bus(apb->bridgeB); /* Only in-built Simba PBMs can exist on the root bus, slot 0 on busA is reserved (leaving no slots free after on-board devices) however slots @@ -463,20 +512,22 @@ static void sun4uv_init(MemoryRegion *address_space_mem, pci_busA->slot_reserved_mask = 0xfffffff1; pci_busB->slot_reserved_mask = 0xfffffff0; - ebus = pci_create_multifunction(pci_busA, PCI_DEVFN(1, 0), true, "ebus"); + ebus = pci_create_multifunction(pci_busA, PCI_DEVFN(1, 0), true, TYPE_EBUS); + qdev_prop_set_uint64(DEVICE(ebus), "console-serial-base", + hwdef->console_serial_base); qdev_init_nofail(DEVICE(ebus)); - isa_bus = pci_ebus_init(ebus, pbm_irqs); - - i = 0; - if (hwdef->console_serial_base) { - serial_mm_init(address_space_mem, hwdef->console_serial_base, 0, - NULL, 115200, serial_hds[i], DEVICE_BIG_ENDIAN); - i++; - } - - serial_hds_isa_init(isa_bus, i, MAX_SERIAL_PORTS); - parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); + /* Wire up "well-known" ISA IRQs to APB legacy obio IRQs */ + qdev_connect_gpio_out_named(DEVICE(ebus), "isa-irq", 7, + qdev_get_gpio_in_named(DEVICE(apb), "pbm-irq", OBIO_LPT_IRQ)); + qdev_connect_gpio_out_named(DEVICE(ebus), "isa-irq", 6, + qdev_get_gpio_in_named(DEVICE(apb), "pbm-irq", OBIO_FDD_IRQ)); + qdev_connect_gpio_out_named(DEVICE(ebus), "isa-irq", 1, + qdev_get_gpio_in_named(DEVICE(apb), "pbm-irq", OBIO_KBD_IRQ)); + qdev_connect_gpio_out_named(DEVICE(ebus), "isa-irq", 12, + qdev_get_gpio_in_named(DEVICE(apb), "pbm-irq", OBIO_MSE_IRQ)); + qdev_connect_gpio_out_named(DEVICE(ebus), "isa-irq", 4, + qdev_get_gpio_in_named(DEVICE(apb), "pbm-irq", OBIO_SER_IRQ)); pci_dev = pci_create_simple(pci_busA, PCI_DEVFN(2, 0), "VGA"); @@ -516,24 +567,6 @@ static void sun4uv_init(MemoryRegion *address_space_mem, qdev_init_nofail(&pci_dev->qdev); pci_ide_create_devs(pci_dev, hd); - isa_create_simple(isa_bus, "i8042"); - - /* Floppy */ - for(i = 0; i < MAX_FD; i++) { - fd[i] = drive_get(IF_FLOPPY, 0, i); - } - dev = DEVICE(isa_create(isa_bus, TYPE_ISA_FDC)); - if (fd[0]) { - qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fd[0]), - &error_abort); - } - if (fd[1]) { - qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fd[1]), - &error_abort); - } - qdev_prop_set_uint32(dev, "dma", -1); - qdev_init_nofail(dev); - /* Map NVRAM into I/O (ebus) space */ nvram = m48t59_init(NULL, 0, 0, NVRAM_SIZE, 1968, 59); s = SYS_BUS_DEVICE(nvram); diff --git a/hw/sparc64/sun4u_iommu.c b/hw/sparc64/sun4u_iommu.c new file mode 100644 index 0000000..4cf8e69 --- /dev/null +++ b/hw/sparc64/sun4u_iommu.c @@ -0,0 +1,342 @@ +/* + * QEMU sun4u IOMMU emulation + * + * Copyright (c) 2006 Fabrice Bellard + * Copyright (c) 2012,2013 Artyom Tarasenko + * Copyright (c) 2017 Mark Cave-Ayland + * + * 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 "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/sparc/sun4u_iommu.h" +#include "exec/address-spaces.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "trace.h" + + +#define IOMMU_PAGE_SIZE_8K (1ULL << 13) +#define IOMMU_PAGE_MASK_8K (~(IOMMU_PAGE_SIZE_8K - 1)) +#define IOMMU_PAGE_SIZE_64K (1ULL << 16) +#define IOMMU_PAGE_MASK_64K (~(IOMMU_PAGE_SIZE_64K - 1)) + +#define IOMMU_CTRL 0x0 +#define IOMMU_CTRL_TBW_SIZE (1ULL << 2) +#define IOMMU_CTRL_MMU_EN (1ULL) + +#define IOMMU_CTRL_TSB_SHIFT 16 + +#define IOMMU_BASE 0x8 +#define IOMMU_FLUSH 0x10 + +#define IOMMU_TTE_DATA_V (1ULL << 63) +#define IOMMU_TTE_DATA_SIZE (1ULL << 61) +#define IOMMU_TTE_DATA_W (1ULL << 1) + +#define IOMMU_TTE_PHYS_MASK_8K 0x1ffffffe000ULL +#define IOMMU_TTE_PHYS_MASK_64K 0x1ffffff8000ULL + +#define IOMMU_TSB_8K_OFFSET_MASK_8M 0x00000000007fe000ULL +#define IOMMU_TSB_8K_OFFSET_MASK_16M 0x0000000000ffe000ULL +#define IOMMU_TSB_8K_OFFSET_MASK_32M 0x0000000001ffe000ULL +#define IOMMU_TSB_8K_OFFSET_MASK_64M 0x0000000003ffe000ULL +#define IOMMU_TSB_8K_OFFSET_MASK_128M 0x0000000007ffe000ULL +#define IOMMU_TSB_8K_OFFSET_MASK_256M 0x000000000fffe000ULL +#define IOMMU_TSB_8K_OFFSET_MASK_512M 0x000000001fffe000ULL +#define IOMMU_TSB_8K_OFFSET_MASK_1G 0x000000003fffe000ULL + +#define IOMMU_TSB_64K_OFFSET_MASK_64M 0x0000000003ff0000ULL +#define IOMMU_TSB_64K_OFFSET_MASK_128M 0x0000000007ff0000ULL +#define IOMMU_TSB_64K_OFFSET_MASK_256M 0x000000000fff0000ULL +#define IOMMU_TSB_64K_OFFSET_MASK_512M 0x000000001fff0000ULL +#define IOMMU_TSB_64K_OFFSET_MASK_1G 0x000000003fff0000ULL +#define IOMMU_TSB_64K_OFFSET_MASK_2G 0x000000007fff0000ULL + + +/* Called from RCU critical section */ +static IOMMUTLBEntry sun4u_translate_iommu(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flag) +{ + IOMMUState *is = container_of(iommu, IOMMUState, iommu); + hwaddr baseaddr, offset; + uint64_t tte; + uint32_t tsbsize; + IOMMUTLBEntry ret = { + .target_as = &address_space_memory, + .iova = 0, + .translated_addr = 0, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + + if (!(is->regs[IOMMU_CTRL >> 3] & IOMMU_CTRL_MMU_EN)) { + /* IOMMU disabled, passthrough using standard 8K page */ + ret.iova = addr & IOMMU_PAGE_MASK_8K; + ret.translated_addr = addr; + ret.addr_mask = IOMMU_PAGE_MASK_8K; + ret.perm = IOMMU_RW; + + return ret; + } + + baseaddr = is->regs[IOMMU_BASE >> 3]; + tsbsize = (is->regs[IOMMU_CTRL >> 3] >> IOMMU_CTRL_TSB_SHIFT) & 0x7; + + if (is->regs[IOMMU_CTRL >> 3] & IOMMU_CTRL_TBW_SIZE) { + /* 64K */ + switch (tsbsize) { + case 0: + offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_64M) >> 13; + break; + case 1: + offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_128M) >> 13; + break; + case 2: + offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_256M) >> 13; + break; + case 3: + offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_512M) >> 13; + break; + case 4: + offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_1G) >> 13; + break; + case 5: + offset = (addr & IOMMU_TSB_64K_OFFSET_MASK_2G) >> 13; + break; + default: + /* Not implemented, error */ + return ret; + } + } else { + /* 8K */ + switch (tsbsize) { + case 0: + offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_8M) >> 10; + break; + case 1: + offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_16M) >> 10; + break; + case 2: + offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_32M) >> 10; + break; + case 3: + offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_64M) >> 10; + break; + case 4: + offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_128M) >> 10; + break; + case 5: + offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_256M) >> 10; + break; + case 6: + offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_512M) >> 10; + break; + case 7: + offset = (addr & IOMMU_TSB_8K_OFFSET_MASK_1G) >> 10; + break; + } + } + + tte = address_space_ldq_be(&address_space_memory, baseaddr + offset, + MEMTXATTRS_UNSPECIFIED, NULL); + + if (!(tte & IOMMU_TTE_DATA_V)) { + /* Invalid mapping */ + return ret; + } + + if (tte & IOMMU_TTE_DATA_W) { + /* Writeable */ + ret.perm = IOMMU_RW; + } else { + ret.perm = IOMMU_RO; + } + + /* Extract phys */ + if (tte & IOMMU_TTE_DATA_SIZE) { + /* 64K */ + ret.iova = addr & IOMMU_PAGE_MASK_64K; + ret.translated_addr = tte & IOMMU_TTE_PHYS_MASK_64K; + ret.addr_mask = (IOMMU_PAGE_SIZE_64K - 1); + } else { + /* 8K */ + ret.iova = addr & IOMMU_PAGE_MASK_8K; + ret.translated_addr = tte & IOMMU_TTE_PHYS_MASK_8K; + ret.addr_mask = (IOMMU_PAGE_SIZE_8K - 1); + } + + trace_sun4u_iommu_translate(ret.iova, ret.translated_addr, tte); + + return ret; +} + +static void iommu_mem_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + IOMMUState *is = opaque; + + trace_sun4u_iommu_mem_write(addr, val, size); + + switch (addr) { + case IOMMU_CTRL: + if (size == 4) { + is->regs[IOMMU_CTRL >> 3] &= 0xffffffffULL; + is->regs[IOMMU_CTRL >> 3] |= val << 32; + } else { + is->regs[IOMMU_CTRL >> 3] = val; + } + break; + case IOMMU_CTRL + 0x4: + is->regs[IOMMU_CTRL >> 3] &= 0xffffffff00000000ULL; + is->regs[IOMMU_CTRL >> 3] |= val & 0xffffffffULL; + break; + case IOMMU_BASE: + if (size == 4) { + is->regs[IOMMU_BASE >> 3] &= 0xffffffffULL; + is->regs[IOMMU_BASE >> 3] |= val << 32; + } else { + is->regs[IOMMU_BASE >> 3] = val; + } + break; + case IOMMU_BASE + 0x4: + is->regs[IOMMU_BASE >> 3] &= 0xffffffff00000000ULL; + is->regs[IOMMU_BASE >> 3] |= val & 0xffffffffULL; + break; + case IOMMU_FLUSH: + case IOMMU_FLUSH + 0x4: + break; + default: + qemu_log_mask(LOG_UNIMP, + "sun4u-iommu: Unimplemented register write " + "reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n", + addr, size, val); + break; + } +} + +static uint64_t iommu_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + IOMMUState *is = opaque; + uint64_t val; + + switch (addr) { + case IOMMU_CTRL: + if (size == 4) { + val = is->regs[IOMMU_CTRL >> 3] >> 32; + } else { + val = is->regs[IOMMU_CTRL >> 3]; + } + break; + case IOMMU_CTRL + 0x4: + val = is->regs[IOMMU_CTRL >> 3] & 0xffffffffULL; + break; + case IOMMU_BASE: + if (size == 4) { + val = is->regs[IOMMU_BASE >> 3] >> 32; + } else { + val = is->regs[IOMMU_BASE >> 3]; + } + break; + case IOMMU_BASE + 0x4: + val = is->regs[IOMMU_BASE >> 3] & 0xffffffffULL; + break; + case IOMMU_FLUSH: + case IOMMU_FLUSH + 0x4: + val = 0; + break; + default: + qemu_log_mask(LOG_UNIMP, + "sun4u-iommu: Unimplemented register read " + "reg 0x%" HWADDR_PRIx " size 0x%x\n", + addr, size); + val = 0; + break; + } + + trace_sun4u_iommu_mem_read(addr, val, size); + + return val; +} + +static const MemoryRegionOps iommu_mem_ops = { + .read = iommu_mem_read, + .write = iommu_mem_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void iommu_reset(DeviceState *d) +{ + IOMMUState *s = SUN4U_IOMMU(d); + + memset(s->regs, 0, IOMMU_NREGS * sizeof(uint64_t)); +} + +static void iommu_init(Object *obj) +{ + IOMMUState *s = SUN4U_IOMMU(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_iommu(&s->iommu, sizeof(s->iommu), + TYPE_SUN4U_IOMMU_MEMORY_REGION, OBJECT(s), + "iommu-sun4u", UINT64_MAX); + address_space_init(&s->iommu_as, MEMORY_REGION(&s->iommu), "iommu-as"); + + memory_region_init_io(&s->iomem, obj, &iommu_mem_ops, s, "iommu", + IOMMU_NREGS * sizeof(uint64_t)); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void iommu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = iommu_reset; +} + +static const TypeInfo iommu_info = { + .name = TYPE_SUN4U_IOMMU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IOMMUState), + .instance_init = iommu_init, + .class_init = iommu_class_init, +}; + +static void sun4u_iommu_memory_region_class_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = sun4u_translate_iommu; +} + +static const TypeInfo sun4u_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_SUN4U_IOMMU_MEMORY_REGION, + .class_init = sun4u_iommu_memory_region_class_init, +}; + +static void iommu_register_types(void) +{ + type_register_static(&iommu_info); + type_register_static(&sun4u_iommu_memory_region_info); +} + +type_init(iommu_register_types) diff --git a/hw/sparc64/trace-events b/hw/sparc64/trace-events new file mode 100644 index 0000000..2ee2d75 --- /dev/null +++ b/hw/sparc64/trace-events @@ -0,0 +1,9 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# hw/sparc64/sun4u.c +ebus_isa_irq_handler(int n, int level) "Set ISA IRQ %d level %d" + +# hw/sparc64/sun4u_iommu.c +sun4u_iommu_mem_read(uint64_t addr, uint64_t val, int size) "addr: 0x%"PRIx64" val: 0x%"PRIx64" size: %d" +sun4u_iommu_mem_write(uint64_t addr, uint64_t val, int size) "addr: 0x%"PRIx64" val: 0x%"PRIx64" size: %d" +sun4u_iommu_translate(uint64_t addr, uint64_t trans_addr, uint64_t tte) "xlate 0x%"PRIx64" => pa 0x%"PRIx64" tte: 0x%"PRIx64 |