diff options
author | Avi Kivity <avi@redhat.com> | 2011-10-09 13:11:50 +0200 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2011-10-09 13:11:50 +0200 |
commit | df2921d326d0903ff684759ac8508f87396b7018 (patch) | |
tree | b9f9d2a2a116413591362278b2dc9d148c7ee943 | |
parent | 306f66b42f961e38442d1721523dbb4906b33afb (diff) | |
parent | 02d6516c8ba00bdd6d96b622f000cb28c3449f43 (diff) | |
download | qemu-df2921d326d0903ff684759ac8508f87396b7018.zip qemu-df2921d326d0903ff684759ac8508f87396b7018.tar.gz qemu-df2921d326d0903ff684759ac8508f87396b7018.tar.bz2 |
Merge remote-tracking branch 'upstream' into memory/batch
* upstream: (87 commits)
target-alpha: Fix compilation errors for 32 bit hosts
target-alpha: Add high-resolution access to wall clock and an alarm.
target-alpha: Implement HALT IPR.
target-alpha: Implement WAIT IPR.
target-alpha: Add CLIPPER emulation.
target-alpha: Add custom PALcode image for CLIPPER emulation.
target-alpha: Honor icount for RPCC instruction.
tcg/s390: Remove unused tcg_out_addi()
tcg/ia64: Remove unused tcg_out_addi()
ARM: fix segfault
ppc64: Fix linker script
pseries: Implement set-time-of-day RTAS function
pseries: Refactor spapr irq allocation
PPC: Clean up BookE timer code
PPC: booke timers
KVM: PPC: Use HIOR setting for -M pseries with PR KVM
KVM: Update kernel headers
KVM: Update kernel headers
PPC: Fix heathrow PIC to use little endian MMIO
PPC: Fix via-cuda memory registration
...
Conflicts:
hw/milkymist-uart.c
hw/ppce500_mpc8544ds.c
Signed-off-by: Avi Kivity <avi@redhat.com>
78 files changed, 3465 insertions, 660 deletions
diff --git a/.gitmodules b/.gitmodules index c3faa38..2a43dbc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "roms/openbios"] path = roms/openbios url = git://git.qemu.org/openbios.git +[submodule "roms/qemu-palcode"] + path = roms/qemu-palcode + url = git://repo.or.cz/qemu-palcode.git @@ -251,7 +251,8 @@ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ mpc8544ds.dtb \ multiboot.bin linuxboot.bin \ s390-zipl.rom \ -spapr-rtas.bin slof.bin +spapr-rtas.bin slof.bin \ +palcode-clipper else BLOBS= endif diff --git a/Makefile.objs b/Makefile.objs index 8d23fbb..c849e51 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -222,7 +222,6 @@ hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o # PPC devices -hw-obj-$(CONFIG_OPENPIC) += openpic.o hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o # Mac shared devices hw-obj-$(CONFIG_MACIO) += macio.o diff --git a/Makefile.target b/Makefile.target index 88d2f1f..1aa6fce 100644 --- a/Makefile.target +++ b/Makefile.target @@ -229,7 +229,7 @@ obj-i386-$(CONFIG_KVM) += kvmclock.o obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o # shared objects -obj-ppc-y = ppc.o +obj-ppc-y = ppc.o ppc_booke.o obj-ppc-y += vga.o # PREP target obj-ppc-y += i8259.o mc146818rtc.o @@ -239,19 +239,19 @@ obj-ppc-y += ppc_oldworld.o # NewWorld PowerMac obj-ppc-y += ppc_newworld.o # IBM pSeries (sPAPR) -ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy) -obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o -obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o -endif +obj-ppc-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o +obj-ppc-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o # PowerPC 4xx boards obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-ppc-y += ppc440.o ppc440_bamboo.o # PowerPC E500 boards -obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o +obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o # PowerPC 440 Xilinx ML507 reference board. obj-ppc-y += virtex_ml507.o obj-ppc-$(CONFIG_KVM) += kvm_ppc.o obj-ppc-$(CONFIG_FDT) += device_tree.o +# PowerPC OpenPIC +obj-ppc-y += openpic.o # Xilinx PPC peripherals obj-ppc-y += xilinx_intc.o @@ -367,6 +367,7 @@ obj-s390x-y = s390-virtio-bus.o s390-virtio.o obj-alpha-y = i8259.o mc146818rtc.o obj-alpha-y += vga.o cirrus_vga.o +obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o obj-xtensa-y += xtensa_pic.o obj-xtensa-y += xtensa_sample.o @@ -475,6 +475,8 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename, assert(drv != NULL); + trace_bdrv_open_common(bs, filename, flags, drv->format_name); + bs->file = NULL; bs->total_sectors = 0; bs->encrypted = 0; @@ -2997,7 +2999,7 @@ static int coroutine_fn bdrv_co_io_em(BlockDriverState *bs, int64_t sector_num, bdrv_co_io_em_complete, &co); } - trace_bdrv_co_io(is_write, acb); + trace_bdrv_co_io_em(bs, sector_num, nb_sectors, is_write, acb); if (!acb) { return -EIO; } diff --git a/block/qed-table.c b/block/qed-table.c index d96afa8..f31f9ff 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -222,21 +222,21 @@ static void qed_read_l2_table_cb(void *opaque, int ret) QEDRequest *request = read_l2_table_cb->request; BDRVQEDState *s = read_l2_table_cb->s; CachedL2Table *l2_table = request->l2_table; + uint64_t l2_offset = read_l2_table_cb->l2_offset; if (ret) { /* can't trust loaded L2 table anymore */ qed_unref_l2_cache_entry(l2_table); request->l2_table = NULL; } else { - l2_table->offset = read_l2_table_cb->l2_offset; + l2_table->offset = l2_offset; qed_commit_l2_cache_entry(&s->l2_cache, l2_table); /* This is guaranteed to succeed because we just committed the entry * to the cache. */ - request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, - l2_table->offset); + request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); assert(request->l2_table != NULL); } diff --git a/block/qed.c b/block/qed.c index 624e261..e87dc4d 100644 --- a/block/qed.c +++ b/block/qed.c @@ -911,14 +911,14 @@ static void qed_commit_l2_update(void *opaque, int ret) QEDAIOCB *acb = opaque; BDRVQEDState *s = acb_to_s(acb); CachedL2Table *l2_table = acb->request.l2_table; + uint64_t l2_offset = l2_table->offset; qed_commit_l2_cache_entry(&s->l2_cache, l2_table); /* This is guaranteed to succeed because we just committed the entry to the * cache. */ - acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, - l2_table->offset); + acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset); assert(acb->request.l2_table != NULL); qed_aio_next_io(opaque, ret); @@ -3389,6 +3389,9 @@ case "$target_arch2" in fi fi esac +if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then + echo "CONFIG_PSERIES=y" >> $config_target_mak +fi if test "$target_bigendian" = "yes" ; then echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak fi @@ -3615,7 +3618,13 @@ FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" FILES="$FILES pc-bios/spapr-rtas/Makefile" FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" -for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.rom $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do +for bios_file in \ + $source_path/pc-bios/*.bin \ + $source_path/pc-bios/*.rom \ + $source_path/pc-bios/*.dtb \ + $source_path/pc-bios/openbios-* \ + $source_path/pc-bios/palcode-* +do FILES="$FILES pc-bios/`basename $bios_file`" done mkdir -p $DIRS @@ -217,6 +217,7 @@ int cpu_exec(CPUState *env) #elif defined(TARGET_ARM) #elif defined(TARGET_UNICORE32) #elif defined(TARGET_PPC) + env->reserve_addr = -1; #elif defined(TARGET_LM32) #elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MIPS) diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak index abadcff..be86d0c 100644 --- a/default-configs/alpha-softmmu.mak +++ b/default-configs/alpha-softmmu.mak @@ -3,7 +3,9 @@ include pci.mak CONFIG_SERIAL=y CONFIG_I8254=y +CONFIG_PCKBD=y CONFIG_VGA_PCI=y CONFIG_IDE_CORE=y CONFIG_IDE_QDEV=y CONFIG_VMWARE_VGA=y +CONFIG_IDE_CMD646=y diff --git a/device_tree.c b/device_tree.c index 3a224d1..dc69232 100644 --- a/device_tree.c +++ b/device_tree.c @@ -41,6 +41,7 @@ void *load_device_tree(const char *filename_path, int *sizep) } /* Expand to 2x size to give enough room for manipulation. */ + dt_size += 10000; dt_size *= 2; /* First allocate space in qemu for device tree */ fdt = g_malloc0(dt_size); @@ -72,38 +73,99 @@ fail: return NULL; } -int qemu_devtree_setprop(void *fdt, const char *node_path, - const char *property, void *val_array, int size) +static int findnode_nofail(void *fdt, const char *node_path) { int offset; offset = fdt_path_offset(fdt, node_path); - if (offset < 0) - return offset; + if (offset < 0) { + fprintf(stderr, "%s Couldn't find node %s: %s\n", __func__, node_path, + fdt_strerror(offset)); + exit(1); + } + + return offset; +} + +int qemu_devtree_setprop(void *fdt, const char *node_path, + const char *property, void *val_array, int size) +{ + int r; + + r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val_array, size); + if (r < 0) { + fprintf(stderr, "%s: Couldn't set %s/%s: %s\n", __func__, node_path, + property, fdt_strerror(r)); + exit(1); + } - return fdt_setprop(fdt, offset, property, val_array, size); + return r; } int qemu_devtree_setprop_cell(void *fdt, const char *node_path, const char *property, uint32_t val) { - int offset; + int r; - offset = fdt_path_offset(fdt, node_path); - if (offset < 0) - return offset; + r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val); + if (r < 0) { + fprintf(stderr, "%s: Couldn't set %s/%s = %#08x: %s\n", __func__, + node_path, property, val, fdt_strerror(r)); + exit(1); + } - return fdt_setprop_cell(fdt, offset, property, val); + return r; } int qemu_devtree_setprop_string(void *fdt, const char *node_path, const char *property, const char *string) { - int offset; + int r; - offset = fdt_path_offset(fdt, node_path); - if (offset < 0) - return offset; + r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string); + if (r < 0) { + fprintf(stderr, "%s: Couldn't set %s/%s = %s: %s\n", __func__, + node_path, property, string, fdt_strerror(r)); + exit(1); + } + + return r; +} + +int qemu_devtree_nop_node(void *fdt, const char *node_path) +{ + int r; + + r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path)); + if (r < 0) { + fprintf(stderr, "%s: Couldn't nop node %s: %s\n", __func__, node_path, + fdt_strerror(r)); + exit(1); + } + + return r; +} + +int qemu_devtree_add_subnode(void *fdt, const char *name) +{ + char *dupname = g_strdup(name); + char *basename = strrchr(dupname, '/'); + int retval; + + if (!basename) { + return -1; + } + + basename[0] = '\0'; + basename++; + + retval = fdt_add_subnode(fdt, findnode_nofail(fdt, dupname), basename); + if (retval < 0) { + fprintf(stderr, "FDT: Failed to create subnode %s: %s\n", name, + fdt_strerror(retval)); + exit(1); + } - return fdt_setprop_string(fdt, offset, property, string); + g_free(dupname); + return retval; } diff --git a/device_tree.h b/device_tree.h index cecd98f..4378685 100644 --- a/device_tree.h +++ b/device_tree.h @@ -22,5 +22,7 @@ int qemu_devtree_setprop_cell(void *fdt, const char *node_path, const char *property, uint32_t val); int qemu_devtree_setprop_string(void *fdt, const char *node_path, const char *property, const char *string); +int qemu_devtree_nop_node(void *fdt, const char *node_path); +int qemu_devtree_add_subnode(void *fdt, const char *name); #endif /* __DEVICE_TREE_H__ */ @@ -733,7 +733,7 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) { if (gdb_has_xml) return 0; - GET_REG32(0); /* fpscr */ + GET_REG32(env->fpscr); } } } diff --git a/hmp-commands.hx b/hmp-commands.hx index 9e1cca8..a8ece43 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -194,7 +194,7 @@ STEXI changes status of a trace event ETEXI -#if defined(CONFIG_SIMPLE_TRACE) +#if defined(CONFIG_TRACE_SIMPLE) { .name = "trace-file", .args_type = "op:s?,arg:F?", @@ -1306,7 +1306,7 @@ show i8259 (PIC) state @item info pci show emulated PCI device info @item info tlb -show virtual to physical memory mappings (i386, SH4 and SPARC only) +show virtual to physical memory mappings (i386, SH4, SPARC, and PPC only) @item info mem show the active virtual memory mappings (i386 only) @item info jit @@ -22,7 +22,7 @@ * THE SOFTWARE. */ #include "hw.h" -#include "ppc_mac.h" +#include "adb.h" #include "console.h" /* debug ADB */ diff --git a/hw/adb.h b/hw/adb.h new file mode 100644 index 0000000..b2a591c --- /dev/null +++ b/hw/adb.h @@ -0,0 +1,67 @@ +/* + * QEMU ADB emulation shared definitions and prototypes + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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. + */ + +#if !defined(__ADB_H__) +#define __ADB_H__ + +#define MAX_ADB_DEVICES 16 + +#define ADB_MAX_OUT_LEN 16 + +typedef struct ADBDevice ADBDevice; + +/* buf = NULL means polling */ +typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, + const uint8_t *buf, int len); +typedef int ADBDeviceReset(ADBDevice *d); + +struct ADBDevice { + struct ADBBusState *bus; + int devaddr; + int handler; + ADBDeviceRequest *devreq; + ADBDeviceReset *devreset; + void *opaque; +}; + +typedef struct ADBBusState { + ADBDevice devices[MAX_ADB_DEVICES]; + int nb_devices; + int poll_index; +} ADBBusState; + +int adb_request(ADBBusState *s, uint8_t *buf_out, + const uint8_t *buf, int len); +int adb_poll(ADBBusState *s, uint8_t *buf_out); + +ADBDevice *adb_register_device(ADBBusState *s, int devaddr, + ADBDeviceRequest *devreq, + ADBDeviceReset *devreset, + void *opaque); +void adb_kbd_init(ADBBusState *bus); +void adb_mouse_init(ADBBusState *bus); + +extern ADBBusState adb_bus; +#endif /* !defined(__ADB_H__) */ diff --git a/hw/alpha_dp264.c b/hw/alpha_dp264.c new file mode 100644 index 0000000..fcc20e9 --- /dev/null +++ b/hw/alpha_dp264.c @@ -0,0 +1,177 @@ +/* + * QEMU Alpha DP264/CLIPPER hardware system emulator. + * + * Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK + * variants because CLIPPER doesn't have an SMC669 SuperIO controler + * that we need to emulate as well. + */ + +#include "hw.h" +#include "elf.h" +#include "loader.h" +#include "boards.h" +#include "alpha_sys.h" +#include "sysemu.h" +#include "mc146818rtc.h" +#include "ide.h" + +#define MAX_IDE_BUS 2 + +static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr) +{ + if (((addr >> 41) & 3) == 2) { + addr &= 0xffffffffffull; + } + return addr; +} + +/* Note that there are at least 3 viewpoints of IRQ numbers on Alpha systems. + (0) The dev_irq_n lines into the cpu, which we totally ignore, + (1) The DRIR lines in the typhoon chipset, + (2) The "vector" aka mangled interrupt number reported by SRM PALcode, + (3) The interrupt number assigned by the kernel. + The following function is concerned with (1) only. */ + +static int clipper_pci_map_irq(PCIDevice *d, int irq_num) +{ + int slot = d->devfn >> 3; + + assert(irq_num >= 0 && irq_num <= 3); + + return (slot + 1) * 4 + irq_num; +} + +static void clipper_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *cpus[4]; + PCIBus *pci_bus; + qemu_irq rtc_irq; + long size, i; + const char *palcode_filename; + uint64_t palcode_entry, palcode_low, palcode_high; + uint64_t kernel_entry, kernel_low, kernel_high; + + /* Create up to 4 cpus. */ + memset(cpus, 0, sizeof(cpus)); + for (i = 0; i < smp_cpus; ++i) { + cpus[i] = cpu_init(cpu_model ? cpu_model : "ev67"); + } + + cpus[0]->trap_arg0 = ram_size; + cpus[0]->trap_arg1 = 0; + cpus[0]->trap_arg2 = smp_cpus; + + /* Init the chipset. */ + pci_bus = typhoon_init(ram_size, &rtc_irq, cpus, clipper_pci_map_irq); + + rtc_init(1980, rtc_irq); + pit_init(0x40, 0); + isa_create_simple("i8042"); + + /* VGA setup. Don't bother loading the bios. */ + alpha_pci_vga_setup(pci_bus); + + /* Serial code setup. */ + for (i = 0; i < MAX_SERIAL_PORTS; ++i) { + if (serial_hds[i]) { + serial_isa_init(i, serial_hds[i]); + } + } + + /* Network setup. e1000 is good enough, failing Tulip support. */ + for (i = 0; i < nb_nics; i++) { + pci_nic_init_nofail(&nd_table[i], "e1000", NULL); + } + + /* IDE disk setup. */ + { + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + ide_drive_get(hd, MAX_IDE_BUS); + + pci_cmd646_ide_init(pci_bus, hd, 0); + } + + /* Load PALcode. Given that this is not "real" cpu palcode, + but one explicitly written for the emulation, we might as + well load it directly from and ELF image. */ + palcode_filename = (bios_name ? bios_name : "palcode-clipper"); + palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename); + if (palcode_filename == NULL) { + hw_error("no palcode provided\n"); + exit(1); + } + size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys, + NULL, &palcode_entry, &palcode_low, &palcode_high, + 0, EM_ALPHA, 0); + if (size < 0) { + hw_error("could not load palcode '%s'\n", palcode_filename); + exit(1); + } + + /* Start all cpus at the PALcode RESET entry point. */ + for (i = 0; i < smp_cpus; ++i) { + cpus[i]->pal_mode = 1; + cpus[i]->pc = palcode_entry; + cpus[i]->palbr = palcode_entry; + } + + /* Load a kernel. */ + if (kernel_filename) { + uint64_t param_offset; + + size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys, + NULL, &kernel_entry, &kernel_low, &kernel_high, + 0, EM_ALPHA, 0); + if (size < 0) { + hw_error("could not load kernel '%s'\n", kernel_filename); + exit(1); + } + + cpus[0]->trap_arg1 = kernel_entry; + + param_offset = kernel_low - 0x6000; + + if (kernel_cmdline) { + pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline); + } + + if (initrd_filename) { + long initrd_base, initrd_size; + + initrd_size = get_image_size(initrd_filename); + if (initrd_size < 0) { + hw_error("could not load initial ram disk '%s'\n", + initrd_filename); + exit(1); + } + + /* Put the initrd image as high in memory as possible. */ + initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; + load_image_targphys(initrd_filename, initrd_base, + ram_size - initrd_base); + + stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000ULL); + stq_phys(param_offset + 0x108, initrd_size); + } + } +} + +static QEMUMachine clipper_machine = { + .name = "clipper", + .desc = "Alpha DP264/CLIPPER", + .init = clipper_init, + .max_cpus = 4, + .is_default = 1, +}; + +static void clipper_machine_init(void) +{ + qemu_register_machine(&clipper_machine); +} + +machine_init(clipper_machine_init); diff --git a/hw/alpha_pci.c b/hw/alpha_pci.c new file mode 100644 index 0000000..e975702 --- /dev/null +++ b/hw/alpha_pci.c @@ -0,0 +1,134 @@ +/* + * QEMU Alpha PCI support functions. + * + * Some of this isn't very Alpha specific at all. + * + * ??? Sparse memory access not implemented. + */ + +#include "config.h" +#include "alpha_sys.h" +#include "qemu-log.h" +#include "sysemu.h" +#include "vmware_vga.h" + + +/* PCI IO reads/writes, to byte-word addressable memory. */ +/* ??? Doesn't handle multiple PCI busses. */ + +static uint64_t bw_io_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + switch (size) { + case 1: + return cpu_inb(addr); + case 2: + return cpu_inw(addr); + case 4: + return cpu_inl(addr); + } + abort(); +} + +static void bw_io_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + switch (size) { + case 1: + cpu_outb(addr, val); + break; + case 2: + cpu_outw(addr, val); + break; + case 4: + cpu_outl(addr, val); + break; + default: + abort(); + } +} + +const MemoryRegionOps alpha_pci_bw_io_ops = { + .read = bw_io_read, + .write = bw_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +/* PCI config space reads/writes, to byte-word addressable memory. */ +static uint64_t bw_conf1_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + PCIBus *b = opaque; + return pci_data_read(b, addr, size); +} + +static void bw_conf1_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + PCIBus *b = opaque; + pci_data_write(b, addr, val, size); +} + +const MemoryRegionOps alpha_pci_conf1_ops = { + .read = bw_conf1_read, + .write = bw_conf1_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +/* PCI/EISA Interrupt Acknowledge Cycle. */ + +static uint64_t iack_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + return pic_read_irq(isa_pic); +} + +static void special_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + qemu_log("pci: special write cycle"); +} + +const MemoryRegionOps alpha_pci_iack_ops = { + .read = iack_read, + .write = special_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +void alpha_pci_vga_setup(PCIBus *pci_bus) +{ + switch (vga_interface_type) { +#ifdef CONFIG_SPICE + case VGA_QXL: + pci_create_simple(pci_bus, -1, "qxl-vga"); + return; +#endif + case VGA_CIRRUS: + pci_cirrus_vga_init(pci_bus); + return; + case VGA_VMWARE: + if (pci_vmsvga_init(pci_bus)) { + return; + } + break; + } + /* If VGA is enabled at all, and one of the above didn't work, then + fallback to Standard VGA. */ + if (vga_interface_type != VGA_NONE) { + pci_vga_init(pci_bus); + } +} diff --git a/hw/alpha_sys.h b/hw/alpha_sys.h new file mode 100644 index 0000000..13f0177 --- /dev/null +++ b/hw/alpha_sys.h @@ -0,0 +1,24 @@ +/* Alpha cores and system support chips. */ + +#ifndef HW_ALPHA_H +#define HW_ALPHA_H 1 + +#include "pci.h" +#include "pci_host.h" +#include "ide.h" +#include "net.h" +#include "pc.h" +#include "usb-ohci.h" +#include "irq.h" + + +PCIBus *typhoon_init(ram_addr_t, qemu_irq *, CPUState *[4], pci_map_irq_fn); + +/* alpha_pci.c. */ +extern const MemoryRegionOps alpha_pci_bw_io_ops; +extern const MemoryRegionOps alpha_pci_conf1_ops; +extern const MemoryRegionOps alpha_pci_iack_ops; + +void alpha_pci_vga_setup(PCIBus *pci_bus); + +#endif diff --git a/hw/alpha_typhoon.c b/hw/alpha_typhoon.c new file mode 100644 index 0000000..c7608bb --- /dev/null +++ b/hw/alpha_typhoon.c @@ -0,0 +1,820 @@ +/* + * DEC 21272 (TSUNAMI/TYPHOON) chipset emulation. + * + * Written by Richard Henderson. + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "cpu.h" +#include "exec-all.h" +#include "hw.h" +#include "devices.h" +#include "sysemu.h" +#include "alpha_sys.h" +#include "exec-memory.h" + + +typedef struct TyphoonCchip { + MemoryRegion region; + uint64_t misc; + uint64_t drir; + uint64_t dim[4]; + uint32_t iic[4]; + CPUState *cpu[4]; +} TyphoonCchip; + +typedef struct TyphoonWindow { + uint32_t base_addr; + uint32_t mask; + uint32_t translated_base_pfn; +} TyphoonWindow; + +typedef struct TyphoonPchip { + MemoryRegion region; + MemoryRegion reg_iack; + MemoryRegion reg_mem; + MemoryRegion reg_io; + MemoryRegion reg_conf; + uint64_t ctl; + TyphoonWindow win[4]; +} TyphoonPchip; + +typedef struct TyphoonState { + PCIHostState host; + TyphoonCchip cchip; + TyphoonPchip pchip; + MemoryRegion dchip_region; + MemoryRegion ram_region; + + /* QEMU emulation state. */ + uint32_t latch_tmp; +} TyphoonState; + +/* Called when one of DRIR or DIM changes. */ +static void cpu_irq_change(CPUState *env, uint64_t req) +{ + /* If there are any non-masked interrupts, tell the cpu. */ + if (env) { + if (req) { + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } + } +} + +static uint64_t cchip_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + CPUState *env = cpu_single_env; + TyphoonState *s = opaque; + uint64_t ret = 0; + + if (addr & 4) { + return s->latch_tmp; + } + + switch (addr) { + case 0x0000: + /* CSC: Cchip System Configuration Register. */ + /* All sorts of data here; probably the only thing relevant is + PIP<14> Pchip 1 Present = 0. */ + break; + + case 0x0040: + /* MTR: Memory Timing Register. */ + /* All sorts of stuff related to real DRAM. */ + break; + + case 0x0080: + /* MISC: Miscellaneous Register. */ + ret = s->cchip.misc | (env->cpu_index & 3); + break; + + case 0x00c0: + /* MPD: Memory Presence Detect Register. */ + break; + + case 0x0100: /* AAR0 */ + case 0x0140: /* AAR1 */ + case 0x0180: /* AAR2 */ + case 0x01c0: /* AAR3 */ + /* AAR: Array Address Register. */ + /* All sorts of information about DRAM. */ + break; + + case 0x0200: + /* DIM0: Device Interrupt Mask Register, CPU0. */ + ret = s->cchip.dim[0]; + break; + case 0x0240: + /* DIM1: Device Interrupt Mask Register, CPU1. */ + ret = s->cchip.dim[1]; + break; + case 0x0280: + /* DIR0: Device Interrupt Request Register, CPU0. */ + ret = s->cchip.dim[0] & s->cchip.drir; + break; + case 0x02c0: + /* DIR1: Device Interrupt Request Register, CPU1. */ + ret = s->cchip.dim[1] & s->cchip.drir; + break; + case 0x0300: + /* DRIR: Device Raw Interrupt Request Register. */ + ret = s->cchip.drir; + break; + + case 0x0340: + /* PRBEN: Probe Enable Register. */ + break; + + case 0x0380: + /* IIC0: Interval Ignore Count Register, CPU0. */ + ret = s->cchip.iic[0]; + break; + case 0x03c0: + /* IIC1: Interval Ignore Count Register, CPU1. */ + ret = s->cchip.iic[1]; + break; + + case 0x0400: /* MPR0 */ + case 0x0440: /* MPR1 */ + case 0x0480: /* MPR2 */ + case 0x04c0: /* MPR3 */ + /* MPR: Memory Programming Register. */ + break; + + case 0x0580: + /* TTR: TIGbus Timing Register. */ + /* All sorts of stuff related to interrupt delivery timings. */ + break; + case 0x05c0: + /* TDR: TIGbug Device Timing Register. */ + break; + + case 0x0600: + /* DIM2: Device Interrupt Mask Register, CPU2. */ + ret = s->cchip.dim[2]; + break; + case 0x0640: + /* DIM3: Device Interrupt Mask Register, CPU3. */ + ret = s->cchip.dim[3]; + break; + case 0x0680: + /* DIR2: Device Interrupt Request Register, CPU2. */ + ret = s->cchip.dim[2] & s->cchip.drir; + break; + case 0x06c0: + /* DIR3: Device Interrupt Request Register, CPU3. */ + ret = s->cchip.dim[3] & s->cchip.drir; + break; + + case 0x0700: + /* IIC2: Interval Ignore Count Register, CPU2. */ + ret = s->cchip.iic[2]; + break; + case 0x0740: + /* IIC3: Interval Ignore Count Register, CPU3. */ + ret = s->cchip.iic[3]; + break; + + case 0x0780: + /* PWR: Power Management Control. */ + break; + + case 0x0c00: /* CMONCTLA */ + case 0x0c40: /* CMONCTLB */ + case 0x0c80: /* CMONCNT01 */ + case 0x0cc0: /* CMONCNT23 */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); + return -1; + } + + s->latch_tmp = ret >> 32; + return ret; +} + +static uint64_t dchip_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + /* Skip this. It's all related to DRAM timing and setup. */ + return 0; +} + +static uint64_t pchip_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t ret = 0; + + if (addr & 4) { + return s->latch_tmp; + } + + switch (addr) { + case 0x0000: + /* WSBA0: Window Space Base Address Register. */ + ret = s->pchip.win[0].base_addr; + break; + case 0x0040: + /* WSBA1 */ + ret = s->pchip.win[1].base_addr; + break; + case 0x0080: + /* WSBA2 */ + ret = s->pchip.win[2].base_addr; + break; + case 0x00c0: + /* WSBA3 */ + ret = s->pchip.win[3].base_addr; + break; + + case 0x0100: + /* WSM0: Window Space Mask Register. */ + ret = s->pchip.win[0].mask; + break; + case 0x0140: + /* WSM1 */ + ret = s->pchip.win[1].mask; + break; + case 0x0180: + /* WSM2 */ + ret = s->pchip.win[2].mask; + break; + case 0x01c0: + /* WSM3 */ + ret = s->pchip.win[3].mask; + break; + + case 0x0200: + /* TBA0: Translated Base Address Register. */ + ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10; + break; + case 0x0240: + /* TBA1 */ + ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10; + break; + case 0x0280: + /* TBA2 */ + ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10; + break; + case 0x02c0: + /* TBA3 */ + ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10; + break; + + case 0x0300: + /* PCTL: Pchip Control Register. */ + ret = s->pchip.ctl; + break; + case 0x0340: + /* PLAT: Pchip Master Latency Register. */ + break; + case 0x03c0: + /* PERROR: Pchip Error Register. */ + break; + case 0x0400: + /* PERRMASK: Pchip Error Mask Register. */ + break; + case 0x0440: + /* PERRSET: Pchip Error Set Register. */ + break; + case 0x0480: + /* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */ + break; + case 0x04c0: + /* TLBIA: Translation Buffer Invalidate All Register (WO). */ + break; + case 0x0500: /* PMONCTL */ + case 0x0540: /* PMONCNT */ + case 0x0800: /* SPRST */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size); + return -1; + } + + s->latch_tmp = ret >> 32; + return ret; +} + +static void cchip_write(void *opaque, target_phys_addr_t addr, + uint64_t v32, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t val, oldval, newval; + + if (addr & 4) { + val = v32 << 32 | s->latch_tmp; + addr ^= 4; + } else { + s->latch_tmp = v32; + return; + } + + switch (addr) { + case 0x0000: + /* CSC: Cchip System Configuration Register. */ + /* All sorts of data here; nothing relevant RW. */ + break; + + case 0x0040: + /* MTR: Memory Timing Register. */ + /* All sorts of stuff related to real DRAM. */ + break; + + case 0x0080: + /* MISC: Miscellaneous Register. */ + newval = oldval = s->cchip.misc; + newval &= ~(val & 0x10000ff0); /* W1C fields */ + if (val & 0x100000) { + newval &= ~0xff0000ull; /* ACL clears ABT and ABW */ + } else { + newval |= val & 0x00f00000; /* ABT field is W1S */ + if ((newval & 0xf0000) == 0) { + newval |= val & 0xf0000; /* ABW field is W1S iff zero */ + } + } + newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */ + + newval &= ~0xf0000000000ull; /* WO and RW fields */ + newval |= val & 0xf0000000000ull; + s->cchip.misc = newval; + + /* Pass on changes to IPI and ITI state. */ + if ((newval ^ oldval) & 0xff0) { + int i; + for (i = 0; i < 4; ++i) { + CPUState *env = s->cchip.cpu[i]; + if (env) { + /* IPI can be either cleared or set by the write. */ + if (newval & (1 << (i + 8))) { + cpu_interrupt(env, CPU_INTERRUPT_SMP); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_SMP); + } + + /* ITI can only be cleared by the write. */ + if ((newval & (1 << (i + 4))) == 0) { + cpu_reset_interrupt(env, CPU_INTERRUPT_TIMER); + } + } + } + } + break; + + case 0x00c0: + /* MPD: Memory Presence Detect Register. */ + break; + + case 0x0100: /* AAR0 */ + case 0x0140: /* AAR1 */ + case 0x0180: /* AAR2 */ + case 0x01c0: /* AAR3 */ + /* AAR: Array Address Register. */ + /* All sorts of information about DRAM. */ + break; + + case 0x0200: /* DIM0 */ + /* DIM: Device Interrupt Mask Register, CPU0. */ + s->cchip.dim[0] = val; + cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir); + break; + case 0x0240: /* DIM1 */ + /* DIM: Device Interrupt Mask Register, CPU1. */ + s->cchip.dim[0] = val; + cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir); + break; + + case 0x0280: /* DIR0 (RO) */ + case 0x02c0: /* DIR1 (RO) */ + case 0x0300: /* DRIR (RO) */ + break; + + case 0x0340: + /* PRBEN: Probe Enable Register. */ + break; + + case 0x0380: /* IIC0 */ + s->cchip.iic[0] = val & 0xffffff; + break; + case 0x03c0: /* IIC1 */ + s->cchip.iic[1] = val & 0xffffff; + break; + + case 0x0400: /* MPR0 */ + case 0x0440: /* MPR1 */ + case 0x0480: /* MPR2 */ + case 0x04c0: /* MPR3 */ + /* MPR: Memory Programming Register. */ + break; + + case 0x0580: + /* TTR: TIGbus Timing Register. */ + /* All sorts of stuff related to interrupt delivery timings. */ + break; + case 0x05c0: + /* TDR: TIGbug Device Timing Register. */ + break; + + case 0x0600: + /* DIM2: Device Interrupt Mask Register, CPU2. */ + s->cchip.dim[2] = val; + cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir); + break; + case 0x0640: + /* DIM3: Device Interrupt Mask Register, CPU3. */ + s->cchip.dim[3] = val; + cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir); + break; + + case 0x0680: /* DIR2 (RO) */ + case 0x06c0: /* DIR3 (RO) */ + break; + + case 0x0700: /* IIC2 */ + s->cchip.iic[2] = val & 0xffffff; + break; + case 0x0740: /* IIC3 */ + s->cchip.iic[3] = val & 0xffffff; + break; + + case 0x0780: + /* PWR: Power Management Control. */ + break; + + case 0x0c00: /* CMONCTLA */ + case 0x0c40: /* CMONCTLB */ + case 0x0c80: /* CMONCNT01 */ + case 0x0cc0: /* CMONCNT23 */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); + return; + } +} + +static void dchip_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) +{ + /* Skip this. It's all related to DRAM timing and setup. */ +} + +static void pchip_write(void *opaque, target_phys_addr_t addr, + uint64_t v32, unsigned size) +{ + TyphoonState *s = opaque; + uint64_t val, oldval; + + if (addr & 4) { + val = v32 << 32 | s->latch_tmp; + addr ^= 4; + } else { + s->latch_tmp = v32; + return; + } + + switch (addr) { + case 0x0000: + /* WSBA0: Window Space Base Address Register. */ + s->pchip.win[0].base_addr = val; + break; + case 0x0040: + /* WSBA1 */ + s->pchip.win[1].base_addr = val; + break; + case 0x0080: + /* WSBA2 */ + s->pchip.win[2].base_addr = val; + break; + case 0x00c0: + /* WSBA3 */ + s->pchip.win[3].base_addr = val; + break; + + case 0x0100: + /* WSM0: Window Space Mask Register. */ + s->pchip.win[0].mask = val; + break; + case 0x0140: + /* WSM1 */ + s->pchip.win[1].mask = val; + break; + case 0x0180: + /* WSM2 */ + s->pchip.win[2].mask = val; + break; + case 0x01c0: + /* WSM3 */ + s->pchip.win[3].mask = val; + break; + + case 0x0200: + /* TBA0: Translated Base Address Register. */ + s->pchip.win[0].translated_base_pfn = val >> 10; + break; + case 0x0240: + /* TBA1 */ + s->pchip.win[1].translated_base_pfn = val >> 10; + break; + case 0x0280: + /* TBA2 */ + s->pchip.win[2].translated_base_pfn = val >> 10; + break; + case 0x02c0: + /* TBA3 */ + s->pchip.win[3].translated_base_pfn = val >> 10; + break; + + case 0x0300: + /* PCTL: Pchip Control Register. */ + oldval = s->pchip.ctl; + oldval &= ~0x00001cff0fc7ffull; /* RW fields */ + oldval |= val & 0x00001cff0fc7ffull; + + s->pchip.ctl = oldval; + break; + + case 0x0340: + /* PLAT: Pchip Master Latency Register. */ + break; + case 0x03c0: + /* PERROR: Pchip Error Register. */ + break; + case 0x0400: + /* PERRMASK: Pchip Error Mask Register. */ + break; + case 0x0440: + /* PERRSET: Pchip Error Set Register. */ + break; + + case 0x0480: + /* TLBIV: Translation Buffer Invalidate Virtual Register. */ + break; + + case 0x04c0: + /* TLBIA: Translation Buffer Invalidate All Register (WO). */ + break; + + case 0x0500: + /* PMONCTL */ + case 0x0540: + /* PMONCNT */ + case 0x0800: + /* SPRST */ + break; + + default: + cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size); + return; + } +} + +static const MemoryRegionOps cchip_ops = { + .read = cchip_read, + .write = cchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps dchip_ops = { + .read = dchip_read, + .write = dchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static const MemoryRegionOps pchip_ops = { + .read = pchip_read, + .write = pchip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, /* ??? Should be 8. */ + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void typhoon_set_irq(void *opaque, int irq, int level) +{ + TyphoonState *s = opaque; + uint64_t drir; + int i; + + /* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */ + drir = s->cchip.drir; + if (level) { + drir |= 1ull << irq; + } else { + drir &= ~(1ull << irq); + } + s->cchip.drir = drir; + + for (i = 0; i < 4; ++i) { + cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir); + } +} + +static void typhoon_set_isa_irq(void *opaque, int irq, int level) +{ + typhoon_set_irq(opaque, 55, level); +} + +static void typhoon_set_timer_irq(void *opaque, int irq, int level) +{ + TyphoonState *s = opaque; + int i; + + /* Thankfully, the mc146818rtc code doesn't track the IRQ state, + and so we don't have to worry about missing interrupts just + because we never actually ACK the interrupt. Just ignore any + case of the interrupt level going low. */ + if (level == 0) { + return; + } + + /* Deliver the interrupt to each CPU, considering each CPU's IIC. */ + for (i = 0; i < 4; ++i) { + CPUState *env = s->cchip.cpu[i]; + if (env) { + uint32_t iic = s->cchip.iic[i]; + + /* ??? The verbage in Section 10.2.2.10 isn't 100% clear. + Bit 24 is the OverFlow bit, RO, and set when the count + decrements past 0. When is OF cleared? My guess is that + OF is actually cleared when the IIC is written, and that + the ICNT field always decrements. At least, that's an + interpretation that makes sense, and "allows the CPU to + determine exactly how mant interval timer ticks were + skipped". At least within the next 4M ticks... */ + + iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000); + s->cchip.iic[i] = iic; + + if (iic & 0x1000000) { + /* Set the ITI bit for this cpu. */ + s->cchip.misc |= 1 << (i + 4); + /* And signal the interrupt. */ + cpu_interrupt(env, CPU_INTERRUPT_TIMER); + } + } + } +} + +static void typhoon_alarm_timer(void *opaque) +{ + TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3); + int cpu = (uintptr_t)opaque & 3; + + /* Set the ITI bit for this cpu. */ + s->cchip.misc |= 1 << (cpu + 4); + cpu_interrupt(s->cchip.cpu[cpu], CPU_INTERRUPT_TIMER); +} + +PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq, + CPUState *cpus[4], pci_map_irq_fn sys_map_irq) +{ + const uint64_t MB = 1024 * 1024; + const uint64_t GB = 1024 * MB; + MemoryRegion *addr_space = get_system_memory(); + MemoryRegion *addr_space_io = get_system_io(); + DeviceState *dev; + PCIHostState *p; + TyphoonState *s; + PCIBus *b; + int i; + + dev = qdev_create(NULL, "typhoon-pcihost"); + qdev_init_nofail(dev); + + p = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); + s = container_of(p, TyphoonState, host); + + /* Remember the CPUs so that we can deliver interrupts to them. */ + for (i = 0; i < 4; i++) { + CPUState *env = cpus[i]; + s->cchip.cpu[i] = env; + if (env) { + env->alarm_timer = qemu_new_timer_ns(rtc_clock, + typhoon_alarm_timer, + (void *)((uintptr_t)s + i)); + } + } + + *p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1); + + /* Main memory region, 0x00.0000.0000. Real hardware supports 32GB, + but the address space hole reserved at this point is 8TB. */ + memory_region_init_ram(&s->ram_region, NULL, "ram", ram_size); + memory_region_add_subregion(addr_space, 0, &s->ram_region); + + /* TIGbus, 0x801.0000.0000, 1GB. */ + /* ??? The TIGbus is used for delivering interrupts, and access to + the flash ROM. I'm not sure that we need to implement it at all. */ + + /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */ + memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x80180000000ULL, + &s->pchip.region); + + /* Cchip CSRs, 0x801.A000.0000, 256MB. */ + memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x801a0000000ULL, + &s->cchip.region); + + /* Dchip CSRs, 0x801.B000.0000, 256MB. */ + memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB); + memory_region_add_subregion(addr_space, 0x801b0000000ULL, + &s->dchip_region); + + /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */ + memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB); + memory_region_add_subregion(addr_space, 0x80000000000ULL, + &s->pchip.reg_mem); + + /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */ + /* ??? Ideally we drop the "system" i/o space on the floor and give the + PCI subsystem the full address space reserved by the chipset. + We can't do that until the MEM and IO paths in memory.c are unified. */ + memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL, + "pci0-io", 32*MB); + memory_region_add_subregion(addr_space, 0x801fc000000ULL, + &s->pchip.reg_io); + + b = pci_register_bus(&s->host.busdev.qdev, "pci", + typhoon_set_irq, sys_map_irq, s, + &s->pchip.reg_mem, addr_space_io, 0, 64); + s->host.bus = b; + + /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ + memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b, + "pci0-iack", 64*MB); + memory_region_add_subregion(addr_space, 0x801f8000000ULL, + &s->pchip.reg_iack); + + /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */ + memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b, + "pci0-conf", 16*MB); + memory_region_add_subregion(addr_space, 0x801fe000000ULL, + &s->pchip.reg_conf); + + /* For the record, these are the mappings for the second PCI bus. + We can get away with not implementing them because we indicate + via the Cchip.CSC<PIP> bit that Pchip1 is not present. */ + /* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */ + /* Pchip1 CSRs, 0x802.8000.0000, 256MB. */ + /* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */ + /* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */ + /* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */ + + /* Init the ISA bus. */ + /* ??? Technically there should be a cy82c693ub pci-isa bridge. */ + { + qemu_irq isa_pci_irq, *isa_irqs; + + isa_bus_new(NULL, addr_space_io); + isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1); + isa_irqs = i8259_init(isa_pci_irq); + isa_bus_irqs(isa_irqs); + } + + return b; +} + +static int typhoon_pcihost_init(SysBusDevice *dev) +{ + return 0; +} + +static SysBusDeviceInfo typhoon_pcihost_info = { + .init = typhoon_pcihost_init, + .qdev.name = "typhoon-pcihost", + .qdev.size = sizeof(TyphoonState), + .qdev.no_user = 1 +}; + +static void typhoon_register(void) +{ + sysbus_register_withprop(&typhoon_pcihost_info); +} +device_init(typhoon_register); @@ -24,6 +24,7 @@ */ #include "hw.h" #include "ppc_mac.h" +#include "adb.h" #include "qemu-timer.h" #include "sysemu.h" @@ -633,16 +634,20 @@ static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr) return 0; } -static CPUWriteMemoryFunc * const cuda_write[] = { - &cuda_writeb, - &cuda_writew, - &cuda_writel, -}; - -static CPUReadMemoryFunc * const cuda_read[] = { - &cuda_readb, - &cuda_readw, - &cuda_readl, +static MemoryRegionOps cuda_ops = { + .old_mmio = { + .write = { + cuda_writeb, + cuda_writew, + cuda_writel, + }, + .read = { + cuda_readb, + cuda_readw, + cuda_readl, + }, + }, + .endianness = DEVICE_NATIVE_ENDIAN, }; static bool cuda_timer_exist(void *opaque, int version_id) @@ -739,8 +744,8 @@ void cuda_init (MemoryRegion **cuda_mem, qemu_irq irq) s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, s); - cpu_register_io_memory(cuda_read, cuda_write, s, - DEVICE_NATIVE_ENDIAN); + memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000); + *cuda_mem = &s->mem; vmstate_register(NULL, -1, &vmstate_cuda, s); qemu_register_reset(cuda_reset, s); diff --git a/hw/etraxfs_dma.c b/hw/etraxfs_dma.c index 94bfb70..02d0183 100644 --- a/hw/etraxfs_dma.c +++ b/hw/etraxfs_dma.c @@ -604,12 +604,10 @@ dma_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value) static void dma_update_state(struct fs_dma_ctrl *ctrl, int c) { - if ((ctrl->channels[c].regs[RW_CFG] & 1) != 3) { - if (ctrl->channels[c].regs[RW_CFG] & 2) - ctrl->channels[c].state = STOPPED; - if (!(ctrl->channels[c].regs[RW_CFG] & 1)) - ctrl->channels[c].state = RST; - } + if (ctrl->channels[c].regs[RW_CFG] & 2) + ctrl->channels[c].state = STOPPED; + if (!(ctrl->channels[c].regs[RW_CFG] & 1)) + ctrl->channels[c].state = RST; } static void diff --git a/hw/heathrow_pic.c b/hw/heathrow_pic.c index 51996ab..16f48d1 100644 --- a/hw/heathrow_pic.c +++ b/hw/heathrow_pic.c @@ -126,7 +126,7 @@ static uint64_t pic_read(void *opaque, target_phys_addr_t addr, static const MemoryRegionOps heathrow_pic_ops = { .read = pic_read, .write = pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void heathrow_pic_set_irq(void *opaque, int num, int level) diff --git a/hw/milkymist-hw.h b/hw/milkymist-hw.h index 20de68e..9f358a7 100644 --- a/hw/milkymist-hw.h +++ b/hw/milkymist-hw.h @@ -5,15 +5,14 @@ #include "qdev-addr.h" static inline DeviceState *milkymist_uart_create(target_phys_addr_t base, - qemu_irq rx_irq, qemu_irq tx_irq) + qemu_irq irq) { DeviceState *dev; dev = qdev_create(NULL, "milkymist-uart"); qdev_init_nofail(dev); sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq); - sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); return dev; } diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c index 128cd8c..5404ca9 100644 --- a/hw/milkymist-uart.c +++ b/hw/milkymist-uart.c @@ -30,21 +30,54 @@ enum { R_RXTX = 0, R_DIV, + R_STAT, + R_CTRL, + R_DBG, R_MAX }; +enum { + STAT_THRE = (1<<0), + STAT_RX_EVT = (1<<1), + STAT_TX_EVT = (1<<2), +}; + +enum { + CTRL_RX_IRQ_EN = (1<<0), + CTRL_TX_IRQ_EN = (1<<1), + CTRL_THRU_EN = (1<<2), +}; + +enum { + DBG_BREAK_EN = (1<<0), +}; + struct MilkymistUartState { SysBusDevice busdev; MemoryRegion regs_region; CharDriverState *chr; - - qemu_irq rx_irq; - qemu_irq tx_irq; + qemu_irq irq; uint32_t regs[R_MAX]; }; typedef struct MilkymistUartState MilkymistUartState; +static void uart_update_irq(MilkymistUartState *s) +{ + int rx_event = s->regs[R_STAT] & STAT_RX_EVT; + int tx_event = s->regs[R_STAT] & STAT_TX_EVT; + int rx_irq_en = s->regs[R_CTRL] & CTRL_RX_IRQ_EN; + int tx_irq_en = s->regs[R_CTRL] & CTRL_TX_IRQ_EN; + + if ((rx_irq_en && rx_event) || (tx_irq_en && tx_event)) { + trace_milkymist_uart_raise_irq(); + qemu_irq_raise(s->irq); + } else { + trace_milkymist_uart_lower_irq(); + qemu_irq_lower(s->irq); + } +} + static uint64_t uart_read(void *opaque, target_phys_addr_t addr, unsigned size) { @@ -54,7 +87,12 @@ static uint64_t uart_read(void *opaque, target_phys_addr_t addr, addr >>= 2; switch (addr) { case R_RXTX: + r = s->regs[addr]; + break; case R_DIV: + case R_STAT: + case R_CTRL: + case R_DBG: r = s->regs[addr]; break; @@ -83,18 +121,26 @@ static void uart_write(void *opaque, target_phys_addr_t addr, uint64_t value, if (s->chr) { qemu_chr_fe_write(s->chr, &ch, 1); } - trace_milkymist_uart_pulse_irq_tx(); - qemu_irq_pulse(s->tx_irq); + s->regs[R_STAT] |= STAT_TX_EVT; break; case R_DIV: + case R_CTRL: + case R_DBG: s->regs[addr] = value; break; + case R_STAT: + /* write one to clear bits */ + s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT)); + break; + default: error_report("milkymist_uart: write access to unknown register 0x" TARGET_FMT_plx, addr << 2); break; } + + uart_update_irq(s); } static const MemoryRegionOps uart_mmio_ops = { @@ -111,14 +157,19 @@ static void uart_rx(void *opaque, const uint8_t *buf, int size) { MilkymistUartState *s = opaque; + assert(!(s->regs[R_STAT] & STAT_RX_EVT)); + + s->regs[R_STAT] |= STAT_RX_EVT; s->regs[R_RXTX] = *buf; - trace_milkymist_uart_pulse_irq_rx(); - qemu_irq_pulse(s->rx_irq); + + uart_update_irq(s); } static int uart_can_rx(void *opaque) { - return 1; + MilkymistUartState *s = opaque; + + return !(s->regs[R_STAT] & STAT_RX_EVT); } static void uart_event(void *opaque, int event) @@ -133,14 +184,16 @@ static void milkymist_uart_reset(DeviceState *d) for (i = 0; i < R_MAX; i++) { s->regs[i] = 0; } + + /* THRE is always set */ + s->regs[R_STAT] = STAT_THRE; } static int milkymist_uart_init(SysBusDevice *dev) { MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev); - sysbus_init_irq(dev, &s->rx_irq); - sysbus_init_irq(dev, &s->tx_irq); + sysbus_init_irq(dev, &s->irq); memory_region_init_io(&s->regs_region, &uart_mmio_ops, s, "milkymist-uart", R_MAX * 4); diff --git a/hw/milkymist.c b/hw/milkymist.c index bca0a58..b7a8c1c 100644 --- a/hw/milkymist.c +++ b/hw/milkymist.c @@ -146,17 +146,17 @@ milkymist_init(ram_addr_t ram_size_not_used, exit(1); } - milkymist_uart_create(0x60000000, irq[0], irq[1]); - milkymist_sysctl_create(0x60001000, irq[2], irq[3], irq[4], + milkymist_uart_create(0x60000000, irq[0]); + milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3], 80000000, 0x10014d31, 0x0000041f, 0x00000001); milkymist_hpdmc_create(0x60002000); milkymist_vgafb_create(0x60003000, 0x40000000, 0x0fffffff); milkymist_memcard_create(0x60004000); - milkymist_ac97_create(0x60005000, irq[5], irq[6], irq[7], irq[8]); - milkymist_pfpu_create(0x60006000, irq[9]); - milkymist_tmu2_create(0x60007000, irq[10]); - milkymist_minimac2_create(0x60008000, 0x30000000, irq[11], irq[12]); - milkymist_softusb_create(0x6000f000, irq[17], + milkymist_ac97_create(0x60005000, irq[4], irq[5], irq[6], irq[7]); + milkymist_pfpu_create(0x60006000, irq[8]); + milkymist_tmu2_create(0x60007000, irq[9]); + milkymist_minimac2_create(0x60008000, 0x30000000, irq[10], irq[11]); + milkymist_softusb_create(0x6000f000, irq[15], 0x20000000, 0x1000, 0x20020000, 0x2000); /* make sure juart isn't the first chardev */ diff --git a/hw/openpic.c b/hw/openpic.c index 88e997e..22fc275 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -2,6 +2,7 @@ * OpenPIC emulation * * Copyright (c) 2004 Jocelyn Mayer + * 2011 Alexander Graf * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -56,13 +57,13 @@ #define MAX_MBX 4 #define MAX_TMR 4 #define VECTOR_BITS 8 -#define MAX_IPI 0 +#define MAX_IPI 4 #define VID (0x00000000) #elif defined(USE_MPCxxx) -#define MAX_CPU 2 +#define MAX_CPU 15 #define MAX_IRQ 128 #define MAX_DBL 0 #define MAX_MBX 0 @@ -127,14 +128,14 @@ enum { #define MPIC_MSI_REG_START 0x11C00 #define MPIC_MSI_REG_SIZE 0x100 #define MPIC_CPU_REG_START 0x20000 -#define MPIC_CPU_REG_SIZE 0x100 +#define MPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000) enum mpic_ide_bits { - IDR_EP = 0, - IDR_CI0 = 1, - IDR_CI1 = 2, - IDR_P1 = 30, - IDR_P0 = 31, + IDR_EP = 31, + IDR_CI0 = 30, + IDR_CI1 = 29, + IDR_P1 = 1, + IDR_P0 = 0, }; #else @@ -161,6 +162,16 @@ static inline int test_bit (uint32_t *field, int bit) return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0; } +static int get_current_cpu(void) +{ + return cpu_single_env->cpu_index; +} + +static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr, + int idx); +static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr, + uint32_t val, int idx); + enum { IRQ_EXTERNAL = 0x01, IRQ_INTERNAL = 0x02, @@ -465,46 +476,35 @@ static void openpic_reset (void *opaque) opp->glbc = 0x00000000; } -static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg) +static inline uint32_t read_IRQreg_ide(openpic_t *opp, int n_IRQ) { - uint32_t retval; - - switch (reg) { - case IRQ_IPVP: - retval = opp->src[n_IRQ].ipvp; - break; - case IRQ_IDE: - retval = opp->src[n_IRQ].ide; - break; - } + return opp->src[n_IRQ].ide; +} - return retval; +static inline uint32_t read_IRQreg_ipvp(openpic_t *opp, int n_IRQ) +{ + return opp->src[n_IRQ].ipvp; } -static inline void write_IRQreg (openpic_t *opp, int n_IRQ, - uint32_t reg, uint32_t val) +static inline void write_IRQreg_ide(openpic_t *opp, int n_IRQ, uint32_t val) { uint32_t tmp; - switch (reg) { - case IRQ_IPVP: - /* NOTE: not fully accurate for special IRQs, but simple and - sufficient */ - /* ACTIVITY bit is read-only */ - opp->src[n_IRQ].ipvp = - (opp->src[n_IRQ].ipvp & 0x40000000) | - (val & 0x800F00FF); - openpic_update_irq(opp, n_IRQ); - DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", - n_IRQ, val, opp->src[n_IRQ].ipvp); - break; - case IRQ_IDE: - tmp = val & 0xC0000000; - tmp |= val & ((1 << MAX_CPU) - 1); - opp->src[n_IRQ].ide = tmp; - DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); - break; - } + tmp = val & 0xC0000000; + tmp |= val & ((1ULL << MAX_CPU) - 1); + opp->src[n_IRQ].ide = tmp; + DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); +} + +static inline void write_IRQreg_ipvp(openpic_t *opp, int n_IRQ, uint32_t val) +{ + /* NOTE: not fully accurate for special IRQs, but simple and sufficient */ + /* ACTIVITY bit is read-only */ + opp->src[n_IRQ].ipvp = (opp->src[n_IRQ].ipvp & 0x40000000) + | (val & 0x800F00FF); + openpic_update_irq(opp, n_IRQ); + DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", n_IRQ, val, + opp->src[n_IRQ].ipvp); } #if 0 // Code provision for Intel model @@ -516,10 +516,10 @@ static uint32_t read_doorbell_register (openpic_t *opp, switch (offset) { case DBL_IPVP_OFFSET: - retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, IRQ_DBL0 + n_dbl); break; case DBL_IDE_OFFSET: - retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE); + retval = read_IRQreg_ide(opp, IRQ_DBL0 + n_dbl); break; case DBL_DMR_OFFSET: retval = opp->doorbells[n_dbl].dmr; @@ -534,10 +534,10 @@ static void write_doorbell_register (penpic_t *opp, int n_dbl, { switch (offset) { case DBL_IVPR_OFFSET: - write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value); + write_IRQreg_ipvp(opp, IRQ_DBL0 + n_dbl, value); break; case DBL_IDE_OFFSET: - write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value); + write_IRQreg_ide(opp, IRQ_DBL0 + n_dbl, value); break; case DBL_DMR_OFFSET: opp->doorbells[n_dbl].dmr = value; @@ -557,10 +557,10 @@ static uint32_t read_mailbox_register (openpic_t *opp, retval = opp->mailboxes[n_mbx].mbr; break; case MBX_IVPR_OFFSET: - retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, IRQ_MBX0 + n_mbx); break; case MBX_DMR_OFFSET: - retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE); + retval = read_IRQreg_ide(opp, IRQ_MBX0 + n_mbx); break; } @@ -575,10 +575,10 @@ static void write_mailbox_register (openpic_t *opp, int n_mbx, opp->mailboxes[n_mbx].mbr = value; break; case MBX_IVPR_OFFSET: - write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value); + write_IRQreg_ipvp(opp, IRQ_MBX0 + n_mbx, value); break; case MBX_DMR_OFFSET: - write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value); + write_IRQreg_ide(opp, IRQ_MBX0 + n_mbx, value); break; } } @@ -594,18 +594,27 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val); if (addr & 0xF) return; - addr &= 0xFF; switch (addr) { - case 0x00: /* FREP */ + case 0x40: + case 0x50: + case 0x60: + case 0x70: + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + openpic_cpu_write_internal(opp, addr, val, get_current_cpu()); break; - case 0x20: /* GLBC */ + case 0x1000: /* FREP */ + break; + case 0x1020: /* GLBC */ if (val & 0x80000000 && opp->reset) opp->reset(opp); opp->glbc = val & ~0x80000000; break; - case 0x80: /* VENI */ + case 0x1080: /* VENI */ break; - case 0x90: /* PINT */ + case 0x1090: /* PINT */ for (idx = 0; idx < opp->nb_cpus; idx++) { if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) { DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx); @@ -619,22 +628,20 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v } opp->pint = val; break; -#if MAX_IPI > 0 - case 0xA0: /* IPI_IPVP */ - case 0xB0: - case 0xC0: - case 0xD0: + case 0x10A0: /* IPI_IPVP */ + case 0x10B0: + case 0x10C0: + case 0x10D0: { int idx; - idx = (addr - 0xA0) >> 4; - write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP, val); + idx = (addr - 0x10A0) >> 4; + write_IRQreg_ipvp(opp, opp->irq_ipi0 + idx, val); } break; -#endif - case 0xE0: /* SPVE */ + case 0x10E0: /* SPVE */ opp->spve = val & 0x000000FF; break; - case 0xF0: /* TIFR */ + case 0x10F0: /* TIFR */ opp->tifr = val; break; default: @@ -651,36 +658,43 @@ static uint32_t openpic_gbl_read (void *opaque, target_phys_addr_t addr) retval = 0xFFFFFFFF; if (addr & 0xF) return retval; - addr &= 0xFF; switch (addr) { - case 0x00: /* FREP */ + case 0x1000: /* FREP */ retval = opp->frep; break; - case 0x20: /* GLBC */ + case 0x1020: /* GLBC */ retval = opp->glbc; break; - case 0x80: /* VENI */ + case 0x1080: /* VENI */ retval = opp->veni; break; - case 0x90: /* PINT */ + case 0x1090: /* PINT */ retval = 0x00000000; break; -#if MAX_IPI > 0 - case 0xA0: /* IPI_IPVP */ + case 0x40: + case 0x50: + case 0x60: + case 0x70: + case 0x80: + case 0x90: + case 0xA0: case 0xB0: - case 0xC0: - case 0xD0: + retval = openpic_cpu_read_internal(opp, addr, get_current_cpu()); + break; + case 0x10A0: /* IPI_IPVP */ + case 0x10B0: + case 0x10C0: + case 0x10D0: { int idx; - idx = (addr - 0xA0) >> 4; - retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP); + idx = (addr - 0x10A0) >> 4; + retval = read_IRQreg_ipvp(opp, opp->irq_ipi0 + idx); } break; -#endif - case 0xE0: /* SPVE */ + case 0x10E0: /* SPVE */ retval = opp->spve; break; - case 0xF0: /* TIFR */ + case 0x10F0: /* TIFR */ retval = opp->tifr; break; default: @@ -714,10 +728,10 @@ static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val) opp->timers[idx].tibc = val; break; case 0x20: /* TIVP */ - write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP, val); + write_IRQreg_ipvp(opp, opp->irq_tim0 + idx, val); break; case 0x30: /* TIDE */ - write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE, val); + write_IRQreg_ide(opp, opp->irq_tim0 + idx, val); break; } } @@ -744,10 +758,10 @@ static uint32_t openpic_timer_read (void *opaque, uint32_t addr) retval = opp->timers[idx].tibc; break; case 0x20: /* TIPV */ - retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, opp->irq_tim0 + idx); break; case 0x30: /* TIDE */ - retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE); + retval = read_IRQreg_ide(opp, opp->irq_tim0 + idx); break; } DPRINTF("%s: => %08x\n", __func__, retval); @@ -767,10 +781,10 @@ static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val) idx = addr >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(opp, idx, IRQ_IDE, val); + write_IRQreg_ide(opp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(opp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(opp, idx, val); } } @@ -788,38 +802,40 @@ static uint32_t openpic_src_read (void *opaque, uint32_t addr) idx = addr >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(opp, idx, IRQ_IDE); + retval = read_IRQreg_ide(opp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(opp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(opp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); return retval; } -static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t val) +static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr, + uint32_t val, int idx) { openpic_t *opp = opaque; IRQ_src_t *src; IRQ_dst_t *dst; - int idx, s_IRQ, n_IRQ; + int s_IRQ, n_IRQ; - DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val); + DPRINTF("%s: cpu %d addr " TARGET_FMT_plx " <= %08x\n", __func__, idx, + addr, val); if (addr & 0xF) return; - addr &= 0x1FFF0; - idx = addr / 0x1000; dst = &opp->dst[idx]; addr &= 0xFF0; switch (addr) { #if MAX_IPI > 0 - case 0x40: /* PIPD */ + case 0x40: /* IPIDR */ case 0x50: case 0x60: case 0x70: idx = (addr - 0x40) >> 4; - write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE, val); + /* we use IDE as mask which CPUs to deliver the IPI to still. */ + write_IRQreg_ide(opp, opp->irq_ipi0 + idx, + opp->src[opp->irq_ipi0 + idx].ide | val); openpic_set_irq(opp, opp->irq_ipi0 + idx, 1); openpic_set_irq(opp, opp->irq_ipi0 + idx, 0); break; @@ -856,20 +872,24 @@ static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t v } } -static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr) +static void openpic_cpu_write(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12); +} + +static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr, + int idx) { openpic_t *opp = opaque; IRQ_src_t *src; IRQ_dst_t *dst; uint32_t retval; - int idx, n_IRQ; + int n_IRQ; - DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr); + DPRINTF("%s: cpu %d addr " TARGET_FMT_plx "\n", __func__, idx, addr); retval = 0xFFFFFFFF; if (addr & 0xF) return retval; - addr &= 0x1FFF0; - idx = addr / 0x1000; dst = &opp->dst[idx]; addr &= 0xFF0; switch (addr) { @@ -909,18 +929,22 @@ static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr) reset_bit(&src->ipvp, IPVP_ACTIVITY); src->pending = 0; } + + if ((n_IRQ >= opp->irq_ipi0) && (n_IRQ < (opp->irq_ipi0 + MAX_IPI))) { + src->ide &= ~(1 << idx); + if (src->ide && !test_bit(&src->ipvp, IPVP_SENSE)) { + /* trigger on CPUs that didn't know about it yet */ + openpic_set_irq(opp, n_IRQ, 1); + openpic_set_irq(opp, n_IRQ, 0); + /* if all CPUs knew about it, set active bit again */ + set_bit(&src->ipvp, IPVP_ACTIVITY); + } + } } break; case 0xB0: /* PEOI */ retval = 0; break; -#if MAX_IPI > 0 - case 0x40: /* IDE */ - case 0x50: - idx = (addr - 0x40) >> 4; - retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE); - break; -#endif default: break; } @@ -929,6 +953,11 @@ static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr) return retval; } +static uint32_t openpic_cpu_read(void *opaque, target_phys_addr_t addr) +{ + return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12); +} + static void openpic_buggy_write (void *opaque, target_phys_addr_t addr, uint32_t val) { @@ -1247,7 +1276,7 @@ static void mpic_reset (void *opaque) mpp->glbc = 0x80000000; /* Initialise controller registers */ - mpp->frep = 0x004f0002; + mpp->frep = 0x004f0002 | ((mpp->nb_cpus - 1) << 8); mpp->veni = VENI; mpp->pint = 0x00000000; mpp->spve = 0x0000FFFF; @@ -1256,6 +1285,10 @@ static void mpic_reset (void *opaque) mpp->src[i].ipvp = 0x80800000; mpp->src[i].ide = 0x00000001; } + /* Set IDE for IPIs to 0 so we don't get spurious interrupts */ + for (i = mpp->irq_ipi0; i < (mpp->irq_ipi0 + MAX_IPI); i++) { + mpp->src[i].ide = 0; + } /* Initialise IRQ destinations */ for (i = 0; i < MAX_CPU; i++) { mpp->dst[i].pctp = 0x0000000F; @@ -1296,13 +1329,13 @@ static void mpic_timer_write (void *opaque, target_phys_addr_t addr, uint32_t va mpp->timers[idx].tibc = val; break; case 0x20: /* GTIVPR */ - write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, MPIC_TMR_IRQ + idx, val); break; case 0x30: /* GTIDR & TFRR */ if ((addr & 0xF0) == 0xF0) mpp->dst[cpu].tfrr = val; else - write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, MPIC_TMR_IRQ + idx, val); break; } } @@ -1328,13 +1361,13 @@ static uint32_t mpic_timer_read (void *opaque, target_phys_addr_t addr) retval = mpp->timers[idx].tibc; break; case 0x20: /* TIPV */ - retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, MPIC_TMR_IRQ + idx); break; case 0x30: /* TIDR */ if ((addr &0xF0) == 0XF0) retval = mpp->dst[cpu].tfrr; else - retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, MPIC_TMR_IRQ + idx); break; } DPRINTF("%s: => %08x\n", __func__, retval); @@ -1357,10 +1390,10 @@ static void mpic_src_ext_write (void *opaque, target_phys_addr_t addr, idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(mpp, idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(mpp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, idx, val); } } } @@ -1381,10 +1414,10 @@ static uint32_t mpic_src_ext_read (void *opaque, target_phys_addr_t addr) idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(mpp, idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(mpp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); } @@ -1407,10 +1440,10 @@ static void mpic_src_int_write (void *opaque, target_phys_addr_t addr, idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(mpp, idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(mpp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, idx, val); } } } @@ -1431,10 +1464,10 @@ static uint32_t mpic_src_int_read (void *opaque, target_phys_addr_t addr) idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(mpp, idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(mpp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); } @@ -1457,10 +1490,10 @@ static void mpic_src_msg_write (void *opaque, target_phys_addr_t addr, idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(mpp, idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(mpp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, idx, val); } } } @@ -1481,10 +1514,10 @@ static uint32_t mpic_src_msg_read (void *opaque, target_phys_addr_t addr) idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(mpp, idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(mpp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); } @@ -1507,10 +1540,10 @@ static void mpic_src_msi_write (void *opaque, target_phys_addr_t addr, idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - write_IRQreg(mpp, idx, IRQ_IDE, val); + write_IRQreg_ide(mpp, idx, val); } else { /* EXVP / IFEVP / IEEVP */ - write_IRQreg(mpp, idx, IRQ_IPVP, val); + write_IRQreg_ipvp(mpp, idx, val); } } } @@ -1530,10 +1563,10 @@ static uint32_t mpic_src_msi_read (void *opaque, target_phys_addr_t addr) idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { /* EXDE / IFEDE / IEEDE */ - retval = read_IRQreg(mpp, idx, IRQ_IDE); + retval = read_IRQreg_ide(mpp, idx); } else { /* EXVP / IFEVP / IEEVP */ - retval = read_IRQreg(mpp, idx, IRQ_IPVP); + retval = read_IRQreg_ipvp(mpp, idx); } DPRINTF("%s: => %08x\n", __func__, retval); } @@ -1659,10 +1692,6 @@ qemu_irq *mpic_init (MemoryRegion *address_space, target_phys_addr_t base, {"cpu", &mpic_cpu_ops, MPIC_CPU_REG_START, MPIC_CPU_REG_SIZE}, }; - /* XXX: for now, only one CPU is supported */ - if (nb_cpus != 1) - return NULL; - mpp = g_malloc0(sizeof(openpic_t)); memory_region_init(&mpp->mem, "mpic", 0x40000); @@ -50,7 +50,7 @@ static void cpu_ppc_tb_stop (CPUState *env); static void cpu_ppc_tb_start (CPUState *env); -static void ppc_set_irq (CPUState *env, int n_IRQ, int level) +void ppc_set_irq(CPUState *env, int n_IRQ, int level) { unsigned int old_pending = env->pending_interrupts; @@ -423,25 +423,8 @@ void ppce500_irq_init (CPUState *env) } /*****************************************************************************/ /* PowerPC time base and decrementer emulation */ -struct ppc_tb_t { - /* Time base management */ - int64_t tb_offset; /* Compensation */ - int64_t atb_offset; /* Compensation */ - uint32_t tb_freq; /* TB frequency */ - /* Decrementer management */ - uint64_t decr_next; /* Tick for next decr interrupt */ - uint32_t decr_freq; /* decrementer frequency */ - struct QEMUTimer *decr_timer; - /* Hypervisor decrementer management */ - uint64_t hdecr_next; /* Tick for next hdecr interrupt */ - struct QEMUTimer *hdecr_timer; - uint64_t purr_load; - uint64_t purr_start; - void *opaque; -}; -static inline uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, - int64_t tb_offset) +uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) { /* TB time in tb periods */ return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset; @@ -611,10 +594,13 @@ static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next) int64_t diff; diff = next - qemu_get_clock_ns(vm_clock); - if (diff >= 0) + if (diff >= 0) { decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec()); - else + } else if (tb_env->flags & PPC_TIMER_BOOKE) { + decr = 0; + } else { decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec()); + } LOG_TB("%s: %08" PRIx32 "\n", __func__, decr); return decr; @@ -678,18 +664,24 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp, decr, value); now = qemu_get_clock_ns(vm_clock); next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); - if (is_excp) + if (is_excp) { next += *nextp - now; - if (next == now) + } + if (next == now) { next++; + } *nextp = next; /* Adjust timer */ qemu_mod_timer(timer, next); - /* If we set a negative value and the decrementer was positive, - * raise an exception. + + /* If we set a negative value and the decrementer was positive, raise an + * exception. */ - if ((value & 0x80000000) && !(decr & 0x80000000)) + if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) + && (value & 0x80000000) + && !(decr & 0x80000000)) { (*raise_excp)(env); + } } static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr, @@ -763,6 +755,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq) tb_env = g_malloc0(sizeof(ppc_tb_t)); env->tb_env = tb_env; + tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; /* Create new timer */ tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, env); if (0) { @@ -806,11 +799,11 @@ uint32_t cpu_ppc601_load_rtcl (CPUState *env) } /*****************************************************************************/ -/* Embedded PowerPC timers */ +/* PowerPC 40x timers */ /* PIT, FIT & WDT */ -typedef struct ppcemb_timer_t ppcemb_timer_t; -struct ppcemb_timer_t { +typedef struct ppc40x_timer_t ppc40x_timer_t; +struct ppc40x_timer_t { uint64_t pit_reload; /* PIT auto-reload value */ uint64_t fit_next; /* Tick for next FIT interrupt */ struct QEMUTimer *fit_timer; @@ -826,12 +819,12 @@ static void cpu_4xx_fit_cb (void *opaque) { CPUState *env; ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; uint64_t now, next; env = opaque; tb_env = env->tb_env; - ppcemb_timer = tb_env->opaque; + ppc40x_timer = tb_env->opaque; now = qemu_get_clock_ns(vm_clock); switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { case 0: @@ -853,7 +846,7 @@ static void cpu_4xx_fit_cb (void *opaque) next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq); if (next == now) next++; - qemu_mod_timer(ppcemb_timer->fit_timer, next); + qemu_mod_timer(ppc40x_timer->fit_timer, next); env->spr[SPR_40x_TSR] |= 1 << 26; if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) ppc_set_irq(env, PPC_INTERRUPT_FIT, 1); @@ -865,11 +858,11 @@ static void cpu_4xx_fit_cb (void *opaque) /* Programmable interval timer */ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) { - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; uint64_t now, next; - ppcemb_timer = tb_env->opaque; - if (ppcemb_timer->pit_reload <= 1 || + ppc40x_timer = tb_env->opaque; + if (ppc40x_timer->pit_reload <= 1 || !((env->spr[SPR_40x_TCR] >> 26) & 0x1) || (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { /* Stop PIT */ @@ -877,9 +870,9 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp) qemu_del_timer(tb_env->decr_timer); } else { LOG_TB("%s: start PIT %016" PRIx64 "\n", - __func__, ppcemb_timer->pit_reload); + __func__, ppc40x_timer->pit_reload); now = qemu_get_clock_ns(vm_clock); - next = now + muldiv64(ppcemb_timer->pit_reload, + next = now + muldiv64(ppc40x_timer->pit_reload, get_ticks_per_sec(), tb_env->decr_freq); if (is_excp) next += tb_env->decr_next - now; @@ -894,21 +887,21 @@ static void cpu_4xx_pit_cb (void *opaque) { CPUState *env; ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; env = opaque; tb_env = env->tb_env; - ppcemb_timer = tb_env->opaque; + ppc40x_timer = tb_env->opaque; env->spr[SPR_40x_TSR] |= 1 << 27; if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) - ppc_set_irq(env, ppcemb_timer->decr_excp, 1); + ppc_set_irq(env, ppc40x_timer->decr_excp, 1); start_stop_pit(env, tb_env, 1); LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " "%016" PRIx64 "\n", __func__, (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1), env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], - ppcemb_timer->pit_reload); + ppc40x_timer->pit_reload); } /* Watchdog timer */ @@ -916,12 +909,12 @@ static void cpu_4xx_wdt_cb (void *opaque) { CPUState *env; ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; uint64_t now, next; env = opaque; tb_env = env->tb_env; - ppcemb_timer = tb_env->opaque; + ppc40x_timer = tb_env->opaque; now = qemu_get_clock_ns(vm_clock); switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { case 0: @@ -948,13 +941,13 @@ static void cpu_4xx_wdt_cb (void *opaque) switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { case 0x0: case 0x1: - qemu_mod_timer(ppcemb_timer->wdt_timer, next); - ppcemb_timer->wdt_next = next; + qemu_mod_timer(ppc40x_timer->wdt_timer, next); + ppc40x_timer->wdt_next = next; env->spr[SPR_40x_TSR] |= 1 << 31; break; case 0x2: - qemu_mod_timer(ppcemb_timer->wdt_timer, next); - ppcemb_timer->wdt_next = next; + qemu_mod_timer(ppc40x_timer->wdt_timer, next); + ppc40x_timer->wdt_next = next; env->spr[SPR_40x_TSR] |= 1 << 30; if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) ppc_set_irq(env, PPC_INTERRUPT_WDT, 1); @@ -982,12 +975,12 @@ static void cpu_4xx_wdt_cb (void *opaque) void store_40x_pit (CPUState *env, target_ulong val) { ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; tb_env = env->tb_env; - ppcemb_timer = tb_env->opaque; + ppc40x_timer = tb_env->opaque; LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val); - ppcemb_timer->pit_reload = val; + ppc40x_timer->pit_reload = val; start_stop_pit(env, tb_env, 0); } @@ -996,31 +989,7 @@ target_ulong load_40x_pit (CPUState *env) return cpu_ppc_load_decr(env); } -void store_booke_tsr (CPUState *env, target_ulong val) -{ - ppc_tb_t *tb_env = env->tb_env; - ppcemb_timer_t *ppcemb_timer; - - ppcemb_timer = tb_env->opaque; - - LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val); - env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000); - if (val & 0x80000000) - ppc_set_irq(env, ppcemb_timer->decr_excp, 0); -} - -void store_booke_tcr (CPUState *env, target_ulong val) -{ - ppc_tb_t *tb_env; - - tb_env = env->tb_env; - LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val); - env->spr[SPR_40x_TCR] = val & 0xFFC00000; - start_stop_pit(env, tb_env, 1); - cpu_4xx_wdt_cb(env); -} - -static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq) +static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) { CPUState *env = opaque; ppc_tb_t *tb_env = env->tb_env; @@ -1032,30 +1001,31 @@ static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq) /* XXX: we should also update all timers */ } -clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq, +clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq, unsigned int decr_excp) { ppc_tb_t *tb_env; - ppcemb_timer_t *ppcemb_timer; + ppc40x_timer_t *ppc40x_timer; tb_env = g_malloc0(sizeof(ppc_tb_t)); env->tb_env = tb_env; - ppcemb_timer = g_malloc0(sizeof(ppcemb_timer_t)); + tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; + ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t)); tb_env->tb_freq = freq; tb_env->decr_freq = freq; - tb_env->opaque = ppcemb_timer; + tb_env->opaque = ppc40x_timer; LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); - if (ppcemb_timer != NULL) { + if (ppc40x_timer != NULL) { /* We use decr timer for PIT */ tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env); - ppcemb_timer->fit_timer = + ppc40x_timer->fit_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env); - ppcemb_timer->wdt_timer = + ppc40x_timer->wdt_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env); - ppcemb_timer->decr_excp = decr_excp; + ppc40x_timer->decr_excp = decr_excp; } - return &ppc_emb_set_tb_clk; + return &ppc_40x_set_tb_clk; } /*****************************************************************************/ @@ -1,3 +1,5 @@ +void ppc_set_irq (CPUState *env, int n_IRQ, int level); + /* PowerPC hardware exceptions management helpers */ typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); typedef struct clk_setup_t clk_setup_t; @@ -11,6 +13,36 @@ static inline void clk_setup (clk_setup_t *clk, uint32_t freq) (*clk->cb)(clk->opaque, freq); } +struct ppc_tb_t { + /* Time base management */ + int64_t tb_offset; /* Compensation */ + int64_t atb_offset; /* Compensation */ + uint32_t tb_freq; /* TB frequency */ + /* Decrementer management */ + uint64_t decr_next; /* Tick for next decr interrupt */ + uint32_t decr_freq; /* decrementer frequency */ + struct QEMUTimer *decr_timer; + /* Hypervisor decrementer management */ + uint64_t hdecr_next; /* Tick for next hdecr interrupt */ + struct QEMUTimer *hdecr_timer; + uint64_t purr_load; + uint64_t purr_start; + void *opaque; + uint32_t flags; +}; + +/* PPC Timers flags */ +#define PPC_TIMER_BOOKE (1 << 0) /* Enable Booke support */ +#define PPC_TIMER_E500 (1 << 1) /* Enable e500 support */ +#define PPC_DECR_UNDERFLOW_TRIGGERED (1 << 2) /* Decr interrupt triggered when + * the most significant bit + * changes from 0 to 1. + */ +#define PPC_DECR_ZERO_TRIGGERED (1 << 3) /* Decr interrupt triggered when + * the decrementer reaches zero. + */ + +uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset); clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq); /* Embedded PowerPC DCR management */ typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn); @@ -19,7 +51,7 @@ int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn), int (*dcr_write_error)(int dcrn)); int ppc_dcr_register (CPUState *env, int dcrn, void *opaque, dcr_read_cb drc_read, dcr_write_cb dcr_write); -clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq, +clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq, unsigned int decr_excp); /* Embedded PowerPC reset */ @@ -55,3 +87,6 @@ enum { #define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) #define PPC_SERIAL_MM_BAUDBASE 399193 + +/* ppc_booke.c */ +void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags); diff --git a/hw/ppc405_boards.c b/hw/ppc405_boards.c index ca65ac3..9136288 100644 --- a/hw/ppc405_boards.c +++ b/hw/ppc405_boards.c @@ -213,7 +213,8 @@ static void ref405ep_init (ram_addr_t ram_size, sram_size = 512 * 1024; sram_offset = qemu_ram_alloc(NULL, "ef405ep.sram", sram_size); #ifdef DEBUG_BOARD_INIT - printf("%s: register SRAM at offset %08lx\n", __func__, sram_offset); + printf("%s: register SRAM at offset " RAM_ADDR_FMT "\n", + __func__, sram_offset); #endif cpu_register_physical_memory(0xFFF00000, sram_size, sram_offset | IO_MEM_RAM); @@ -357,7 +358,7 @@ static void ref405ep_init (ram_addr_t ram_size, #ifdef DEBUG_BOARD_INIT printf("%s: Done\n", __func__); #endif - printf("bdloc %016lx\n", (unsigned long)bdloc); + printf("bdloc " RAM_ADDR_FMT "\n", bdloc); } static QEMUMachine ref405ep_machine = { diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c index 9228939..b734e3a 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -44,6 +44,8 @@ static int bamboo_load_device_tree(target_phys_addr_t addr, char *filename; int fdt_size; void *fdt; + uint32_t tb_freq = 400000000; + uint32_t clock_freq = 400000000; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -77,8 +79,18 @@ static int bamboo_load_device_tree(target_phys_addr_t addr, if (ret < 0) fprintf(stderr, "couldn't set /chosen/bootargs\n"); - if (kvm_enabled()) - kvmppc_fdt_update(fdt); + /* Copy data from the host device tree into the guest. Since the guest can + * directly access the timebase without host involvement, we must expose + * the correct frequencies. */ + if (kvm_enabled()) { + tb_freq = kvmppc_get_tbfreq(); + clock_freq = kvmppc_get_clockfreq(); + } + + qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", + clock_freq); + qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", + tb_freq); ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c index 349f046..d18caa4 100644 --- a/hw/ppc4xx_devs.c +++ b/hw/ppc4xx_devs.c @@ -56,7 +56,7 @@ CPUState *ppc4xx_init (const char *cpu_model, cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ cpu_clk->opaque = env; /* Set time-base frequency to sysclk */ - tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT); + tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT); tb_clk->opaque = env; ppc_dcr_init(env, NULL, NULL); /* Register qemu callbacks */ diff --git a/hw/ppc_booke.c b/hw/ppc_booke.c new file mode 100644 index 0000000..8871945 --- /dev/null +++ b/hw/ppc_booke.c @@ -0,0 +1,254 @@ +/* + * QEMU PowerPC Booke hardware System Emulator + * + * Copyright (c) 2011 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 "hw.h" +#include "ppc.h" +#include "qemu-timer.h" +#include "sysemu.h" +#include "nvram.h" +#include "qemu-log.h" +#include "loader.h" + + +/* Timer Control Register */ + +#define TCR_WP_SHIFT 30 /* Watchdog Timer Period */ +#define TCR_WP_MASK (0x3 << TCR_WP_SHIFT) +#define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */ +#define TCR_WRC_MASK (0x3 << TCR_WRC_SHIFT) +#define TCR_WIE (1 << 27) /* Watchdog Timer Interrupt Enable */ +#define TCR_DIE (1 << 26) /* Decrementer Interrupt Enable */ +#define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */ +#define TCR_FP_MASK (0x3 << TCR_FP_SHIFT) +#define TCR_FIE (1 << 23) /* Fixed-Interval Timer Interrupt Enable */ +#define TCR_ARE (1 << 22) /* Auto-Reload Enable */ + +/* Timer Control Register (e500 specific fields) */ + +#define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */ +#define TCR_E500_FPEXT_MASK (0xf << TCR_E500_FPEXT_SHIFT) +#define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */ +#define TCR_E500_WPEXT_MASK (0xf << TCR_E500_WPEXT_SHIFT) + +/* Timer Status Register */ + +#define TSR_FIS (1 << 26) /* Fixed-Interval Timer Interrupt Status */ +#define TSR_DIS (1 << 27) /* Decrementer Interrupt Status */ +#define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */ +#define TSR_WRS_MASK (0x3 << TSR_WRS_SHIFT) +#define TSR_WIS (1 << 30) /* Watchdog Timer Interrupt Status */ +#define TSR_ENW (1 << 31) /* Enable Next Watchdog Timer */ + +typedef struct booke_timer_t booke_timer_t; +struct booke_timer_t { + + uint64_t fit_next; + struct QEMUTimer *fit_timer; + + uint64_t wdt_next; + struct QEMUTimer *wdt_timer; + + uint32_t flags; +}; + +static void booke_update_irq(CPUState *env) +{ + ppc_set_irq(env, PPC_INTERRUPT_DECR, + (env->spr[SPR_BOOKE_TSR] & TSR_DIS + && env->spr[SPR_BOOKE_TCR] & TCR_DIE)); + + ppc_set_irq(env, PPC_INTERRUPT_WDT, + (env->spr[SPR_BOOKE_TSR] & TSR_WIS + && env->spr[SPR_BOOKE_TCR] & TCR_WIE)); + + ppc_set_irq(env, PPC_INTERRUPT_FIT, + (env->spr[SPR_BOOKE_TSR] & TSR_FIS + && env->spr[SPR_BOOKE_TCR] & TCR_FIE)); +} + +/* Return the location of the bit of time base at which the FIT will raise an + interrupt */ +static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env) +{ + uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT; + + if (tb_env->flags & PPC_TIMER_E500) { + /* e500 Fixed-interval timer period extension */ + uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK) + >> TCR_E500_FPEXT_SHIFT; + fp = 63 - (fp | fpext << 2); + } else { + fp = env->fit_period[fp]; + } + + return fp; +} + +/* Return the location of the bit of time base at which the WDT will raise an + interrupt */ +static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env) +{ + uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT; + + if (tb_env->flags & PPC_TIMER_E500) { + /* e500 Watchdog timer period extension */ + uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK) + >> TCR_E500_WPEXT_SHIFT; + wp = 63 - (wp | wpext << 2); + } else { + wp = env->wdt_period[wp]; + } + + return wp; +} + +static void booke_update_fixed_timer(CPUState *env, + uint8_t target_bit, + uint64_t *next, + struct QEMUTimer *timer) +{ + ppc_tb_t *tb_env = env->tb_env; + uint64_t lapse; + uint64_t tb; + uint64_t period = 1 << (target_bit + 1); + uint64_t now; + + now = qemu_get_clock_ns(vm_clock); + tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset); + + lapse = period - ((tb - (1 << target_bit)) & (period - 1)); + + *next = now + muldiv64(lapse, get_ticks_per_sec(), tb_env->tb_freq); + + /* XXX: If expire time is now. We can't run the callback because we don't + * have access to it. So we just set the timer one nanosecond later. + */ + + if (*next == now) { + (*next)++; + } + + qemu_mod_timer(timer, *next); +} + +static void booke_decr_cb(void *opaque) +{ + CPUState *env = opaque; + + env->spr[SPR_BOOKE_TSR] |= TSR_DIS; + booke_update_irq(env); + + if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) { + /* Auto Reload */ + cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]); + } +} + +static void booke_fit_cb(void *opaque) +{ + CPUState *env; + ppc_tb_t *tb_env; + booke_timer_t *booke_timer; + + env = opaque; + tb_env = env->tb_env; + booke_timer = tb_env->opaque; + env->spr[SPR_BOOKE_TSR] |= TSR_FIS; + + booke_update_irq(env); + + booke_update_fixed_timer(env, + booke_get_fit_target(env, tb_env), + &booke_timer->fit_next, + booke_timer->fit_timer); +} + +static void booke_wdt_cb(void *opaque) +{ + CPUState *env; + ppc_tb_t *tb_env; + booke_timer_t *booke_timer; + + env = opaque; + tb_env = env->tb_env; + booke_timer = tb_env->opaque; + + /* TODO: There's lots of complicated stuff to do here */ + + booke_update_irq(env); + + booke_update_fixed_timer(env, + booke_get_wdt_target(env, tb_env), + &booke_timer->wdt_next, + booke_timer->wdt_timer); +} + +void store_booke_tsr(CPUState *env, target_ulong val) +{ + env->spr[SPR_BOOKE_TSR] &= ~val; + booke_update_irq(env); +} + +void store_booke_tcr(CPUState *env, target_ulong val) +{ + ppc_tb_t *tb_env = env->tb_env; + booke_timer_t *booke_timer = tb_env->opaque; + + tb_env = env->tb_env; + env->spr[SPR_BOOKE_TCR] = val; + + booke_update_irq(env); + + booke_update_fixed_timer(env, + booke_get_fit_target(env, tb_env), + &booke_timer->fit_next, + booke_timer->fit_timer); + + booke_update_fixed_timer(env, + booke_get_wdt_target(env, tb_env), + &booke_timer->wdt_next, + booke_timer->wdt_timer); + +} + +void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags) +{ + ppc_tb_t *tb_env; + booke_timer_t *booke_timer; + + tb_env = g_malloc0(sizeof(ppc_tb_t)); + booke_timer = g_malloc0(sizeof(booke_timer_t)); + + env->tb_env = tb_env; + tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; + + tb_env->tb_freq = freq; + tb_env->decr_freq = freq; + tb_env->opaque = booke_timer; + tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, env); + + booke_timer->fit_timer = + qemu_new_timer_ns(vm_clock, &booke_fit_cb, env); + booke_timer->wdt_timer = + qemu_new_timer_ns(vm_clock, &booke_wdt_cb, env); +} diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h index 7351bb6..af75e45 100644 --- a/hw/ppc_mac.h +++ b/hw/ppc_mac.h @@ -77,46 +77,4 @@ void macio_nvram_setup_bar(MacIONVRAMState *s, MemoryRegion *bar, void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len); uint32_t macio_nvram_read (void *opaque, uint32_t addr); void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val); - -/* adb.c */ - -#define MAX_ADB_DEVICES 16 - -#define ADB_MAX_OUT_LEN 16 - -typedef struct ADBDevice ADBDevice; - -/* buf = NULL means polling */ -typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out, - const uint8_t *buf, int len); -typedef int ADBDeviceReset(ADBDevice *d); - -struct ADBDevice { - struct ADBBusState *bus; - int devaddr; - int handler; - ADBDeviceRequest *devreq; - ADBDeviceReset *devreset; - void *opaque; -}; - -typedef struct ADBBusState { - ADBDevice devices[MAX_ADB_DEVICES]; - int nb_devices; - int poll_index; -} ADBBusState; - -int adb_request(ADBBusState *s, uint8_t *buf_out, - const uint8_t *buf, int len); -int adb_poll(ADBBusState *s, uint8_t *buf_out); - -ADBDevice *adb_register_device(ADBBusState *s, int devaddr, - ADBDeviceRequest *devreq, - ADBDeviceReset *devreset, - void *opaque); -void adb_kbd_init(ADBBusState *bus); -void adb_mouse_init(ADBBusState *bus); - -extern ADBBusState adb_bus; - #endif /* !defined(__PPC_MAC_H__) */ diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index b1cc3d7..b9a50db 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -49,6 +49,7 @@ #include "hw.h" #include "ppc.h" #include "ppc_mac.h" +#include "adb.h" #include "mac_dbdma.h" #include "nvram.h" #include "pc.h" diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index fa2c1e7..ebcaafa 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -26,6 +26,7 @@ #include "hw.h" #include "ppc.h" #include "ppc_mac.h" +#include "adb.h" #include "mac_dbdma.h" #include "nvram.h" #include "pc.h" diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 28acf0b..5bf8eab 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -14,8 +14,6 @@ * (at your option) any later version. */ -#include <dirent.h> - #include "config.h" #include "qemu-common.h" #include "net.h" @@ -52,6 +50,7 @@ #define MPC8544_PCI_IO 0xE1000000 #define MPC8544_PCI_IOLEN 0x10000 #define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000) +#define MPC8544_SPIN_BASE 0xEF000000 struct boot_info { @@ -59,30 +58,6 @@ struct boot_info uint32_t entry; }; -#ifdef CONFIG_FDT -static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop) -{ - uint32_t cell; - int ret; - - ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell)); - if (ret < 0) { - fprintf(stderr, "couldn't read host %s/%s\n", node, prop); - goto out; - } - - ret = qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", - prop, cell); - if (ret < 0) { - fprintf(stderr, "couldn't set guest /cpus/PowerPC,8544@0/%s\n", prop); - goto out; - } - -out: - return ret; -} -#endif - static int mpc8544_load_device_tree(CPUState *env, target_phys_addr_t addr, uint32_t ramsize, @@ -97,6 +72,9 @@ static int mpc8544_load_device_tree(CPUState *env, int fdt_size; void *fdt; uint8_t hypercall[16]; + uint32_t clock_freq = 400000000; + uint32_t tb_freq = 400000000; + int i; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -134,32 +112,9 @@ static int mpc8544_load_device_tree(CPUState *env, fprintf(stderr, "couldn't set /chosen/bootargs\n"); if (kvm_enabled()) { - struct dirent *dirp; - DIR *dp; - char buf[128]; - - if ((dp = opendir("/proc/device-tree/cpus/")) == NULL) { - printf("Can't open directory /proc/device-tree/cpus/\n"); - ret = -1; - goto out; - } - - buf[0] = '\0'; - while ((dirp = readdir(dp)) != NULL) { - if (strncmp(dirp->d_name, "PowerPC", 7) == 0) { - snprintf(buf, 128, "/cpus/%s", dirp->d_name); - break; - } - } - closedir(dp); - if (buf[0] == '\0') { - printf("Unknow host!\n"); - ret = -1; - goto out; - } - - mpc8544_copy_soc_cell(fdt, buf, "clock-frequency"); - mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency"); + /* Read out host's frequencies */ + clock_freq = kvmppc_get_clockfreq(); + tb_freq = kvmppc_get_tbfreq(); /* indicate KVM hypercall interface */ qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible", @@ -167,13 +122,45 @@ static int mpc8544_load_device_tree(CPUState *env, kvmppc_get_hypercall(env, hypercall, sizeof(hypercall)); qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions", hypercall, sizeof(hypercall)); - } else { - const uint32_t freq = 400000000; + } - qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", - "clock-frequency", freq); - qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0", - "timebase-frequency", freq); + /* We need to generate the cpu nodes in reverse order, so Linux can pick + the first node as boot node and be happy */ + for (i = smp_cpus - 1; i >= 0; i--) { + char cpu_name[128]; + uint64_t cpu_release_addr = cpu_to_be64(MPC8544_SPIN_BASE + (i * 0x20)); + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->cpu_index == i) { + break; + } + } + + if (!env) { + continue; + } + + snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", env->cpu_index); + qemu_devtree_add_subnode(fdt, cpu_name); + qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); + qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); + qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu"); + qemu_devtree_setprop_cell(fdt, cpu_name, "reg", env->cpu_index); + qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size", + env->dcache_line_size); + qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size", + env->icache_line_size); + qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000); + qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000); + qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0); + if (env->cpu_index) { + qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled"); + qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table"); + qemu_devtree_setprop(fdt, cpu_name, "cpu-release-addr", + &cpu_release_addr, sizeof(cpu_release_addr)); + } else { + qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay"); + } } ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); @@ -188,7 +175,7 @@ out: /* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) { - return (ffs(size >> 10) - 1) >> 1; + return ffs(size >> 10) - 1; } static void mmubooke_create_initial_mapping(CPUState *env, @@ -203,6 +190,20 @@ static void mmubooke_create_initial_mapping(CPUState *env, tlb->mas2 = va & TARGET_PAGE_MASK; tlb->mas7_3 = pa & TARGET_PAGE_MASK; tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; + + env->tlb_dirty = true; +} + +static void mpc8544ds_cpu_reset_sec(void *opaque) +{ + CPUState *env = opaque; + + cpu_reset(env); + + /* Secondary CPU starts in halted state for now. Needs to change when + implementing non-kernel boot. */ + env->halted = 1; + env->exception_index = EXCP_HLT; } static void mpc8544ds_cpu_reset(void *opaque) @@ -213,6 +214,7 @@ static void mpc8544ds_cpu_reset(void *opaque) cpu_reset(env); /* Set initial guest state. */ + env->halted = 0; env->gpr[1] = (16<<20) - 8; env->gpr[3] = bi->dt_base; env->nip = bi->entry; @@ -228,7 +230,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, { MemoryRegion *address_space_mem = get_system_memory(); PCIBus *pci_bus; - CPUState *env; + CPUState *env = NULL; uint64_t elf_entry; uint64_t elf_lowaddr; target_phys_addr_t entry=0; @@ -239,27 +241,51 @@ static void mpc8544ds_init(ram_addr_t ram_size, target_long initrd_size=0; int i=0; unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; - qemu_irq *irqs, *mpic; + qemu_irq **irqs, *mpic; DeviceState *dev; - struct boot_info *boot_info; + CPUState *firstenv = NULL; - /* Setup CPU */ + /* Setup CPUs */ if (cpu_model == NULL) { cpu_model = "e500v2_v30"; } - env = cpu_ppc_init(cpu_model); - if (!env) { - fprintf(stderr, "Unable to initialize CPU!\n"); - exit(1); - } + irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); + irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); + for (i = 0; i < smp_cpus; i++) { + qemu_irq *input; + env = cpu_ppc_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to initialize CPU!\n"); + exit(1); + } - /* XXX register timer? */ - ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR); - ppc_dcr_init(env, NULL, NULL); + if (!firstenv) { + firstenv = env; + } + + irqs[i] = irqs[0] + (i * OPENPIC_OUTPUT_NB); + input = (qemu_irq *)env->irq_inputs; + irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; + irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; + env->spr[SPR_BOOKE_PIR] = env->cpu_index = i; + + ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500); + + /* Register reset handler */ + if (!i) { + /* Primary CPU */ + struct boot_info *boot_info; + boot_info = g_malloc0(sizeof(struct boot_info)); + qemu_register_reset(mpc8544ds_cpu_reset, env); + env->load_info = boot_info; + } else { + /* Secondary CPUs */ + qemu_register_reset(mpc8544ds_cpu_reset_sec, env); + } + } - /* Register reset handler */ - qemu_register_reset(mpc8544ds_cpu_reset, env); + env = firstenv; /* Fixup Memory size on a alignment boundary */ ram_size &= ~(RAM_SIZES_ALIGN - 1); @@ -269,10 +295,12 @@ static void mpc8544ds_init(ram_addr_t ram_size, "mpc8544ds.ram", ram_size)); /* MPIC */ - irqs = g_malloc0(sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); - irqs[OPENPIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_INT]; - irqs[OPENPIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_CINT]; - mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE, 1, &irqs, NULL); + mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE, + smp_cpus, irqs, NULL); + + if (!mpic) { + cpu_abort(env, "MPIC failed to initialize\n"); + } /* Serial */ if (serial_hds[0]) { @@ -308,6 +336,9 @@ static void mpc8544ds_init(ram_addr_t ram_size, } } + /* Register spinning region */ + sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL); + /* Load kernel. */ if (kernel_filename) { kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); @@ -338,10 +369,10 @@ static void mpc8544ds_init(ram_addr_t ram_size, } } - boot_info = g_malloc0(sizeof(struct boot_info)); - /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { + struct boot_info *boot_info; + #ifndef CONFIG_FDT cpu_abort(env, "Compiled without FDT support - can't load kernel\n"); #endif @@ -352,10 +383,10 @@ static void mpc8544ds_init(ram_addr_t ram_size, exit(1); } + boot_info = env->load_info; boot_info->entry = entry; boot_info->dt_base = dt_base; } - env->load_info = boot_info; if (kvm_enabled()) { kvmppc_init(); @@ -366,6 +397,7 @@ static QEMUMachine mpc8544ds_machine = { .name = "mpc8544ds", .desc = "mpc8544ds", .init = mpc8544ds_init, + .max_cpus = 15, }; static void mpc8544ds_machine_init(void) diff --git a/hw/ppce500_spin.c b/hw/ppce500_spin.c new file mode 100644 index 0000000..cccd940 --- /dev/null +++ b/hw/ppce500_spin.c @@ -0,0 +1,215 @@ +/* + * QEMU PowerPC e500v2 ePAPR spinning code + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Alexander Graf, <agraf@suse.de> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + * This code is not really a device, but models an interface that usually + * firmware takes care of. It's used when QEMU plays the role of firmware. + * + * Specification: + * + * https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf + * + */ + +#include "hw.h" +#include "sysemu.h" +#include "sysbus.h" +#include "kvm.h" + +#define MAX_CPUS 32 + +typedef struct spin_info { + uint64_t addr; + uint64_t r3; + uint32_t resv; + uint32_t pir; + uint64_t reserved; +} __attribute__ ((packed)) SpinInfo; + +typedef struct spin_state { + SysBusDevice busdev; + MemoryRegion iomem; + SpinInfo spin[MAX_CPUS]; +} SpinState; + +typedef struct spin_kick { + CPUState *env; + SpinInfo *spin; +} SpinKick; + +static void spin_reset(void *opaque) +{ + SpinState *s = opaque; + int i; + + for (i = 0; i < MAX_CPUS; i++) { + SpinInfo *info = &s->spin[i]; + + info->pir = i; + info->r3 = i; + info->addr = 1; + } +} + +/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */ +static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size) +{ + return (ffs(size >> 10) - 1) >> 1; +} + +static void mmubooke_create_initial_mapping(CPUState *env, + target_ulong va, + target_phys_addr_t pa, + target_phys_addr_t len) +{ + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1); + target_phys_addr_t size; + + size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT); + tlb->mas1 = MAS1_VALID | size; + tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M; + tlb->mas7_3 = pa & TARGET_PAGE_MASK; + tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; +} + +static void spin_kick(void *data) +{ + SpinKick *kick = data; + CPUState *env = kick->env; + SpinInfo *curspin = kick->spin; + target_phys_addr_t map_size = 64 * 1024 * 1024; + target_phys_addr_t map_start; + + cpu_synchronize_state(env); + stl_p(&curspin->pir, env->spr[SPR_PIR]); + env->nip = ldq_p(&curspin->addr) & (map_size - 1); + env->gpr[3] = ldq_p(&curspin->r3); + env->gpr[4] = 0; + env->gpr[5] = 0; + env->gpr[6] = 0; + env->gpr[7] = map_size; + env->gpr[8] = 0; + env->gpr[9] = 0; + + map_start = ldq_p(&curspin->addr) & ~(map_size - 1); + mmubooke_create_initial_mapping(env, 0, map_start, map_size); + + env->halted = 0; + env->exception_index = -1; + qemu_cpu_kick(env); +} + +static void spin_write(void *opaque, target_phys_addr_t addr, uint64_t value, + unsigned len) +{ + SpinState *s = opaque; + int env_idx = addr / sizeof(SpinInfo); + CPUState *env; + SpinInfo *curspin = &s->spin[env_idx]; + uint8_t *curspin_p = (uint8_t*)curspin; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (env->cpu_index == env_idx) { + break; + } + } + + if (!env) { + /* Unknown CPU */ + return; + } + + if (!env->cpu_index) { + /* primary CPU doesn't spin */ + return; + } + + curspin_p = &curspin_p[addr % sizeof(SpinInfo)]; + switch (len) { + case 1: + stb_p(curspin_p, value); + break; + case 2: + stw_p(curspin_p, value); + break; + case 4: + stl_p(curspin_p, value); + break; + } + + if (!(ldq_p(&curspin->addr) & 1)) { + /* run CPU */ + SpinKick kick = { + .env = env, + .spin = curspin, + }; + + run_on_cpu(env, spin_kick, &kick); + } +} + +static uint64_t spin_read(void *opaque, target_phys_addr_t addr, unsigned len) +{ + SpinState *s = opaque; + uint8_t *spin_p = &((uint8_t*)s->spin)[addr]; + + switch (len) { + case 1: + return ldub_p(spin_p); + case 2: + return lduw_p(spin_p); + case 4: + return ldl_p(spin_p); + default: + assert(0); + } +} + +const MemoryRegionOps spin_rw_ops = { + .read = spin_read, + .write = spin_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static int ppce500_spin_initfn(SysBusDevice *dev) +{ + SpinState *s; + + s = FROM_SYSBUS(SpinState, sysbus_from_qdev(dev)); + + memory_region_init_io(&s->iomem, &spin_rw_ops, s, "e500 spin pv device", + sizeof(SpinInfo) * MAX_CPUS); + sysbus_init_mmio_region(dev, &s->iomem); + + qemu_register_reset(spin_reset, s); + + return 0; +} + +static SysBusDeviceInfo ppce500_spin_info = { + .init = ppce500_spin_initfn, + .qdev.name = "e500-spin", + .qdev.size = sizeof(SpinState), +}; + +static void ppce500_spin_register(void) +{ + sysbus_register_withprop(&ppce500_spin_info); +} +device_init(ppce500_spin_register); @@ -38,6 +38,9 @@ #include "hw/spapr_vio.h" #include "hw/xics.h" +#include "kvm.h" +#include "kvm_ppc.h" + #include <libfdt.h> #define KERNEL_LOAD_ADDR 0x00000000 @@ -54,8 +57,34 @@ #define MAX_CPUS 256 #define XICS_IRQS 1024 +#define PHANDLE_XICP 0x00001111 + sPAPREnvironment *spapr; +qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num) +{ + uint32_t irq; + qemu_irq qirq; + + if (hint) { + irq = hint; + /* FIXME: we should probably check for collisions somehow */ + } else { + irq = spapr->next_irq++; + } + + qirq = xics_find_qirq(spapr->icp, irq); + if (!qirq) { + return NULL; + } + + if (irq_num) { + *irq_num = irq; + } + + return qirq; +} + static void *spapr_create_fdt_skel(const char *cpu_model, target_phys_addr_t initrd_base, target_phys_addr_t initrd_size, @@ -70,7 +99,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)}; char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt" - "\0hcall-tce\0hcall-vio\0hcall-splpar"; + "\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk"; uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; int i; char *modelname; @@ -137,6 +166,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, char *nodename; uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), 0xffffffff, 0xffffffff}; + uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ; + uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; if (asprintf(&nodename, "%s@%x", modelname, index) < 0) { fprintf(stderr, "Allocation failure\n"); @@ -155,10 +186,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model, env->dcache_line_size))); _FDT((fdt_property_cell(fdt, "icache-block-size", env->icache_line_size))); - _FDT((fdt_property_cell(fdt, "timebase-frequency", TIMEBASE_FREQ))); - /* Hardcode CPU frequency for now. It's kind of arbitrary on - * full emu, for kvm we should copy it from the host */ - _FDT((fdt_property_cell(fdt, "clock-frequency", 1000000000))); + _FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq))); + _FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq))); _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr))); _FDT((fdt_property(fdt, "ibm,pft-size", pft_size_prop, sizeof(pft_size_prop)))); @@ -189,16 +218,18 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_end_node(fdt))); /* interrupt controller */ - _FDT((fdt_begin_node(fdt, "interrupt-controller@0"))); + _FDT((fdt_begin_node(fdt, "interrupt-controller"))); _FDT((fdt_property_string(fdt, "device_type", "PowerPC-External-Interrupt-Presentation"))); _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp"))); - _FDT((fdt_property_cell(fdt, "reg", 0))); _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", interrupt_server_ranges_prop, sizeof(interrupt_server_ranges_prop)))); + _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); + _FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP))); + _FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP))); _FDT((fdt_end_node(fdt))); @@ -298,7 +329,6 @@ static void ppc_spapr_init(ram_addr_t ram_size, long kernel_size, initrd_size, fw_size; long pteg_shift = 17; char *filename; - int irq = 16; spapr = g_malloc(sizeof(*spapr)); cpu_ppc_hypercall = emulate_spapr_hypercall; @@ -330,19 +360,29 @@ static void ppc_spapr_init(ram_addr_t ram_size, } /* allocate RAM */ - ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size); + spapr->ram_limit = ram_size; + ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", spapr->ram_limit); cpu_register_physical_memory(0, ram_size, ram_offset); /* allocate hash page table. For now we always make this 16mb, * later we should probably make it scale to the size of guest * RAM */ spapr->htab_size = 1ULL << (pteg_shift + 7); - spapr->htab = g_malloc(spapr->htab_size); + spapr->htab = qemu_memalign(spapr->htab_size, spapr->htab_size); for (env = first_cpu; env != NULL; env = env->next_cpu) { env->external_htab = spapr->htab; env->htab_base = -1; env->htab_mask = spapr->htab_size - 1; + + /* Tell KVM that we're in PAPR mode */ + env->spr[SPR_SDR1] = (unsigned long)spapr->htab | + ((pteg_shift + 7) - 18); + env->spr[SPR_HIOR] = 0; + + if (kvm_enabled()) { + kvmppc_set_papr(env); + } } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin"); @@ -356,19 +396,19 @@ static void ppc_spapr_init(ram_addr_t ram_size, /* Set up Interrupt Controller */ spapr->icp = xics_system_init(XICS_IRQS); + spapr->next_irq = 16; /* Set up VIO bus */ spapr->vio_bus = spapr_vio_bus_init(); - for (i = 0; i < MAX_SERIAL_PORTS; i++, irq++) { + for (i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { spapr_vty_create(spapr->vio_bus, SPAPR_VTY_BASE_ADDRESS + i, - serial_hds[i], xics_find_qirq(spapr->icp, irq), - irq); + serial_hds[i]); } } - for (i = 0; i < nb_nics; i++, irq++) { + for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; if (!nd->model) { @@ -376,8 +416,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, } if (strcmp(nd->model, "ibmveth") == 0) { - spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd, - xics_find_qirq(spapr->icp, irq), irq); + spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd); } else { fprintf(stderr, "pSeries (sPAPR) platform does not support " "NIC model '%s' (only ibmveth is supported)\n", @@ -387,9 +426,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, } for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) { - spapr_vscsi_create(spapr->vio_bus, 0x2000 + i, - xics_find_qirq(spapr->icp, irq), irq); - irq++; + spapr_vscsi_create(spapr->vio_bus, 0x2000 + i); } if (kernel_filename) { @@ -430,7 +467,7 @@ static void ppc_spapr_init(ram_addr_t ram_size, "%ldM guest RAM\n", MIN_RAM_SLOF); exit(1); } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin"); + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, FW_FILE_NAME); fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); if (fw_size < 0) { hw_error("qemu: could not load LPAR rtas '%s'\n", filename); @@ -1,6 +1,8 @@ #if !defined(__HW_SPAPR_H__) #define __HW_SPAPR_H__ +#include "hw/xics.h" + struct VIOsPAPRBus; struct icp_state; @@ -8,12 +10,15 @@ typedef struct sPAPREnvironment { struct VIOsPAPRBus *vio_bus; struct icp_state *icp; + target_phys_addr_t ram_limit; void *htab; long htab_size; target_phys_addr_t fdt_addr, rtas_addr; long rtas_size; void *fdt_skel; target_ulong entry_point; + int next_irq; + int rtc_offset; } sPAPREnvironment; #define H_SUCCESS 0 @@ -278,6 +283,8 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); target_ulong spapr_hypercall(CPUState *env, target_ulong opcode, target_ulong *args); +qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num); + static inline uint32_t rtas_ld(target_ulong phys, int n) { return ldl_be_phys(phys + 4*n); diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index f7ead04..84281be 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -99,6 +99,8 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, target_ulong pte_index = args[1]; target_ulong pteh = args[2]; target_ulong ptel = args[3]; + target_ulong page_shift = 12; + target_ulong raddr; target_ulong i; uint8_t *hpte; @@ -111,6 +113,7 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, #endif if ((ptel & 0xff000) == 0) { /* 16M page */ + page_shift = 24; /* lowest AVA bit must be 0 for 16M pages */ if (pteh & 0x80) { return H_PARAMETER; @@ -120,12 +123,23 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, } } - /* FIXME: bounds check the pa? */ + raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1); - /* Check WIMG */ - if ((ptel & HPTE_R_WIMG) != HPTE_R_M) { - return H_PARAMETER; + if (raddr < spapr->ram_limit) { + /* Regular RAM - should have WIMG=0010 */ + if ((ptel & HPTE_R_WIMG) != HPTE_R_M) { + return H_PARAMETER; + } + } else { + /* Looks like an IO address */ + /* FIXME: What WIMG combinations could be sensible for IO? + * For now we allow WIMG=010x, but are there others? */ + /* FIXME: Should we check against registered IO addresses? */ + if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) { + return H_PARAMETER; + } } + pteh &= ~0x60ULL; if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { @@ -160,20 +174,26 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) +enum { + REMOVE_SUCCESS = 0, + REMOVE_NOT_FOUND = 1, + REMOVE_PARM = 2, + REMOVE_HW = 3, +}; + +static target_ulong remove_hpte(CPUState *env, target_ulong ptex, + target_ulong avpn, + target_ulong flags, + target_ulong *vp, target_ulong *rp) { - target_ulong flags = args[0]; - target_ulong pte_index = args[1]; - target_ulong avpn = args[2]; uint8_t *hpte; target_ulong v, r, rb; - if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { - return H_PARAMETER; + if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { + return REMOVE_PARM; } - hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); + hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64); while (!lock_hpte(hpte, HPTE_V_HVLOCK)) { /* We have no real concurrency in qemu soft-emulation, so we * will never actually have a contested lock */ @@ -188,14 +208,106 @@ static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, ((flags & H_ANDCOND) && (v & avpn) != 0)) { stq_p(hpte, v & ~HPTE_V_HVLOCK); assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); - return H_NOT_FOUND; + return REMOVE_NOT_FOUND; } - args[0] = v & ~HPTE_V_HVLOCK; - args[1] = r; + *vp = v & ~HPTE_V_HVLOCK; + *rp = r; stq_p(hpte, 0); - rb = compute_tlbie_rb(v, r, pte_index); + rb = compute_tlbie_rb(v, r, ptex); ppc_tlb_invalidate_one(env, rb); assert(!(ldq_p(hpte) & HPTE_V_HVLOCK)); + return REMOVE_SUCCESS; +} + +static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong pte_index = args[1]; + target_ulong avpn = args[2]; + int ret; + + ret = remove_hpte(env, pte_index, avpn, flags, + &args[0], &args[1]); + + switch (ret) { + case REMOVE_SUCCESS: + return H_SUCCESS; + + case REMOVE_NOT_FOUND: + return H_NOT_FOUND; + + case REMOVE_PARM: + return H_PARAMETER; + + case REMOVE_HW: + return H_HARDWARE; + } + + assert(0); +} + +#define H_BULK_REMOVE_TYPE 0xc000000000000000ULL +#define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL +#define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL +#define H_BULK_REMOVE_END 0xc000000000000000ULL +#define H_BULK_REMOVE_CODE 0x3000000000000000ULL +#define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL +#define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL +#define H_BULK_REMOVE_PARM 0x2000000000000000ULL +#define H_BULK_REMOVE_HW 0x3000000000000000ULL +#define H_BULK_REMOVE_RC 0x0c00000000000000ULL +#define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL +#define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL +#define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL +#define H_BULK_REMOVE_AVPN 0x0200000000000000ULL +#define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL + +#define H_BULK_REMOVE_MAX_BATCH 4 + +static target_ulong h_bulk_remove(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + int i; + + for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { + target_ulong *tsh = &args[i*2]; + target_ulong tsl = args[i*2 + 1]; + target_ulong v, r, ret; + + if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) { + break; + } else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) { + return H_PARAMETER; + } + + *tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS; + *tsh |= H_BULK_REMOVE_RESPONSE; + + if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) { + *tsh |= H_BULK_REMOVE_PARM; + return H_PARAMETER; + } + + ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl, + (*tsh & H_BULK_REMOVE_FLAGS) >> 26, + &v, &r); + + *tsh |= ret << 60; + + switch (ret) { + case REMOVE_SUCCESS: + *tsh |= (r & (HPTE_R_C | HPTE_R_R)) << 43; + break; + + case REMOVE_PARM: + return H_PARAMETER; + + case REMOVE_HW: + return H_HARDWARE; + } + } + return H_SUCCESS; } @@ -449,6 +561,67 @@ static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr, nret, rtas_r3 + 12 + 4*nargs); } +static target_ulong h_logical_load(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong size = args[0]; + target_ulong addr = args[1]; + + switch (size) { + case 1: + args[0] = ldub_phys(addr); + return H_SUCCESS; + case 2: + args[0] = lduw_phys(addr); + return H_SUCCESS; + case 4: + args[0] = ldl_phys(addr); + return H_SUCCESS; + case 8: + args[0] = ldq_phys(addr); + return H_SUCCESS; + } + return H_PARAMETER; +} + +static target_ulong h_logical_store(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong size = args[0]; + target_ulong addr = args[1]; + target_ulong val = args[2]; + + switch (size) { + case 1: + stb_phys(addr, val); + return H_SUCCESS; + case 2: + stw_phys(addr, val); + return H_SUCCESS; + case 4: + stl_phys(addr, val); + return H_SUCCESS; + case 8: + stq_phys(addr, val); + return H_SUCCESS; + } + return H_PARAMETER; +} + +static target_ulong h_logical_icbi(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + /* Nothing to do on emulation, KVM will trap this in the kernel */ + return H_SUCCESS; +} + +static target_ulong h_logical_dcbf(CPUState *env, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + /* Nothing to do on emulation, KVM will trap this in the kernel */ + return H_SUCCESS; +} + static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; @@ -506,6 +679,9 @@ static void hypercall_init(void) spapr_register_hypercall(H_REMOVE, h_remove); spapr_register_hypercall(H_PROTECT, h_protect); + /* hcall-bulk */ + spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); + /* hcall-dabr */ spapr_register_hypercall(H_SET_DABR, h_set_dabr); @@ -513,6 +689,18 @@ static void hypercall_init(void) spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate + * here between the "CI" and the "CACHE" variants, they will use whatever + * mapping attributes qemu is using. When using KVM, the kernel will + * enforce the attributes more strongly + */ + spapr_register_hypercall(H_LOGICAL_CI_LOAD, h_logical_load); + spapr_register_hypercall(H_LOGICAL_CI_STORE, h_logical_store); + spapr_register_hypercall(H_LOGICAL_CACHE_LOAD, h_logical_load); + spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store); + spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi); + spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf); + /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); } diff --git a/hw/spapr_llan.c b/hw/spapr_llan.c index c18efc7..abe1297 100644 --- a/hw/spapr_llan.c +++ b/hw/spapr_llan.c @@ -195,11 +195,9 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev) return 0; } -void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd, - qemu_irq qirq, uint32_t vio_irq_num) +void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd) { DeviceState *dev; - VIOsPAPRDevice *sdev; dev = qdev_create(&bus->bus, "spapr-vlan"); qdev_prop_set_uint32(dev, "reg", reg); @@ -207,9 +205,6 @@ void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd, qdev_set_nic_properties(dev, nd); qdev_init_nofail(dev); - sdev = (VIOsPAPRDevice *)dev; - sdev->qirq = qirq; - sdev->vio_irq_num = vio_irq_num; } static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) @@ -500,9 +495,7 @@ static VIOsPAPRDeviceInfo spapr_vlan = { .qdev.name = "spapr-vlan", .qdev.size = sizeof(VIOsPAPRVLANDevice), .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x1000), - DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, rtce_window_size, - 0x10000000), + DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev, 0x1000, 0x10000000), DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), DEFINE_PROP_END_OF_LIST(), }, diff --git a/hw/spapr_rtas.c b/hw/spapr_rtas.c index 00c8ce5..d1ac74c 100644 --- a/hw/spapr_rtas.c +++ b/hw/spapr_rtas.c @@ -67,7 +67,7 @@ static void rtas_get_time_of_day(sPAPREnvironment *spapr, return; } - qemu_get_timedate(&tm, 0); + qemu_get_timedate(&tm, spapr->rtc_offset); rtas_st(rets, 0, 0); /* Success */ rtas_st(rets, 1, tm.tm_year + 1900); @@ -79,6 +79,27 @@ static void rtas_get_time_of_day(sPAPREnvironment *spapr, rtas_st(rets, 7, 0); /* we don't do nanoseconds */ } +static void rtas_set_time_of_day(sPAPREnvironment *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + struct tm tm; + + tm.tm_year = rtas_ld(args, 0) - 1900; + tm.tm_mon = rtas_ld(args, 1) - 1; + tm.tm_mday = rtas_ld(args, 2); + tm.tm_hour = rtas_ld(args, 3); + tm.tm_min = rtas_ld(args, 4); + tm.tm_sec = rtas_ld(args, 5); + + /* Just generate a monitor event for the change */ + rtc_change_mon_event(&tm); + spapr->rtc_offset = qemu_timedate_diff(&tm); + + rtas_st(rets, 0, 0); /* Success */ +} + static void rtas_power_off(sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -271,6 +292,7 @@ static void register_core_rtas(void) { spapr_rtas_register("display-character", rtas_display_character); spapr_rtas_register("get-time-of-day", rtas_get_time_of_day); + spapr_rtas_register("set-time-of-day", rtas_set_time_of_day); spapr_rtas_register("power-off", rtas_power_off); spapr_rtas_register("query-cpu-stopped-state", rtas_query_cpu_stopped_state); diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c index ce6558b..35818e1 100644 --- a/hw/spapr_vio.c +++ b/hw/spapr_vio.c @@ -32,6 +32,7 @@ #include "hw/spapr.h" #include "hw/spapr_vio.h" +#include "hw/xics.h" #ifdef CONFIG_FDT #include <libfdt.h> @@ -51,6 +52,10 @@ static struct BusInfo spapr_vio_bus_info = { .name = "spapr-vio", .size = sizeof(VIOsPAPRBus), + .props = (Property[]) { + DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, vio_irq_num, 0), \ + DEFINE_PROP_END_OF_LIST(), + }, }; VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg) @@ -603,6 +608,11 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo) dev->qdev.id = id; + dev->qirq = spapr_allocate_irq(dev->vio_irq_num, &dev->vio_irq_num); + if (!dev->qirq) { + return -1; + } + rtce_init(dev); return info->init(dev); diff --git a/hw/spapr_vio.h b/hw/spapr_vio.h index 603a8c4..4fe5f74 100644 --- a/hw/spapr_vio.h +++ b/hw/spapr_vio.h @@ -60,6 +60,11 @@ typedef struct VIOsPAPRDevice { VIOsPAPR_CRQ crq; } VIOsPAPRDevice; +#define DEFINE_SPAPR_PROPERTIES(type, field, default_reg, default_dma_window) \ + DEFINE_PROP_UINT32("reg", type, field.reg, default_reg), \ + DEFINE_PROP_UINT32("dma-window", type, field.rtce_window_size, \ + default_dma_window) + typedef struct VIOsPAPRBus { BusState bus; } VIOsPAPRBus; @@ -98,15 +103,9 @@ uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr); int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq); void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len); -void spapr_vty_create(VIOsPAPRBus *bus, - uint32_t reg, CharDriverState *chardev, - qemu_irq qirq, uint32_t vio_irq_num); - -void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd, - qemu_irq qirq, uint32_t vio_irq_num); - -void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg, - qemu_irq qirq, uint32_t vio_irq_num); +void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev); +void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd); +void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg); int spapr_tce_set_bypass(uint32_t unit, uint32_t enable); void spapr_vio_quiesce(void); diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index fc9ac6a..e8426d7 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -483,7 +483,6 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) if (status == CHECK_CONDITION) { req->senselen = scsi_req_get_sense(req->sreq, req->sense, sizeof(req->sense)); - status = 0; dprintf("VSCSI: Sense data, %d bytes:\n", len); dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n", req->sense[0], req->sense[1], req->sense[2], req->sense[3], @@ -893,20 +892,14 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev) return 0; } -void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg, - qemu_irq qirq, uint32_t vio_irq_num) +void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg) { DeviceState *dev; - VIOsPAPRDevice *sdev; dev = qdev_create(&bus->bus, "spapr-vscsi"); qdev_prop_set_uint32(dev, "reg", reg); qdev_init_nofail(dev); - - sdev = (VIOsPAPRDevice *)dev; - sdev->qirq = qirq; - sdev->vio_irq_num = vio_irq_num; } static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) @@ -936,9 +929,7 @@ static VIOsPAPRDeviceInfo spapr_vscsi = { .qdev.name = "spapr-vscsi", .qdev.size = sizeof(VSCSIState), .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000), - DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, - rtce_window_size, 0x10000000), + DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev, 0x2000, 0x10000000), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c index f5046d9..a9d4b03 100644 --- a/hw/spapr_vty.c +++ b/hw/spapr_vty.c @@ -115,20 +115,14 @@ static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr, return H_SUCCESS; } -void spapr_vty_create(VIOsPAPRBus *bus, - uint32_t reg, CharDriverState *chardev, - qemu_irq qirq, uint32_t vio_irq_num) +void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev) { DeviceState *dev; - VIOsPAPRDevice *sdev; dev = qdev_create(&bus->bus, "spapr-vty"); qdev_prop_set_uint32(dev, "reg", reg); qdev_prop_set_chr(dev, "chardev", chardev); qdev_init_nofail(dev); - sdev = (VIOsPAPRDevice *)dev; - sdev->qirq = qirq; - sdev->vio_irq_num = vio_irq_num; } static void vty_hcalls(VIOsPAPRBus *bus) @@ -146,7 +140,7 @@ static VIOsPAPRDeviceInfo spapr_vty = { .qdev.name = "spapr-vty", .qdev.size = sizeof(VIOsPAPRVTYDevice), .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0), + DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev, 0, 0), DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), DEFINE_PROP_END_OF_LIST(), }, diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c index 68bf53a..d31a204 100644 --- a/hw/virtex_ml507.c +++ b/hw/virtex_ml507.c @@ -82,7 +82,6 @@ static void mmubooke_create_initial_mapping(CPUState *env, static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, int do_init, const char *cpu_model, - clk_setup_t *cpu_clk, clk_setup_t *tb_clk, uint32_t sysclk) { CPUState *env; @@ -94,11 +93,7 @@ static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, exit(1); } - cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ - cpu_clk->opaque = env; - /* Set time-base frequency to sysclk */ - tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR); - tb_clk->opaque = env; + ppc_booke_timers_init(env, sysclk, 0/* no flags */); ppc_dcr_init(env, NULL, NULL); @@ -199,7 +194,6 @@ static void virtex_init(ram_addr_t ram_size, DriveInfo *dinfo; ram_addr_t phys_ram; qemu_irq irq[32], *cpu_irq; - clk_setup_t clk_setup[7]; int kernel_size; int i; @@ -209,8 +203,7 @@ static void virtex_init(ram_addr_t ram_size, } memset(clk_setup, 0, sizeof(clk_setup)); - env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0], - &clk_setup[1], 400000000); + env = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000); qemu_register_reset(main_cpu_reset, env); phys_ram = qemu_ram_alloc(NULL, "ram", ram_size); @@ -185,17 +185,17 @@ static int ics_valid_irq(struct ics_state *ics, uint32_t nr) && (nr < (ics->offset + ics->nr_irqs)); } -static void ics_set_irq_msi(void *opaque, int nr, int val) +static void ics_set_irq_msi(void *opaque, int srcno, int val) { struct ics_state *ics = (struct ics_state *)opaque; - struct ics_irq_state *irq = ics->irqs + nr; + struct ics_irq_state *irq = ics->irqs + srcno; if (val) { if (irq->priority == 0xff) { irq->masked_pending = 1; /* masked pending */ ; } else { - icp_irq(ics->icp, irq->server, nr + ics->offset, irq->priority); + icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority); } } } @@ -227,7 +227,7 @@ static void ics_resend_msi(struct ics_state *ics) static void ics_write_xive_msi(struct ics_state *ics, int nr, int server, uint8_t priority) { - struct ics_irq_state *irq = ics->irqs + nr; + struct ics_irq_state *irq = ics->irqs + nr - ics->offset; irq->server = server; irq->priority = priority; @@ -237,7 +237,7 @@ static void ics_write_xive_msi(struct ics_state *ics, int nr, int server, } irq->masked_pending = 0; - icp_irq(ics->icp, server, nr + ics->offset, priority); + icp_irq(ics->icp, server, nr, priority); } static void ics_reject(struct ics_state *ics, int nr) @@ -332,7 +332,7 @@ static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token, return; } - ics_write_xive_msi(ics, nr - ics->offset, server, priority); + ics_write_xive_msi(ics, nr, server, priority); rtas_st(rets, 0, 0); /* Success */ } @@ -386,7 +386,7 @@ static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token, struct ics_irq_state *irq = xics->irqs + (nr - xics->offset); irq->saved_priority = irq->priority; - ics_write_xive_msi(xics, nr - xics->offset, irq->server, 0xff); + ics_write_xive_msi(xics, nr, irq->server, 0xff); #endif rtas_st(rets, 0, 0); /* Success */ @@ -416,8 +416,7 @@ static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token, #if 0 struct ics_irq_state *irq = xics->irqs + (nr - xics->offset); - ics_write_xive_msi(xics, nr - xics->offset, - irq->server, irq->saved_priority); + ics_write_xive_msi(xics, nr, irq->server, irq->saved_priority); #endif rtas_st(rets, 0, 0); /* Success */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 777d307..fb3fddc 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -22,6 +22,10 @@ #include <linux/types.h> +/* Select powerpc specific features in <linux/kvm.h> */ +#define __KVM_HAVE_SPAPR_TCE +#define __KVM_HAVE_PPC_SMT + struct kvm_regs { __u64 pc; __u64 cr; @@ -272,4 +276,57 @@ struct kvm_guest_debug_arch { #define KVM_INTERRUPT_UNSET -2U #define KVM_INTERRUPT_SET_LEVEL -3U +#define KVM_CPU_440 1 +#define KVM_CPU_E500V2 2 +#define KVM_CPU_3S_32 3 +#define KVM_CPU_3S_64 4 + +/* for KVM_CAP_SPAPR_TCE */ +struct kvm_create_spapr_tce { + __u64 liobn; + __u32 window_size; +}; + +/* for KVM_ALLOCATE_RMA */ +struct kvm_allocate_rma { + __u64 rma_size; +}; + +struct kvm_book3e_206_tlb_entry { + __u32 mas8; + __u32 mas1; + __u64 mas2; + __u64 mas7_3; +}; + +struct kvm_book3e_206_tlb_params { + /* + * For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV: + * + * - The number of ways of TLB0 must be a power of two between 2 and + * 16. + * - TLB1 must be fully associative. + * - The size of TLB0 must be a multiple of the number of ways, and + * the number of sets must be a power of two. + * - The size of TLB1 may not exceed 64 entries. + * - TLB0 supports 4 KiB pages. + * - The page sizes supported by TLB1 are as indicated by + * TLB1CFG (if MMUCFG[MAVN] = 0) or TLB1PS (if MMUCFG[MAVN] = 1) + * as returned by KVM_GET_SREGS. + * - TLB2 and TLB3 are reserved, and their entries in tlb_sizes[] + * and tlb_ways[] must be zero. + * + * tlb_ways[n] = tlb_sizes[n] means the array is fully associative. + * + * KVM will adjust TLBnCFG based on the sizes configured here, + * though arrays greater than 2048 entries will have TLBnCFG[NENTRY] + * set to zero. + */ + __u32 tlb_sizes[4]; + __u32 tlb_ways[4]; + __u32 reserved[8]; +}; + +#define KVM_ONE_REG_PPC_HIOR KVM_ONE_REG_PPC | 0x100 + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h index 834d71e..f2ac46a 100644 --- a/linux-headers/asm-x86/kvm_para.h +++ b/linux-headers/asm-x86/kvm_para.h @@ -21,6 +21,7 @@ */ #define KVM_FEATURE_CLOCKSOURCE2 3 #define KVM_FEATURE_ASYNC_PF 4 +#define KVM_FEATURE_STEAL_TIME 5 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. @@ -30,10 +31,23 @@ #define MSR_KVM_WALL_CLOCK 0x11 #define MSR_KVM_SYSTEM_TIME 0x12 +#define KVM_MSR_ENABLED 1 /* Custom MSRs falls in the range 0x4b564d00-0x4b564dff */ #define MSR_KVM_WALL_CLOCK_NEW 0x4b564d00 #define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 #define MSR_KVM_ASYNC_PF_EN 0x4b564d02 +#define MSR_KVM_STEAL_TIME 0x4b564d03 + +struct kvm_steal_time { + __u64 steal; + __u32 version; + __u32 flags; + __u32 pad[12]; +}; + +#define KVM_STEAL_ALIGNMENT_BITS 5 +#define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1))) +#define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1) #define KVM_MAX_MMU_OP_BATCH 32 diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index fc63b73..a8761d3 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -161,6 +161,7 @@ struct kvm_pit_config { #define KVM_EXIT_NMI 16 #define KVM_EXIT_INTERNAL_ERROR 17 #define KVM_EXIT_OSI 18 +#define KVM_EXIT_PAPR_HCALL 19 /* For KVM_EXIT_INTERNAL_ERROR */ #define KVM_INTERNAL_ERROR_EMULATION 1 @@ -264,6 +265,11 @@ struct kvm_run { struct { __u64 gprs[32]; } osi; + struct { + __u64 nr; + __u64 ret; + __u64 args[9]; + } papr_hcall; /* Fix the size of the union. */ char padding[256]; }; @@ -457,7 +463,7 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_VAPIC 6 #define KVM_CAP_EXT_CPUID 7 #define KVM_CAP_CLOCKSOURCE 8 -#define KVM_CAP_NR_VCPUS 9 /* returns max vcpus per vm */ +#define KVM_CAP_NR_VCPUS 9 /* returns recommended max vcpus per vm */ #define KVM_CAP_NR_MEMSLOTS 10 /* returns max memory slots per vm */ #define KVM_CAP_PIT 11 #define KVM_CAP_NOP_IO_DELAY 12 @@ -544,6 +550,14 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_TSC_CONTROL 60 #define KVM_CAP_GET_TSC_KHZ 61 #define KVM_CAP_PPC_BOOKE_SREGS 62 +#define KVM_CAP_SPAPR_TCE 63 +#define KVM_CAP_PPC_SMT 64 +#define KVM_CAP_PPC_RMA 65 +#define KVM_CAP_MAX_VCPUS 66 /* returns max vcpus per vm */ +#define KVM_CAP_PPC_HIOR 67 +#define KVM_CAP_PPC_PAPR 68 +#define KVM_CAP_SW_TLB 69 +#define KVM_CAP_ONE_REG 70 #ifdef KVM_CAP_IRQ_ROUTING @@ -623,6 +637,49 @@ struct kvm_clock_data { __u32 pad[9]; }; +#define KVM_MMU_FSL_BOOKE_NOHV 0 +#define KVM_MMU_FSL_BOOKE_HV 1 + +struct kvm_config_tlb { + __u64 params; + __u64 array; + __u32 mmu_type; + __u32 array_len; +}; + +struct kvm_dirty_tlb { + __u64 bitmap; + __u32 num_dirty; +}; + +/* Available with KVM_CAP_ONE_REG */ + +#define KVM_ONE_REG_GENERIC 0x0000000000000000ULL + +/* + * Architecture specific registers are to be defined in arch headers and + * ORed with the arch identifier. + */ +#define KVM_ONE_REG_PPC 0x1000000000000000ULL +#define KVM_ONE_REG_X86 0x2000000000000000ULL +#define KVM_ONE_REG_IA64 0x3000000000000000ULL +#define KVM_ONE_REG_ARM 0x4000000000000000ULL +#define KVM_ONE_REG_S390 0x5000000000000000ULL + +struct kvm_one_reg { + __u64 id; + union { + __u8 reg8; + __u16 reg16; + __u32 reg32; + __u64 reg64; + __u8 reg128[16]; + __u8 reg256[32]; + __u8 reg512[64]; + __u8 reg1024[128]; + } u; +}; + /* * ioctls for VM fds */ @@ -746,6 +803,14 @@ struct kvm_clock_data { /* Available with KVM_CAP_XCRS */ #define KVM_GET_XCRS _IOR(KVMIO, 0xa6, struct kvm_xcrs) #define KVM_SET_XCRS _IOW(KVMIO, 0xa7, struct kvm_xcrs) +#define KVM_CREATE_SPAPR_TCE _IOW(KVMIO, 0xa8, struct kvm_create_spapr_tce) +/* Available with KVM_CAP_RMA */ +#define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma) +/* Available with KVM_CAP_SW_TLB */ +#define KVM_DIRTY_TLB _IOW(KVMIO, 0xaa, struct kvm_dirty_tlb) +/* Available with KVM_CAP_ONE_REG */ +#define KVM_GET_ONE_REG _IOWR(KVMIO, 0xab, struct kvm_one_reg) +#define KVM_SET_ONE_REG _IOW(KVMIO, 0xac, struct kvm_one_reg) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) @@ -773,20 +838,14 @@ struct kvm_assigned_pci_dev { struct kvm_assigned_irq { __u32 assigned_dev_id; - __u32 host_irq; + __u32 host_irq; /* ignored (legacy field) */ __u32 guest_irq; __u32 flags; union { - struct { - __u32 addr_lo; - __u32 addr_hi; - __u32 data; - } guest_msi; __u32 reserved[12]; }; }; - struct kvm_assigned_msix_nr { __u32 assigned_dev_id; __u16 entry_nr; diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h index 7bdcf93..b315e27 100644 --- a/linux-headers/linux/kvm_para.h +++ b/linux-headers/linux/kvm_para.h @@ -26,3 +26,4 @@ #include <asm/kvm_para.h> #endif /* __LINUX_KVM_PARA_H */ + @@ -1271,3 +1271,124 @@ void set_system_io_map(MemoryRegion *mr) address_space_io.root = mr; memory_region_update_topology(); } + +typedef struct MemoryRegionList MemoryRegionList; + +struct MemoryRegionList { + const MemoryRegion *mr; + bool printed; + QTAILQ_ENTRY(MemoryRegionList) queue; +}; + +typedef QTAILQ_HEAD(queue, MemoryRegionList) MemoryRegionListHead; + +static void mtree_print_mr(fprintf_function mon_printf, void *f, + const MemoryRegion *mr, unsigned int level, + target_phys_addr_t base, + MemoryRegionListHead *alias_print_queue) +{ + MemoryRegionList *new_ml, *ml, *next_ml; + MemoryRegionListHead submr_print_queue; + const MemoryRegion *submr; + unsigned int i; + + if (!mr) { + return; + } + + for (i = 0; i < level; i++) { + mon_printf(f, " "); + } + + if (mr->alias) { + MemoryRegionList *ml; + bool found = false; + + /* check if the alias is already in the queue */ + QTAILQ_FOREACH(ml, alias_print_queue, queue) { + if (ml->mr == mr->alias && !ml->printed) { + found = true; + } + } + + if (!found) { + ml = g_new(MemoryRegionList, 1); + ml->mr = mr->alias; + ml->printed = false; + QTAILQ_INSERT_TAIL(alias_print_queue, ml, queue); + } + mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d): alias %s @%s " + TARGET_FMT_plx "-" TARGET_FMT_plx "\n", + base + mr->addr, + base + mr->addr + (target_phys_addr_t)mr->size - 1, + mr->priority, + mr->name, + mr->alias->name, + mr->alias_offset, + mr->alias_offset + (target_phys_addr_t)mr->size - 1); + } else { + mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d): %s\n", + base + mr->addr, + base + mr->addr + (target_phys_addr_t)mr->size - 1, + mr->priority, + mr->name); + } + + QTAILQ_INIT(&submr_print_queue); + + QTAILQ_FOREACH(submr, &mr->subregions, subregions_link) { + new_ml = g_new(MemoryRegionList, 1); + new_ml->mr = submr; + QTAILQ_FOREACH(ml, &submr_print_queue, queue) { + if (new_ml->mr->addr < ml->mr->addr || + (new_ml->mr->addr == ml->mr->addr && + new_ml->mr->priority > ml->mr->priority)) { + QTAILQ_INSERT_BEFORE(ml, new_ml, queue); + new_ml = NULL; + break; + } + } + if (new_ml) { + QTAILQ_INSERT_TAIL(&submr_print_queue, new_ml, queue); + } + } + + QTAILQ_FOREACH(ml, &submr_print_queue, queue) { + mtree_print_mr(mon_printf, f, ml->mr, level + 1, base + mr->addr, + alias_print_queue); + } + + QTAILQ_FOREACH_SAFE(next_ml, &submr_print_queue, queue, ml) { + g_free(ml); + } +} + +void mtree_info(fprintf_function mon_printf, void *f) +{ + MemoryRegionListHead ml_head; + MemoryRegionList *ml, *ml2; + + QTAILQ_INIT(&ml_head); + + mon_printf(f, "memory\n"); + mtree_print_mr(mon_printf, f, address_space_memory.root, 0, 0, &ml_head); + + /* print aliased regions */ + QTAILQ_FOREACH(ml, &ml_head, queue) { + if (!ml->printed) { + mon_printf(f, "%s\n", ml->mr->name); + mtree_print_mr(mon_printf, f, ml->mr, 0, 0, &ml_head); + } + } + + QTAILQ_FOREACH_SAFE(ml, &ml_head, queue, ml2) { + g_free(ml2); + } + + if (address_space_io.root && + !QTAILQ_EMPTY(&address_space_io.root->subregions)) { + QTAILQ_INIT(&ml_head); + mon_printf(f, "I/O\n"); + mtree_print_mr(mon_printf, f, address_space_io.root, 0, 0, &ml_head); + } +} @@ -501,6 +501,8 @@ void memory_region_transaction_begin(void); */ void memory_region_transaction_commit(void); +void mtree_info(fprintf_function mon_printf, void *f); + #endif #endif @@ -57,12 +57,13 @@ #include "json-parser.h" #include "osdep.h" #include "cpu.h" +#include "trace.h" #include "trace/control.h" #ifdef CONFIG_TRACE_SIMPLE #include "trace/simple.h" #endif -#include "trace/control.h" #include "ui/qemu-spice.h" +#include "memory.h" //#define DEBUG //#define DEBUG_COMPLETION @@ -369,6 +370,8 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) { QDict *qmp; + trace_monitor_protocol_emitter(mon); + qmp = qdict_new(); if (!monitor_has_error(mon)) { @@ -605,7 +608,7 @@ static void do_trace_event_set_state(Monitor *mon, const QDict *qdict) } } -#ifdef CONFIG_SIMPLE_TRACE +#ifdef CONFIG_TRACE_SIMPLE static void do_trace_file(Monitor *mon, const QDict *qdict) { const char *op = qdict_get_try_str(qdict, "op"); @@ -2461,7 +2464,7 @@ static void tlb_info(Monitor *mon) #endif -#if defined(TARGET_SPARC) +#if defined(TARGET_SPARC) || defined(TARGET_PPC) static void tlb_info(Monitor *mon) { CPUState *env1 = mon_get_cpu(); @@ -2470,6 +2473,11 @@ static void tlb_info(Monitor *mon) } #endif +static void do_info_mtree(Monitor *mon) +{ + mtree_info((fprintf_function)monitor_printf, mon); +} + static void do_info_kvm_print(Monitor *mon, const QObject *data) { QDict *qdict; @@ -2959,7 +2967,8 @@ static const mon_cmd_t info_cmds[] = { .user_print = do_pci_info_print, .mhandler.info_new = do_pci_info, }, -#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) +#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \ + defined(TARGET_PPC) { .name = "tlb", .args_type = "", @@ -2978,6 +2987,13 @@ static const mon_cmd_t info_cmds[] = { }, #endif { + .name = "mtree", + .args_type = "", + .params = "", + .help = "show memory tree", + .mhandler.info = do_info_mtree, + }, + { .name = "jit", .args_type = "", .params = "", @@ -5089,6 +5105,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) qobject_incref(mon->mc->id); cmd_name = qdict_get_str(input, "execute"); + trace_handle_qmp_command(mon, cmd_name); if (invalid_qmp_mode(mon, cmd_name)) { qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); goto err_out; diff --git a/pc-bios/README b/pc-bios/README index 02651fe..4d1d816 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -32,3 +32,6 @@ - The S390 zipl loader is an addition to the official IBM s390-tools package. That fork is maintained in its own git repository at: git://repo.or.cz/s390-tools.git + +- The sources for the Alpha palcode image is available from: + git://repo.or.cz/qemu-palcode.git diff --git a/pc-bios/mpc8544ds.dtb b/pc-bios/mpc8544ds.dtb Binary files differindex ae318b1..c6d3021 100644 --- a/pc-bios/mpc8544ds.dtb +++ b/pc-bios/mpc8544ds.dtb diff --git a/pc-bios/mpc8544ds.dts b/pc-bios/mpc8544ds.dts index a88b47c..7eb3160 100644 --- a/pc-bios/mpc8544ds.dts +++ b/pc-bios/mpc8544ds.dts @@ -25,18 +25,6 @@ cpus { #address-cells = <1>; #size-cells = <0>; - - PowerPC,8544@0 { - device_type = "cpu"; - reg = <0x0>; - d-cache-line-size = <32>; // 32 bytes - i-cache-line-size = <32>; // 32 bytes - d-cache-size = <0x8000>; // L1, 32K - i-cache-size = <0x8000>; // L1, 32K - timebase-frequency = <0>; - bus-frequency = <0>; - clock-frequency = <0>; - }; }; memory { diff --git a/pc-bios/palcode-clipper b/pc-bios/palcode-clipper Binary files differnew file mode 100755 index 0000000..a92372c --- /dev/null +++ b/pc-bios/palcode-clipper @@ -81,8 +81,8 @@ SECTIONS .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) } .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } .eh_frame_hdr : { *(.eh_frame_hdr) } -*(.gcc_except_table.*) } /* Adjust the address for the data segment. We want to -adjust up to + the same address within the page on the next page up. */ + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ . = ALIGN (0x10000) - ((0x10000 - .) & (0x10000 - 1)); . = DATA_SEGMENT_ALIGN (0x10000, 0x1000); /* Exception handling */ .eh_frame : { KEEP (*(.eh_frame)) } diff --git a/roms/qemu-palcode b/roms/qemu-palcode new file mode 160000 +Subproject 7abb12f60eb3069019e9497e193733d77d8f072 diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h index c2e7bb3..9d61d45 100644 --- a/target-alpha/cpu.h +++ b/target-alpha/cpu.h @@ -265,6 +265,10 @@ struct CPUAlphaState { uint64_t scratch[24]; #endif + /* This alarm doesn't exist in real hardware; we wish it did. */ + struct QEMUTimer *alarm_timer; + uint64_t alarm_expire; + #if TARGET_LONG_BITS > HOST_LONG_BITS /* temporary fixed-point registers * used to emulate 64 bits target on 32 bits hosts diff --git a/target-alpha/helper.h b/target-alpha/helper.h index 2dec57e..b693cee 100644 --- a/target-alpha/helper.h +++ b/target-alpha/helper.h @@ -113,6 +113,11 @@ DEF_HELPER_2(stq_c_phys, i64, i64, i64) DEF_HELPER_FLAGS_0(tbia, TCG_CALL_CONST, void) DEF_HELPER_FLAGS_1(tbis, TCG_CALL_CONST, void, i64) + +DEF_HELPER_1(halt, void, i64); + +DEF_HELPER_FLAGS_0(get_time, TCG_CALL_CONST, i64) +DEF_HELPER_FLAGS_1(set_alarm, TCG_CALL_CONST, void, i64) #endif #include "def-helper.h" diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c index d8945dc..cc102db 100644 --- a/target-alpha/op_helper.c +++ b/target-alpha/op_helper.c @@ -22,6 +22,7 @@ #include "host-utils.h" #include "softfloat.h" #include "helper.h" +#include "sysemu.h" #include "qemu-timer.h" #define FP_STATUS (env->fp_status) @@ -1218,6 +1219,30 @@ void helper_tbis(uint64_t p) { tlb_flush_page(env, p); } + +void helper_halt(uint64_t restart) +{ + if (restart) { + qemu_system_reset_request(); + } else { + qemu_system_shutdown_request(); + } +} + +uint64_t helper_get_time(void) +{ + return qemu_get_clock_ns(rtc_clock); +} + +void helper_set_alarm(uint64_t expire) +{ + if (expire) { + env->alarm_expire = expire; + qemu_mod_timer(env->alarm_timer, expire); + } else { + qemu_del_timer(env->alarm_timer); + } +} #endif /*****************************************************************************/ diff --git a/target-alpha/translate.c b/target-alpha/translate.c index 1e224a2..a961159 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -1590,18 +1590,34 @@ static int cpu_pr_data(int pr) return offsetof(CPUAlphaState, shadow[pr - 32]); case 40 ... 63: return offsetof(CPUAlphaState, scratch[pr - 40]); + + case 251: + return offsetof(CPUAlphaState, alarm_expire); } return 0; } -static void gen_mfpr(int ra, int regno) +static ExitStatus gen_mfpr(int ra, int regno) { int data = cpu_pr_data(regno); /* In our emulated PALcode, these processor registers have no side effects from reading. */ if (ra == 31) { - return; + return NO_EXIT; + } + + if (regno == 250) { + /* WALL_TIME */ + if (use_icount) { + gen_io_start(); + gen_helper_get_time(cpu_ir[ra]); + gen_io_end(); + return EXIT_PC_STALE; + } else { + gen_helper_get_time(cpu_ir[ra]); + return NO_EXIT; + } } /* The basic registers are data only, and unknown registers @@ -1615,11 +1631,13 @@ static void gen_mfpr(int ra, int regno) } else { tcg_gen_ld_i64(cpu_ir[ra], cpu_env, data); } + return NO_EXIT; } -static void gen_mtpr(int rb, int regno) +static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) { TCGv tmp; + int data; if (rb == 31) { tmp = tcg_const_i64(0); @@ -1627,19 +1645,37 @@ static void gen_mtpr(int rb, int regno) tmp = cpu_ir[rb]; } - /* These two register numbers perform a TLB cache flush. Thankfully we - can only do this inside PALmode, which means that the current basic - block cannot be affected by the change in mappings. */ - if (regno == 255) { + switch (regno) { + case 255: /* TBIA */ gen_helper_tbia(); - } else if (regno == 254) { + break; + + case 254: /* TBIS */ gen_helper_tbis(tmp); - } else { + break; + + case 253: + /* WAIT */ + tmp = tcg_const_i64(1); + tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUState, halted)); + return gen_excp(ctx, EXCP_HLT, 0); + + case 252: + /* HALT */ + gen_helper_halt(tmp); + return EXIT_PC_STALE; + + case 251: + /* ALARM */ + gen_helper_set_alarm(tmp); + break; + + default: /* The basic registers are data only, and unknown registers are read-zero, write-ignore. */ - int data = cpu_pr_data(regno); + data = cpu_pr_data(regno); if (data != 0) { if (data & PR_BYTE) { tcg_gen_st8_i64(tmp, cpu_env, data & ~PR_BYTE); @@ -1649,11 +1685,14 @@ static void gen_mtpr(int rb, int regno) tcg_gen_st_i64(tmp, cpu_env, data); } } + break; } if (rb == 31) { tcg_temp_free(tmp); } + + return NO_EXIT; } #endif /* !USER_ONLY*/ @@ -2721,8 +2760,16 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) break; case 0xC000: /* RPCC */ - if (ra != 31) - gen_helper_load_pcc(cpu_ir[ra]); + if (ra != 31) { + if (use_icount) { + gen_io_start(); + gen_helper_load_pcc(cpu_ir[ra]); + gen_io_end(); + ret = EXIT_PC_STALE; + } else { + gen_helper_load_pcc(cpu_ir[ra]); + } + } break; case 0xE000: /* RC */ @@ -2747,8 +2794,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) /* HW_MFPR (PALcode) */ #ifndef CONFIG_USER_ONLY if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { - gen_mfpr(ra, insn & 0xffff); - break; + return gen_mfpr(ra, insn & 0xffff); } #endif goto invalid_opc; @@ -3053,8 +3099,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) /* HW_MTPR (PALcode) */ #ifndef CONFIG_USER_ONLY if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { - gen_mtpr(rb, insn & 0xffff); - break; + return gen_mtpr(ctx, rb, insn & 0xffff); } #endif goto invalid_opc; diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index ab9c923..1892b35 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -84,6 +84,7 @@ void tlb_fill(CPUState *env1, target_ulong addr, int is_write, int mmu_idx, int ret; saved_env = env; + env = env1; ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx); if (unlikely(ret)) { if (retaddr) { diff --git a/target-lm32/helper.c b/target-lm32/helper.c index 014fd8d..fc0b444 100644 --- a/target-lm32/helper.c +++ b/target-lm32/helper.c @@ -218,6 +218,7 @@ CPUState *cpu_lm32_init(const char *cpu_model) cpu_exec_init(env); cpu_reset(env); + qemu_init_vcpu(env); if (!tcg_initialized) { tcg_initialized = 1; diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index b8d42e0..3f77e30 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -555,6 +555,8 @@ enum { /* Decrementer clock: RTC clock (POWER, 601) or bus clock */ POWERPC_FLAG_RTC_CLK = 0x00010000, POWERPC_FLAG_BUS_CLK = 0x00020000, + /* Has CFAR */ + POWERPC_FLAG_CFAR = 0x00040000, }; /*****************************************************************************/ @@ -667,8 +669,8 @@ enum { #define MAS0_ATSEL_TLB 0 #define MAS0_ATSEL_LRAT MAS0_ATSEL -#define MAS1_TSIZE_SHIFT 8 -#define MAS1_TSIZE_MASK (0xf << MAS1_TSIZE_SHIFT) +#define MAS1_TSIZE_SHIFT 7 +#define MAS1_TSIZE_MASK (0x1f << MAS1_TSIZE_SHIFT) #define MAS1_TS_SHIFT 12 #define MAS1_TS (1 << MAS1_TS_SHIFT) @@ -872,6 +874,10 @@ struct CPUPPCState { target_ulong ctr; /* condition register */ uint32_t crf[8]; +#if defined(TARGET_PPC64) + /* CFAR */ + target_ulong cfar; +#endif /* XER */ target_ulong xer; /* Reservation address */ @@ -934,6 +940,8 @@ struct CPUPPCState { ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ /* 403 dedicated access protection registers */ target_ulong pb[4]; + bool tlb_dirty; /* Set to non-zero when modifying TLB */ + bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */ #endif /* Other registers */ @@ -1010,8 +1018,35 @@ struct CPUPPCState { #if !defined(CONFIG_USER_ONLY) void *load_info; /* Holds boot loading state. */ #endif + + /* booke timers */ + + /* Specifies bit locations of the Time Base used to signal a fixed timer + * exception on a transition from 0 to 1. (watchdog or fixed-interval timer) + * + * 0 selects the least significant bit. + * 63 selects the most significant bit. + */ + uint8_t fit_period[4]; + uint8_t wdt_period[4]; }; +#define SET_FIT_PERIOD(a_, b_, c_, d_) \ +do { \ + env->fit_period[0] = (a_); \ + env->fit_period[1] = (b_); \ + env->fit_period[2] = (c_); \ + env->fit_period[3] = (d_); \ + } while (0) + +#define SET_WDT_PERIOD(a_, b_, c_, d_) \ +do { \ + env->wdt_period[0] = (a_); \ + env->wdt_period[1] = (b_); \ + env->wdt_period[2] = (c_); \ + env->wdt_period[3] = (d_); \ + } while (0) + #if !defined(CONFIG_USER_ONLY) /* Context used internally during MMU translations */ typedef struct mmu_ctx_t mmu_ctx_t; @@ -1202,6 +1237,7 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) #define SPR_601_UDECR (0x006) #define SPR_LR (0x008) #define SPR_CTR (0x009) +#define SPR_DSCR (0x011) #define SPR_DSISR (0x012) #define SPR_DAR (0x013) /* DAE for PowerPC 601 */ #define SPR_601_RTCU (0x014) @@ -1210,6 +1246,7 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) #define SPR_SDR1 (0x019) #define SPR_SRR0 (0x01A) #define SPR_SRR1 (0x01B) +#define SPR_CFAR (0x01C) #define SPR_AMR (0x01D) #define SPR_BOOKE_PID (0x030) #define SPR_BOOKE_DECAR (0x036) @@ -2043,4 +2080,6 @@ static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) env->nip = tb->pc; } +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env); + #endif /* !defined (__CPU_PPC_H__) */ diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 96ea464..6339be3 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -1293,7 +1293,7 @@ target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb) { uint32_t tlbncfg; int tlbn = booke206_tlbm_to_tlbn(env, tlb); - target_phys_addr_t tlbm_size; + int tlbm_size; tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn]; @@ -1301,9 +1301,10 @@ target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb) tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; } else { tlbm_size = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT; + tlbm_size <<= 1; } - return (1 << (tlbm_size << 1)) << 10; + return 1024ULL << tlbm_size; } /* TLB check function for MAS based SoftTLBs */ @@ -1465,6 +1466,94 @@ found_tlb: return ret; } +static const char *book3e_tsize_to_str[32] = { + "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", + "1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M", + "1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G", + "1T", "2T" +}; + +static void mmubooke206_dump_one_tlb(FILE *f, fprintf_function cpu_fprintf, + CPUState *env, int tlbn, int offset, + int tlbsize) +{ + ppcmas_tlb_t *entry; + int i; + + cpu_fprintf(f, "\nTLB%d:\n", tlbn); + cpu_fprintf(f, "Effective Physical Size TID TS SRWX URWX WIMGE U0123\n"); + + entry = &env->tlb.tlbm[offset]; + for (i = 0; i < tlbsize; i++, entry++) { + target_phys_addr_t ea, pa, size; + int tsize; + + if (!(entry->mas1 & MAS1_VALID)) { + continue; + } + + tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + size = 1024ULL << tsize; + ea = entry->mas2 & ~(size - 1); + pa = entry->mas7_3 & ~(size - 1); + + cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c U%c%c%c %c%c%c%c%c U%c%c%c%c\n", + (uint64_t)ea, (uint64_t)pa, + book3e_tsize_to_str[tsize], + (entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT, + (entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT, + entry->mas7_3 & MAS3_SR ? 'R' : '-', + entry->mas7_3 & MAS3_SW ? 'W' : '-', + entry->mas7_3 & MAS3_SX ? 'X' : '-', + entry->mas7_3 & MAS3_UR ? 'R' : '-', + entry->mas7_3 & MAS3_UW ? 'W' : '-', + entry->mas7_3 & MAS3_UX ? 'X' : '-', + entry->mas2 & MAS2_W ? 'W' : '-', + entry->mas2 & MAS2_I ? 'I' : '-', + entry->mas2 & MAS2_M ? 'M' : '-', + entry->mas2 & MAS2_G ? 'G' : '-', + entry->mas2 & MAS2_E ? 'E' : '-', + entry->mas7_3 & MAS3_U0 ? '0' : '-', + entry->mas7_3 & MAS3_U1 ? '1' : '-', + entry->mas7_3 & MAS3_U2 ? '2' : '-', + entry->mas7_3 & MAS3_U3 ? '3' : '-'); + } +} + +static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf, + CPUState *env) +{ + int offset = 0; + int i; + + if (kvm_enabled() && !env->kvm_sw_tlb) { + cpu_fprintf(f, "Cannot access KVM TLB\n"); + return; + } + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int size = booke206_tlb_size(env, i); + + if (size == 0) { + continue; + } + + mmubooke206_dump_one_tlb(f, cpu_fprintf, env, i, offset, size); + offset += size; + } +} + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) +{ + switch (env->mmu_model) { + case POWERPC_MMU_BOOKE206: + mmubooke206_dump_mmu(f, cpu_fprintf, env); + break; + default: + cpu_fprintf(f, "%s: unimplemented\n", __func__); + } +} + static inline int check_physical(CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, int rw) { diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 21f35af7..75832d8 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -14,6 +14,7 @@ * */ +#include <dirent.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/mman.h> @@ -28,6 +29,10 @@ #include "cpu.h" #include "device_tree.h" +#include "hw/sysbus.h" +#include "hw/spapr.h" +#include "hw/spapr_vio.h" + //#define DEBUG_KVM #ifdef DEBUG_KVM @@ -38,6 +43,8 @@ do { } while (0) #endif +#define PROC_DEVTREE_CPU "/proc/device-tree/cpus/" + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; @@ -105,6 +112,52 @@ static int kvm_arch_sync_sregs(CPUState *cenv) return kvm_vcpu_ioctl(cenv, KVM_SET_SREGS, &sregs); } +/* Set up a shared TLB array with KVM */ +static int kvm_booke206_tlb_init(CPUState *env) +{ + struct kvm_book3e_206_tlb_params params = {}; + struct kvm_config_tlb cfg = {}; + struct kvm_enable_cap encap = {}; + unsigned int entries = 0; + int ret, i; + + if (!kvm_enabled() || + !kvm_check_extension(env->kvm_state, KVM_CAP_SW_TLB)) { + return 0; + } + + assert(ARRAY_SIZE(params.tlb_sizes) == BOOKE206_MAX_TLBN); + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + params.tlb_sizes[i] = booke206_tlb_size(env, i); + params.tlb_ways[i] = booke206_tlb_ways(env, i); + entries += params.tlb_sizes[i]; + } + + assert(entries == env->nb_tlb); + assert(sizeof(struct kvm_book3e_206_tlb_entry) == sizeof(ppcmas_tlb_t)); + + env->tlb_dirty = true; + + cfg.array = (uintptr_t)env->tlb.tlbm; + cfg.array_len = sizeof(ppcmas_tlb_t) * entries; + cfg.params = (uintptr_t)¶ms; + cfg.mmu_type = KVM_MMU_FSL_BOOKE_NOHV; + + encap.cap = KVM_CAP_SW_TLB; + encap.args[0] = (uintptr_t)&cfg; + + ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &encap); + if (ret < 0) { + fprintf(stderr, "%s: couldn't enable KVM_CAP_SW_TLB: %s\n", + __func__, strerror(-ret)); + return ret; + } + + env->kvm_sw_tlb = true; + return 0; +} + int kvm_arch_init_vcpu(CPUState *cenv) { int ret; @@ -116,6 +169,15 @@ int kvm_arch_init_vcpu(CPUState *cenv) idle_timer = qemu_new_timer_ns(vm_clock, kvm_kick_env, cenv); + /* Some targets support access to KVM's guest TLB. */ + switch (cenv->mmu_model) { + case POWERPC_MMU_BOOKE206: + ret = kvm_booke206_tlb_init(cenv); + break; + default: + break; + } + return ret; } @@ -123,6 +185,31 @@ void kvm_arch_reset_vcpu(CPUState *env) { } +static void kvm_sw_tlb_put(CPUState *env) +{ + struct kvm_dirty_tlb dirty_tlb; + unsigned char *bitmap; + int ret; + + if (!env->kvm_sw_tlb) { + return; + } + + bitmap = g_malloc((env->nb_tlb + 7) / 8); + memset(bitmap, 0xFF, (env->nb_tlb + 7) / 8); + + dirty_tlb.bitmap = (uintptr_t)bitmap; + dirty_tlb.num_dirty = env->nb_tlb; + + ret = kvm_vcpu_ioctl(env, KVM_DIRTY_TLB, &dirty_tlb); + if (ret) { + fprintf(stderr, "%s: KVM_DIRTY_TLB: %s\n", + __func__, strerror(-ret)); + } + + g_free(bitmap); +} + int kvm_arch_put_registers(CPUState *env, int level) { struct kvm_regs regs; @@ -160,6 +247,11 @@ int kvm_arch_put_registers(CPUState *env, int level) if (ret < 0) return ret; + if (env->tlb_dirty) { + kvm_sw_tlb_put(env); + env->tlb_dirty = false; + } + return ret; } @@ -452,6 +544,14 @@ int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run) dprintf("handle halt\n"); ret = kvmppc_handle_halt(env); break; +#ifdef CONFIG_PSERIES + case KVM_EXIT_PAPR_HCALL: + dprintf("handle PAPR hypercall\n"); + run->papr_hcall.ret = spapr_hypercall(env, run->papr_hcall.nr, + run->papr_hcall.args); + ret = 1; + break; +#endif default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -509,6 +609,70 @@ uint32_t kvmppc_get_tbfreq(void) return retval; } +/* Try to find a device tree node for a CPU with clock-frequency property */ +static int kvmppc_find_cpu_dt(char *buf, int buf_len) +{ + struct dirent *dirp; + DIR *dp; + + if ((dp = opendir(PROC_DEVTREE_CPU)) == NULL) { + printf("Can't open directory " PROC_DEVTREE_CPU "\n"); + return -1; + } + + buf[0] = '\0'; + while ((dirp = readdir(dp)) != NULL) { + FILE *f; + snprintf(buf, buf_len, "%s%s/clock-frequency", PROC_DEVTREE_CPU, + dirp->d_name); + f = fopen(buf, "r"); + if (f) { + snprintf(buf, buf_len, "%s%s", PROC_DEVTREE_CPU, dirp->d_name); + fclose(f); + break; + } + buf[0] = '\0'; + } + closedir(dp); + if (buf[0] == '\0') { + printf("Unknown host!\n"); + return -1; + } + + return 0; +} + +uint64_t kvmppc_get_clockfreq(void) +{ + char buf[512]; + uint32_t tb[2]; + FILE *f; + int len; + + if (kvmppc_find_cpu_dt(buf, sizeof(buf))) { + return 0; + } + + strncat(buf, "/clock-frequency", sizeof(buf) - strlen(buf)); + + f = fopen(buf, "rb"); + if (!f) { + return -1; + } + + len = fread(tb, sizeof(tb[0]), 2, f); + fclose(f); + switch (len) { + case 1: + /* freq is only a single cell */ + return tb[0]; + case 2: + return *(uint64_t*)tb; + } + + return 0; +} + int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len) { uint32_t *hc = (uint32_t*)buf; @@ -539,6 +703,53 @@ int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len) return 0; } +void kvmppc_set_papr(CPUState *env) +{ + struct kvm_enable_cap cap = {}; + struct kvm_one_reg reg = {}; + struct kvm_sregs sregs = {}; + int ret; + + cap.cap = KVM_CAP_PPC_PAPR; + ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &cap); + + if (ret) { + goto fail; + } + + /* + * XXX We set HIOR here. It really should be a qdev property of + * the CPU node, but we don't have CPUs converted to qdev yet. + * + * Once we have qdev CPUs, move HIOR to a qdev property and + * remove this chunk. + */ + reg.id = KVM_ONE_REG_PPC_HIOR; + reg.u.reg64 = env->spr[SPR_HIOR]; + ret = kvm_vcpu_ioctl(env, KVM_SET_ONE_REG, ®); + if (ret) { + goto fail; + } + + /* Set SDR1 so kernel space finds the HTAB */ + ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + if (ret) { + goto fail; + } + + sregs.u.s.sdr1 = env->spr[SPR_SDR1]; + + ret = kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs); + if (ret) { + goto fail; + } + + return; + +fail: + cpu_abort(env, "This KVM version does not support PAPR\n"); +} + bool kvm_arch_stop_on_emulation_error(CPUState *env) { return true; diff --git a/target-ppc/kvm_ppc.c b/target-ppc/kvm_ppc.c index c031fcb..24fc6bc 100644 --- a/target-ppc/kvm_ppc.c +++ b/target-ppc/kvm_ppc.c @@ -21,71 +21,6 @@ static QEMUTimer *kvmppc_timer; static unsigned int kvmppc_timer_rate; -#ifdef CONFIG_FDT -int kvmppc_read_host_property(const char *node_path, const char *prop, - void *val, size_t len) -{ - char *path; - FILE *f; - int ret = 0; - int pathlen; - - pathlen = snprintf(NULL, 0, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop) - + 1; - path = g_malloc(pathlen); - - snprintf(path, pathlen, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop); - - f = fopen(path, "rb"); - if (f == NULL) { - ret = errno; - goto free; - } - - len = fread(val, len, 1, f); - if (len != 1) { - ret = ferror(f); - goto close; - } - -close: - fclose(f); -free: - free(path); - return ret; -} - -static int kvmppc_copy_host_cell(void *fdt, const char *node, const char *prop) -{ - uint32_t cell; - int ret; - - ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell)); - if (ret < 0) { - fprintf(stderr, "couldn't read host %s/%s\n", node, prop); - goto out; - } - - ret = qemu_devtree_setprop_cell(fdt, node, prop, cell); - if (ret < 0) { - fprintf(stderr, "couldn't set guest %s/%s\n", node, prop); - goto out; - } - -out: - return ret; -} - -void kvmppc_fdt_update(void *fdt) -{ - /* Copy data from the host device tree into the guest. Since the guest can - * directly access the timebase without host involvement, we must expose - * the correct frequencies. */ - kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "clock-frequency"); - kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "timebase-frequency"); -} -#endif - static void kvmppc_timer_hack(void *opaque) { qemu_notify_event(); diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 45a1373..c484e60 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -10,22 +10,42 @@ #define __KVM_PPC_H__ void kvmppc_init(void); -void kvmppc_fdt_update(void *fdt); -#ifndef CONFIG_KVM -static inline int kvmppc_read_host_property(const char *node_path, const char *prop, - void *val, size_t len) -{ - assert(0); - return -ENOSYS; -} -#else -int kvmppc_read_host_property(const char *node_path, const char *prop, - void *val, size_t len); -#endif + +#ifdef CONFIG_KVM uint32_t kvmppc_get_tbfreq(void); +uint64_t kvmppc_get_clockfreq(void); int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len); int kvmppc_set_interrupt(CPUState *env, int irq, int level); +void kvmppc_set_papr(CPUState *env); + +#else + +static inline uint32_t kvmppc_get_tbfreq(void) +{ + return 0; +} + +static inline uint64_t kvmppc_get_clockfreq(void) +{ + return 0; +} + +static inline int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len) +{ + return -1; +} + +static inline int kvmppc_set_interrupt(CPUState *env, int irq, int level) +{ + return -1; +} + +static inline void kvmppc_set_papr(CPUState *env) +{ +} + +#endif #ifndef CONFIG_KVM #define kvmppc_eieio() do { } while (0) diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 4277460..1e362fc 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -69,6 +69,9 @@ static TCGv cpu_nip; static TCGv cpu_msr; static TCGv cpu_ctr; static TCGv cpu_lr; +#if defined(TARGET_PPC64) +static TCGv cpu_cfar; +#endif static TCGv cpu_xer; static TCGv cpu_reserve; static TCGv_i32 cpu_fpscr; @@ -154,6 +157,11 @@ void ppc_translate_init(void) cpu_lr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, lr), "lr"); +#if defined(TARGET_PPC64) + cpu_cfar = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cfar), "cfar"); +#endif + cpu_xer = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, xer), "xer"); @@ -187,6 +195,7 @@ typedef struct DisasContext { int le_mode; #if defined(TARGET_PPC64) int sf_mode; + int has_cfar; #endif int fpu_enabled; int altivec_enabled; @@ -3345,6 +3354,14 @@ static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) /* stfiwx */ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX); +static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip) +{ +#if defined(TARGET_PPC64) + if (ctx->has_cfar) + tcg_gen_movi_tl(cpu_cfar, nip); +#endif +} + /*** Branch ***/ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { @@ -3407,6 +3424,7 @@ static void gen_b(DisasContext *ctx) target = li; if (LK(ctx->opcode)) gen_setlr(ctx, ctx->nip); + gen_update_cfar(ctx, ctx->nip); gen_goto_tb(ctx, 0, target); } @@ -3469,6 +3487,7 @@ static inline void gen_bcond(DisasContext *ctx, int type) } tcg_temp_free_i32(temp); } + gen_update_cfar(ctx, ctx->nip); if (type == BCOND_IM) { target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); if (likely(AA(ctx->opcode) == 0)) { @@ -3580,6 +3599,7 @@ static void gen_rfi(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } + gen_update_cfar(ctx, ctx->nip); gen_helper_rfi(); gen_sync_exception(ctx); #endif @@ -3596,6 +3616,7 @@ static void gen_rfid(DisasContext *ctx) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); return; } + gen_update_cfar(ctx, ctx->nip); gen_helper_rfid(); gen_sync_exception(ctx); #endif @@ -9263,6 +9284,12 @@ void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf, */ } +#if defined(TARGET_PPC64) + if (env->flags & POWERPC_FLAG_CFAR) { + cpu_fprintf(f, " CFAR " TARGET_FMT_lx"\n", env->cfar); + } +#endif + switch (env->mmu_model) { case POWERPC_MMU_32B: case POWERPC_MMU_601: @@ -9371,6 +9398,7 @@ static inline void gen_intermediate_code_internal(CPUState *env, ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0; #if defined(TARGET_PPC64) ctx.sf_mode = msr_sf; + ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); #endif ctx.fpu_enabled = msr_fp; if ((env->flags & POWERPC_FLAG_SPE) && msr_spe) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 9ea193d..ca0d852 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -129,6 +129,19 @@ static void spr_write_lr (void *opaque, int sprn, int gprn) tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]); } +/* CFAR */ +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +static void spr_read_cfar (void *opaque, int gprn, int sprn) +{ + tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar); +} + +static void spr_write_cfar (void *opaque, int sprn, int gprn) +{ + tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]); +} +#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ + /* CTR */ static void spr_read_ctr (void *opaque, int gprn, int sprn) { @@ -3253,6 +3266,9 @@ static void init_proc_401 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 401x2 */ @@ -3291,6 +3307,9 @@ static void init_proc_401x2 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 401x3 */ @@ -3324,6 +3343,9 @@ static void init_proc_401x3 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(16, 20, 24, 28); } /* IOP480 */ @@ -3362,6 +3384,9 @@ static void init_proc_IOP480 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 403 */ @@ -3392,6 +3417,9 @@ static void init_proc_403 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 403 GCX */ @@ -3442,6 +3470,9 @@ static void init_proc_403GCX (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 405 */ @@ -3491,6 +3522,9 @@ static void init_proc_405 (CPUPPCState *env) env->icache_line_size = 32; /* Allocate hardware IRQ controller */ ppc40x_irq_init(env); + + SET_FIT_PERIOD(8, 12, 16, 20); + SET_WDT_PERIOD(16, 20, 24, 28); } /* PowerPC 440 EP */ @@ -3573,6 +3607,9 @@ static void init_proc_440EP (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 440 GP */ @@ -3637,6 +3674,9 @@ static void init_proc_440GP (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 440x4 */ @@ -3701,6 +3741,9 @@ static void init_proc_440x4 (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 440x5 */ @@ -3782,6 +3825,9 @@ static void init_proc_440x5 (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; ppc40x_irq_init(env); + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 460 (guessed) */ @@ -3870,6 +3916,9 @@ static void init_proc_460 (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* PowerPC 460F (guessed) */ @@ -3961,6 +4010,9 @@ static void init_proc_460F (CPUPPCState *env) env->dcache_line_size = 32; env->icache_line_size = 32; /* XXX: TODO: allocate internal IRQ controller */ + + SET_FIT_PERIOD(12, 16, 20, 24); + SET_WDT_PERIOD(20, 24, 28, 32); } /* Freescale 5xx cores (aka RCPU) */ @@ -6489,7 +6541,7 @@ static void init_proc_970MP (CPUPPCState *env) #define POWERPC_BFDM_POWER7 (bfd_mach_ppc64) #define POWERPC_FLAG_POWER7 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ - POWERPC_FLAG_BUS_CLK) + POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR) #define check_pow_POWER7 check_pow_nocheck static void init_proc_POWER7 (CPUPPCState *env) @@ -6508,6 +6560,14 @@ static void init_proc_POWER7 (CPUPPCState *env) &spr_read_purr, SPR_NOACCESS, &spr_read_purr, SPR_NOACCESS, 0x00000000); + spr_register(env, SPR_CFAR, "SPR_CFAR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_cfar, &spr_write_cfar, + 0x00000000); + spr_register(env, SPR_DSCR, "SPR_DSCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); #endif /* !CONFIG_USER_ONLY */ /* Memory management */ /* XXX : not implemented */ @@ -9679,8 +9739,7 @@ static int gdb_get_float_reg(CPUState *env, uint8_t *mem_buf, int n) return 8; } if (n == 32) { - /* FPSCR not implemented */ - memset(mem_buf, 0, 4); + stl_p(mem_buf, env->fpscr); return 4; } return 0; diff --git a/tcg/ia64/tcg-target.c b/tcg/ia64/tcg-target.c index 3803ab6..bb22b9a 100644 --- a/tcg/ia64/tcg-target.c +++ b/tcg/ia64/tcg-target.c @@ -847,25 +847,6 @@ static inline void tcg_out_movi(TCGContext *s, TCGType type, tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg)); } -static inline void tcg_out_addi(TCGContext *s, TCGArg reg, tcg_target_long val) -{ - if (val == ((int32_t)val << 10) >> 10) { - tcg_out_bundle(s, MmI, - tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, - TCG_REG_R2, val, TCG_REG_R0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg, - reg, TCG_REG_R2)); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, val); - tcg_out_bundle(s, mmI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg, - reg, TCG_REG_R2)); - } -} - static void tcg_out_br(TCGContext *s, int label_index) { TCGLabel *l = &s->labels[label_index]; diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c index b58df71..9317fe8 100644 --- a/tcg/s390/tcg-target.c +++ b/tcg/s390/tcg-target.c @@ -2322,8 +2322,3 @@ static void tcg_target_qemu_prologue(TCGContext *s) /* br %r14 (return) */ tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14); } - -static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) -{ - tcg_abort(); -} diff --git a/trace-events b/trace-events index b7ddf14..63d8c8e 100644 --- a/trace-events +++ b/trace-events @@ -56,6 +56,7 @@ virtio_console_chr_read(unsigned int port, int size) "port %u, size %d" virtio_console_chr_event(unsigned int port, int event) "port %u, event %d" # block.c +bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags %#x format_name \"%s\"" multiwrite_cb(void *mcb, int ret) "mcb %p ret %d" bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d" bdrv_aio_multiwrite_earlyfail(void *mcb) "mcb %p" @@ -66,7 +67,7 @@ bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" -bdrv_co_io(int is_write, void *acb) "is_write %d acb %p" +bdrv_co_io_em(void *bs, int64_t sector_num, int nb_sectors, int is_write, void *acb) "bs %p sector_num %"PRId64" nb_sectors %d is_write %d acb %p" # hw/virtio-blk.c virtio_blk_req_complete(void *req, int status) "req %p status %d" @@ -444,8 +445,8 @@ milkymist_tmu2_pulse_irq(void) "Pulse IRQ" # hw/milkymist-uart.c milkymist_uart_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" milkymist_uart_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x" -milkymist_uart_pulse_irq_rx(void) "Pulse IRQ RX" -milkymist_uart_pulse_irq_tx(void) "Pulse IRQ TX" +milkymist_uart_raise_irq(void) "Raise IRQ" +milkymist_uart_lower_irq(void) "Lower IRQ" # hw/milkymist-vgafb.c milkymist_vgafb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x" @@ -533,3 +534,7 @@ esp_mem_writeb_cmd_sel(uint32_t val) "Select without ATN (%2.2x)" esp_mem_writeb_cmd_selatn(uint32_t val) "Select with ATN (%2.2x)" esp_mem_writeb_cmd_selatns(uint32_t val) "Select with ATN & stop (%2.2x)" esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (%2.2x)" + +# monitor.c +handle_qmp_command(void *mon, const char *cmd_name) "mon %p cmd_name \"%s\"" +monitor_protocol_emitter(void *mon) "mon %p" |