diff options
77 files changed, 5152 insertions, 3031 deletions
diff --git a/.gitlab-ci.d/containers.yml b/.gitlab-ci.d/containers.yml index 4ef76d1f54..765408ae27 100644 --- a/.gitlab-ci.d/containers.yml +++ b/.gitlab-ci.d/containers.yml @@ -144,19 +144,12 @@ mipsel-debian-cross-container: variables: NAME: debian-mipsel-cross -powerpc-debian-cross-container: +powerpc-test-cross-container: extends: .container_job_template stage: containers-layer2 - needs: ['amd64-debian10-container'] - variables: - NAME: debian-powerpc-cross - -ppc64-debian-cross-container: - extends: .container_job_template - stage: containers-layer2 - needs: ['amd64-debian10-container'] + needs: ['amd64-debian11-container'] variables: - NAME: debian-ppc64-cross + NAME: debian-powerpc-test-cross ppc64el-debian-cross-container: extends: .container_job_template diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 24f300aace..f718b61fa7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -416,6 +416,17 @@ build-user-static: CONFIGURE_ARGS: --disable-tools --disable-system --static MAKE_CHECK_ARGS: check-tcg +# Because the hexagon cross-compiler takes so long to build we don't rely +# on the CI system to build it and hence this job has no dependency +# declared. The image is manually uploaded. +build-user-hexagon: + extends: .native_build_job_template + variables: + IMAGE: debian-hexagon-cross + TARGETS: hexagon-linux-user + CONFIGURE_ARGS: --disable-tools --disable-docs --enable-debug-tcg + MAKE_CHECK_ARGS: check-tcg + # Only build the softmmu targets we have check-tcg tests for build-some-softmmu: extends: .native_build_job_template diff --git a/MAINTAINERS b/MAINTAINERS index eab178aeee..75e0f2d750 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -197,6 +197,8 @@ F: linux-user/hexagon/ F: tests/tcg/hexagon/ F: disas/hexagon.c F: default-configs/targets/hexagon-linux-user.mak +F: docker/dockerfiles/debian-hexagon-cross.docker +F: docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh HPPA (PA-RISC) TCG CPUs M: Richard Henderson <richard.henderson@linaro.org> @@ -347,6 +349,7 @@ S: Maintained F: target/tricore/ F: hw/tricore/ F: include/hw/tricore/ +F: tests/tcg/tricore/ Multiarch Linux User Tests M: Alex Bennée <alex.bennee@linaro.org> @@ -3245,6 +3248,8 @@ F: block/export/vhost-user-blk-server.c F: block/export/vhost-user-blk-server.h F: include/qemu/vhost-user-server.h F: tests/qtest/libqos/vhost-user-blk.c +F: tests/qtest/libqos/vhost-user-blk.h +F: tests/qtest/vhost-user-blk-test.c F: util/vhost-user-server.c FUSE block device exports diff --git a/block.c b/block.c index 75a82af641..0dc97281dc 100644 --- a/block.c +++ b/block.c @@ -2916,13 +2916,14 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, child_role, perm, shared_perm, opaque, &child, tran, errp); if (ret < 0) { - bdrv_unref(child_bs); - return NULL; + assert(child == NULL); + goto out; } ret = bdrv_refresh_perms(child_bs, errp); - tran_finalize(tran, ret); +out: + tran_finalize(tran, ret); bdrv_unref(child_bs); return child; } @@ -4049,7 +4050,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) ret = bdrv_flush(bs_entry->state.bs); if (ret < 0) { error_setg_errno(errp, -ret, "Error flushing drive"); - goto cleanup; + goto abort; } } diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c index fa06996d37..1862563336 100644 --- a/block/export/vhost-user-blk-server.c +++ b/block/export/vhost-user-blk-server.c @@ -70,9 +70,16 @@ static void vu_blk_req_complete(VuBlkReq *req) static bool vu_blk_sect_range_ok(VuBlkExport *vexp, uint64_t sector, size_t size) { - uint64_t nb_sectors = size >> BDRV_SECTOR_BITS; + uint64_t nb_sectors; uint64_t total_sectors; + if (size % VIRTIO_BLK_SECTOR_SIZE) { + return false; + } + + nb_sectors = size >> VIRTIO_BLK_SECTOR_BITS; + + QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != VIRTIO_BLK_SECTOR_SIZE); if (nb_sectors > BDRV_REQUEST_MAX_SECTORS) { return false; } diff --git a/block/qcow2.c b/block/qcow2.c index 9727ae8fe3..39b91ef940 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5089,6 +5089,7 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) BDRVQcow2State *s = bs->opaque; bdi->cluster_size = s->cluster_size; bdi->vm_state_offset = qcow2_vm_state_offset(s); + bdi->is_dirty = s->incompatible_features & QCOW2_INCOMPAT_DIRTY; return 0; } diff --git a/configure b/configure index 0e4233fd8a..9470fff09a 100755 --- a/configure +++ b/configure @@ -1651,7 +1651,7 @@ case "$cpu" in # No special flags required for other host CPUs esac -eval "cross_cc_${cpu}=\$host_cc" +eval "cross_cc_${cpu}=\$cc" cross_cc_vars="$cross_cc_vars cross_cc_${cpu}" QEMU_CFLAGS="$CPU_CFLAGS $QEMU_CFLAGS" @@ -6164,6 +6164,7 @@ echo "GENISOIMAGE=$genisoimage" >> $config_host_mak echo "MESON=$meson" >> $config_host_mak echo "NINJA=$ninja" >> $config_host_mak echo "CC=$cc" >> $config_host_mak +echo "HOST_CC=$host_cc" >> $config_host_mak if $iasl -h > /dev/null 2>&1; then echo "CONFIG_IASL=$iasl" >> $config_host_mak fi diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index f5e9682703..c6210fad0c 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -47,9 +47,13 @@ static const int user_feature_bits[] = { VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_RING_PACKED, + VIRTIO_F_IOMMU_PLATFORM, VHOST_INVALID_FEATURE_BIT }; +static void vhost_user_blk_event(void *opaque, QEMUChrEvent event); + static void vhost_user_blk_update_config(VirtIODevice *vdev, uint8_t *config) { VHostUserBlk *s = VHOST_USER_BLK(vdev); @@ -309,7 +313,7 @@ static void vhost_user_blk_reset(VirtIODevice *vdev) vhost_dev_free_inflight(s->inflight); } -static int vhost_user_blk_connect(DeviceState *dev) +static int vhost_user_blk_connect(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(vdev); @@ -320,6 +324,7 @@ static int vhost_user_blk_connect(DeviceState *dev) } s->connected = true; + s->dev.num_queues = s->num_queues; s->dev.nvqs = s->num_queues; s->dev.vqs = s->vhost_vqs; s->dev.vq_index = 0; @@ -329,8 +334,7 @@ static int vhost_user_blk_connect(DeviceState *dev) ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); if (ret < 0) { - error_report("vhost-user-blk: vhost initialization failed: %s", - strerror(-ret)); + error_setg_errno(errp, -ret, "vhost initialization failed"); return ret; } @@ -338,8 +342,7 @@ static int vhost_user_blk_connect(DeviceState *dev) if (virtio_device_started(vdev, vdev->status)) { ret = vhost_user_blk_start(vdev); if (ret < 0) { - error_report("vhost-user-blk: vhost start failed: %s", - strerror(-ret)); + error_setg_errno(errp, -ret, "vhost start failed"); return ret; } } @@ -362,19 +365,6 @@ static void vhost_user_blk_disconnect(DeviceState *dev) vhost_dev_cleanup(&s->dev); } -static void vhost_user_blk_event(void *opaque, QEMUChrEvent event, - bool realized); - -static void vhost_user_blk_event_realize(void *opaque, QEMUChrEvent event) -{ - vhost_user_blk_event(opaque, event, false); -} - -static void vhost_user_blk_event_oper(void *opaque, QEMUChrEvent event) -{ - vhost_user_blk_event(opaque, event, true); -} - static void vhost_user_blk_chr_closed_bh(void *opaque) { DeviceState *dev = opaque; @@ -382,36 +372,27 @@ static void vhost_user_blk_chr_closed_bh(void *opaque) VHostUserBlk *s = VHOST_USER_BLK(vdev); vhost_user_blk_disconnect(dev); - qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, - vhost_user_blk_event_oper, NULL, opaque, NULL, true); + qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event, + NULL, opaque, NULL, true); } -static void vhost_user_blk_event(void *opaque, QEMUChrEvent event, - bool realized) +static void vhost_user_blk_event(void *opaque, QEMUChrEvent event) { DeviceState *dev = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(vdev); + Error *local_err = NULL; switch (event) { case CHR_EVENT_OPENED: - if (vhost_user_blk_connect(dev) < 0) { + if (vhost_user_blk_connect(dev, &local_err) < 0) { + error_report_err(local_err); qemu_chr_fe_disconnect(&s->chardev); return; } break; case CHR_EVENT_CLOSED: - /* - * Closing the connection should happen differently on device - * initialization and operation stages. - * On initalization, we want to re-start vhost_dev initialization - * from the very beginning right away when the connection is closed, - * so we clean up vhost_dev on each connection closing. - * On operation, we want to postpone vhost_dev cleanup to let the - * other code perform its own cleanup sequence using vhost_dev data - * (e.g. vhost_dev_set_log). - */ - if (realized && !runstate_check(RUN_STATE_SHUTDOWN)) { + if (!runstate_check(RUN_STATE_SHUTDOWN)) { /* * A close event may happen during a read/write, but vhost * code assumes the vhost_dev remains setup, so delay the @@ -431,8 +412,6 @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent event, * knowing its type (in this case vhost-user). */ s->dev.started = false; - } else { - vhost_user_blk_disconnect(dev); } break; case CHR_EVENT_BREAK: @@ -447,11 +426,10 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(vdev); - Error *err = NULL; int i, ret; if (!s->chardev.chr) { - error_setg(errp, "vhost-user-blk: chardev is mandatory"); + error_setg(errp, "chardev is mandatory"); return; } @@ -459,16 +437,16 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) s->num_queues = 1; } if (!s->num_queues || s->num_queues > VIRTIO_QUEUE_MAX) { - error_setg(errp, "vhost-user-blk: invalid number of IO queues"); + error_setg(errp, "invalid number of IO queues"); return; } if (!s->queue_size) { - error_setg(errp, "vhost-user-blk: queue size must be non-zero"); + error_setg(errp, "queue size must be non-zero"); return; } if (s->queue_size > VIRTQUEUE_MAX_SIZE) { - error_setg(errp, "vhost-user-blk: queue size must not exceed %d", + error_setg(errp, "queue size must not exceed %d", VIRTQUEUE_MAX_SIZE); return; } @@ -490,34 +468,31 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) s->vhost_vqs = g_new0(struct vhost_virtqueue, s->num_queues); s->connected = false; - qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, - vhost_user_blk_event_realize, NULL, (void *)dev, - NULL, true); - -reconnect: - if (qemu_chr_fe_wait_connected(&s->chardev, &err) < 0) { - error_report_err(err); + if (qemu_chr_fe_wait_connected(&s->chardev, errp) < 0) { goto virtio_err; } - /* check whether vhost_user_blk_connect() failed or not */ - if (!s->connected) { - goto reconnect; + if (vhost_user_blk_connect(dev, errp) < 0) { + qemu_chr_fe_disconnect(&s->chardev); + goto virtio_err; } + assert(s->connected); ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg, sizeof(struct virtio_blk_config)); if (ret < 0) { - error_report("vhost-user-blk: get block config failed"); - goto reconnect; + error_setg(errp, "vhost-user-blk: get block config failed"); + goto vhost_err; } - /* we're fully initialized, now we can operate, so change the handler */ + /* we're fully initialized, now we can operate, so add the handler */ qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, - vhost_user_blk_event_oper, NULL, (void *)dev, + vhost_user_blk_event, NULL, (void *)dev, NULL, true); return; +vhost_err: + vhost_dev_cleanup(&s->dev); virtio_err: g_free(s->vhost_vqs); s->vhost_vqs = NULL; diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 86d6f379d1..597d974dd4 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -29,6 +29,9 @@ ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files( 'spapr_numa.c', 'pef.c', )) +ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_TCG'], if_true: files( + 'spapr_softmmu.c', +)) ppc_ss.add(when: 'CONFIG_SPAPR_RNG', if_true: files('spapr_rng.c')) ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_LINUX'], if_true: files( 'spapr_pci_vfio.c', diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index ffe01977cd..d16dd2d080 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -196,7 +196,7 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); - if (env->spr_cb[SPR_PURR].oea_read) { + if (ppc_has_spr(cpu, SPR_PURR)) { _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 8f40319aee..c23bcc4490 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -703,10 +703,10 @@ static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); - if (env->spr_cb[SPR_PURR].oea_read) { + if (ppc_has_spr(cpu, SPR_PURR)) { _FDT((fdt_setprop_cell(fdt, offset, "ibm,purr", 1))); } - if (env->spr_cb[SPR_SPURR].oea_read) { + if (ppc_has_spr(cpu, SPR_PURR)) { _FDT((fdt_setprop_cell(fdt, offset, "ibm,spurr", 1))); } @@ -979,6 +979,7 @@ static void spapr_dt_ov5_platform_support(SpaprMachineState *spapr, void *fdt, */ val[1] = SPAPR_OV5_XIVE_LEGACY; /* XICS */ val[3] = 0x00; /* Hash */ + spapr_check_mmu_mode(false); } else if (kvm_enabled()) { if (kvmppc_has_cap_mmu_radix() && kvmppc_has_cap_mmu_hash_v3()) { val[3] = 0x80; /* OV5_MMU_BOTH */ @@ -1556,6 +1557,22 @@ void spapr_setup_hpt(SpaprMachineState *spapr) } } +void spapr_check_mmu_mode(bool guest_radix) +{ + if (guest_radix) { + if (kvm_enabled() && !kvmppc_has_cap_mmu_radix()) { + error_report("Guest requested unavailable MMU mode (radix)."); + exit(EXIT_FAILURE); + } + } else { + if (kvm_enabled() && kvmppc_has_cap_mmu_radix() + && !kvmppc_has_cap_mmu_hash_v3()) { + error_report("Guest requested unavailable MMU mode (hash)."); + exit(EXIT_FAILURE); + } + } +} + static void spapr_machine_reset(MachineState *machine) { SpaprMachineState *spapr = SPAPR_MACHINE(machine); diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 9ea7ddd1e9..d0c419b392 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -371,6 +371,65 @@ static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift, return true; } +static void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, + bool (*cb)(void *, uint32_t, uint32_t), + void *opaque) +{ + PPCHash64Options *opts = cpu->hash64_opts; + int i; + int n = 0; + bool ci_largepage = false; + + assert(opts); + + n = 0; + for (i = 0; i < ARRAY_SIZE(opts->sps); i++) { + PPCHash64SegmentPageSizes *sps = &opts->sps[i]; + int j; + int m = 0; + + assert(n <= i); + + if (!sps->page_shift) { + break; + } + + for (j = 0; j < ARRAY_SIZE(sps->enc); j++) { + PPCHash64PageSize *ps = &sps->enc[j]; + + assert(m <= j); + if (!ps->page_shift) { + break; + } + + if (cb(opaque, sps->page_shift, ps->page_shift)) { + if (ps->page_shift >= 16) { + ci_largepage = true; + } + sps->enc[m++] = *ps; + } + } + + /* Clear rest of the row */ + for (j = m; j < ARRAY_SIZE(sps->enc); j++) { + memset(&sps->enc[j], 0, sizeof(sps->enc[j])); + } + + if (m) { + n++; + } + } + + /* Clear the rest of the table */ + for (i = n; i < ARRAY_SIZE(opts->sps); i++) { + memset(&opts->sps[i], 0, sizeof(opts->sps[i])); + } + + if (!ci_largepage) { + opts->flags &= ~PPC_HASH64_CI_LARGEPAGE; + } +} + static void cap_hpt_maxpagesize_cpu_apply(SpaprMachineState *spapr, PowerPCCPU *cpu, uint8_t val, Error **errp) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 16c719c3de..f25014afda 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -20,24 +20,7 @@ #include "mmu-book3s-v3.h" #include "hw/mem/memory-device.h" -static bool has_spr(PowerPCCPU *cpu, int spr) -{ - /* We can test whether the SPR is defined by checking for a valid name */ - return cpu->env.spr_cb[spr].name != NULL; -} - -static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) -{ - /* - * hash value/pteg group index is normalized by HPT mask - */ - if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) { - return false; - } - return true; -} - -static bool is_ram_address(SpaprMachineState *spapr, hwaddr addr) +bool is_ram_address(SpaprMachineState *spapr, hwaddr addr) { MachineState *machine = MACHINE(spapr); DeviceMemoryState *dms = machine->device_memory; @@ -53,355 +36,6 @@ static bool is_ram_address(SpaprMachineState *spapr, hwaddr addr) return false; } -static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong flags = args[0]; - target_ulong ptex = args[1]; - target_ulong pteh = args[2]; - target_ulong ptel = args[3]; - unsigned apshift; - target_ulong raddr; - target_ulong slot; - const ppc_hash_pte64_t *hptes; - - apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel); - if (!apshift) { - /* Bad page size encoding */ - return H_PARAMETER; - } - - raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << apshift) - 1); - - if (is_ram_address(spapr, raddr)) { - /* Regular RAM - should have WIMG=0010 */ - if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { - return H_PARAMETER; - } - } else { - target_ulong wimg_flags; - /* 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? */ - wimg_flags = (ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)); - - if (wimg_flags != HPTE64_R_I && - wimg_flags != (HPTE64_R_I | HPTE64_R_M)) { - return H_PARAMETER; - } - } - - pteh &= ~0x60ULL; - - if (!valid_ptex(cpu, ptex)) { - return H_PARAMETER; - } - - slot = ptex & 7ULL; - ptex = ptex & ~7ULL; - - if (likely((flags & H_EXACT) == 0)) { - hptes = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); - for (slot = 0; slot < 8; slot++) { - if (!(ppc_hash64_hpte0(cpu, hptes, slot) & HPTE64_V_VALID)) { - break; - } - } - ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); - if (slot == 8) { - return H_PTEG_FULL; - } - } else { - hptes = ppc_hash64_map_hptes(cpu, ptex + slot, 1); - if (ppc_hash64_hpte0(cpu, hptes, 0) & HPTE64_V_VALID) { - ppc_hash64_unmap_hptes(cpu, hptes, ptex + slot, 1); - return H_PTEG_FULL; - } - ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); - } - - spapr_store_hpte(cpu, ptex + slot, pteh | HPTE64_V_HPTE_DIRTY, ptel); - - args[0] = ptex + slot; - return H_SUCCESS; -} - -typedef enum { - REMOVE_SUCCESS = 0, - REMOVE_NOT_FOUND = 1, - REMOVE_PARM = 2, - REMOVE_HW = 3, -} RemoveResult; - -static RemoveResult remove_hpte(PowerPCCPU *cpu - , target_ulong ptex, - target_ulong avpn, - target_ulong flags, - target_ulong *vp, target_ulong *rp) -{ - const ppc_hash_pte64_t *hptes; - target_ulong v, r; - - if (!valid_ptex(cpu, ptex)) { - return REMOVE_PARM; - } - - hptes = ppc_hash64_map_hptes(cpu, ptex, 1); - v = ppc_hash64_hpte0(cpu, hptes, 0); - r = ppc_hash64_hpte1(cpu, hptes, 0); - ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); - - if ((v & HPTE64_V_VALID) == 0 || - ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || - ((flags & H_ANDCOND) && (v & avpn) != 0)) { - return REMOVE_NOT_FOUND; - } - *vp = v; - *rp = r; - spapr_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0); - ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); - return REMOVE_SUCCESS; -} - -static target_ulong h_remove(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUPPCState *env = &cpu->env; - target_ulong flags = args[0]; - target_ulong ptex = args[1]; - target_ulong avpn = args[2]; - RemoveResult ret; - - ret = remove_hpte(cpu, ptex, avpn, flags, - &args[0], &args[1]); - - switch (ret) { - case REMOVE_SUCCESS: - check_tlb_flush(env, true); - return H_SUCCESS; - - case REMOVE_NOT_FOUND: - return H_NOT_FOUND; - - case REMOVE_PARM: - return H_PARAMETER; - - case REMOVE_HW: - return H_HARDWARE; - } - - g_assert_not_reached(); -} - -#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(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUPPCState *env = &cpu->env; - int i; - target_ulong rc = H_SUCCESS; - - 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(cpu, *tsh & H_BULK_REMOVE_PTEX, tsl, - (*tsh & H_BULK_REMOVE_FLAGS) >> 26, - &v, &r); - - *tsh |= ret << 60; - - switch (ret) { - case REMOVE_SUCCESS: - *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43; - break; - - case REMOVE_PARM: - rc = H_PARAMETER; - goto exit; - - case REMOVE_HW: - rc = H_HARDWARE; - goto exit; - } - } - exit: - check_tlb_flush(env, true); - - return rc; -} - -static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUPPCState *env = &cpu->env; - target_ulong flags = args[0]; - target_ulong ptex = args[1]; - target_ulong avpn = args[2]; - const ppc_hash_pte64_t *hptes; - target_ulong v, r; - - if (!valid_ptex(cpu, ptex)) { - return H_PARAMETER; - } - - hptes = ppc_hash64_map_hptes(cpu, ptex, 1); - v = ppc_hash64_hpte0(cpu, hptes, 0); - r = ppc_hash64_hpte1(cpu, hptes, 0); - ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); - - if ((v & HPTE64_V_VALID) == 0 || - ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { - return H_NOT_FOUND; - } - - r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N | - HPTE64_R_KEY_HI | HPTE64_R_KEY_LO); - r |= (flags << 55) & HPTE64_R_PP0; - r |= (flags << 48) & HPTE64_R_KEY_HI; - r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); - spapr_store_hpte(cpu, ptex, - (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); - ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); - /* Flush the tlb */ - check_tlb_flush(env, true); - /* Don't need a memory barrier, due to qemu's global lock */ - spapr_store_hpte(cpu, ptex, v | HPTE64_V_HPTE_DIRTY, r); - return H_SUCCESS; -} - -static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong flags = args[0]; - target_ulong ptex = args[1]; - int i, ridx, n_entries = 1; - const ppc_hash_pte64_t *hptes; - - if (!valid_ptex(cpu, ptex)) { - return H_PARAMETER; - } - - if (flags & H_READ_4) { - /* Clear the two low order bits */ - ptex &= ~(3ULL); - n_entries = 4; - } - - hptes = ppc_hash64_map_hptes(cpu, ptex, n_entries); - for (i = 0, ridx = 0; i < n_entries; i++) { - args[ridx++] = ppc_hash64_hpte0(cpu, hptes, i); - args[ridx++] = ppc_hash64_hpte1(cpu, hptes, i); - } - ppc_hash64_unmap_hptes(cpu, hptes, ptex, n_entries); - - return H_SUCCESS; -} - -struct SpaprPendingHpt { - /* These fields are read-only after initialization */ - int shift; - QemuThread thread; - - /* These fields are protected by the BQL */ - bool complete; - - /* These fields are private to the preparation thread if - * !complete, otherwise protected by the BQL */ - int ret; - void *hpt; -}; - -static void free_pending_hpt(SpaprPendingHpt *pending) -{ - if (pending->hpt) { - qemu_vfree(pending->hpt); - } - - g_free(pending); -} - -static void *hpt_prepare_thread(void *opaque) -{ - SpaprPendingHpt *pending = opaque; - size_t size = 1ULL << pending->shift; - - pending->hpt = qemu_try_memalign(size, size); - if (pending->hpt) { - memset(pending->hpt, 0, size); - pending->ret = H_SUCCESS; - } else { - pending->ret = H_NO_MEM; - } - - qemu_mutex_lock_iothread(); - - if (SPAPR_MACHINE(qdev_get_machine())->pending_hpt == pending) { - /* Ready to go */ - pending->complete = true; - } else { - /* We've been cancelled, clean ourselves up */ - free_pending_hpt(pending); - } - - qemu_mutex_unlock_iothread(); - return NULL; -} - -/* Must be called with BQL held */ -static void cancel_hpt_prepare(SpaprMachineState *spapr) -{ - SpaprPendingHpt *pending = spapr->pending_hpt; - - /* Let the thread know it's cancelled */ - spapr->pending_hpt = NULL; - - if (!pending) { - /* Nothing to do */ - return; - } - - if (!pending->complete) { - /* thread will clean itself up */ - return; - } - - free_pending_hpt(pending); -} - /* Convert a return code from the KVM ioctl()s implementing resize HPT * into a PAPR hypercall return code */ static target_ulong resize_hpt_convert_rc(int ret) @@ -447,7 +81,6 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, { target_ulong flags = args[0]; int shift = args[1]; - SpaprPendingHpt *pending = spapr->pending_hpt; uint64_t current_ram_size; int rc; @@ -484,182 +117,11 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, return resize_hpt_convert_rc(rc); } - if (pending) { - /* something already in progress */ - if (pending->shift == shift) { - /* and it's suitable */ - if (pending->complete) { - return pending->ret; - } else { - return H_LONG_BUSY_ORDER_100_MSEC; - } - } - - /* not suitable, cancel and replace */ - cancel_hpt_prepare(spapr); - } - - if (!shift) { - /* nothing to do */ - return H_SUCCESS; - } - - /* start new prepare */ - - pending = g_new0(SpaprPendingHpt, 1); - pending->shift = shift; - pending->ret = H_HARDWARE; - - qemu_thread_create(&pending->thread, "sPAPR HPT prepare", - hpt_prepare_thread, pending, QEMU_THREAD_DETACHED); - - spapr->pending_hpt = pending; - - /* In theory we could estimate the time more accurately based on - * the new size, but there's not much point */ - return H_LONG_BUSY_ORDER_100_MSEC; -} - -static uint64_t new_hpte_load0(void *htab, uint64_t pteg, int slot) -{ - uint8_t *addr = htab; - - addr += pteg * HASH_PTEG_SIZE_64; - addr += slot * HASH_PTE_SIZE_64; - return ldq_p(addr); -} - -static void new_hpte_store(void *htab, uint64_t pteg, int slot, - uint64_t pte0, uint64_t pte1) -{ - uint8_t *addr = htab; - - addr += pteg * HASH_PTEG_SIZE_64; - addr += slot * HASH_PTE_SIZE_64; - - stq_p(addr, pte0); - stq_p(addr + HASH_PTE_SIZE_64 / 2, pte1); -} - -static int rehash_hpte(PowerPCCPU *cpu, - const ppc_hash_pte64_t *hptes, - void *old_hpt, uint64_t oldsize, - void *new_hpt, uint64_t newsize, - uint64_t pteg, int slot) -{ - uint64_t old_hash_mask = (oldsize >> 7) - 1; - uint64_t new_hash_mask = (newsize >> 7) - 1; - target_ulong pte0 = ppc_hash64_hpte0(cpu, hptes, slot); - target_ulong pte1; - uint64_t avpn; - unsigned base_pg_shift; - uint64_t hash, new_pteg, replace_pte0; - - if (!(pte0 & HPTE64_V_VALID) || !(pte0 & HPTE64_V_BOLTED)) { - return H_SUCCESS; - } - - pte1 = ppc_hash64_hpte1(cpu, hptes, slot); - - base_pg_shift = ppc_hash64_hpte_page_shift_noslb(cpu, pte0, pte1); - assert(base_pg_shift); /* H_ENTER shouldn't allow a bad encoding */ - avpn = HPTE64_V_AVPN_VAL(pte0) & ~(((1ULL << base_pg_shift) - 1) >> 23); - - if (pte0 & HPTE64_V_SECONDARY) { - pteg = ~pteg; - } - - if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_256M) { - uint64_t offset, vsid; - - /* We only have 28 - 23 bits of offset in avpn */ - offset = (avpn & 0x1f) << 23; - vsid = avpn >> 5; - /* We can find more bits from the pteg value */ - if (base_pg_shift < 23) { - offset |= ((vsid ^ pteg) & old_hash_mask) << base_pg_shift; - } - - hash = vsid ^ (offset >> base_pg_shift); - } else if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_1T) { - uint64_t offset, vsid; - - /* We only have 40 - 23 bits of seg_off in avpn */ - offset = (avpn & 0x1ffff) << 23; - vsid = avpn >> 17; - if (base_pg_shift < 23) { - offset |= ((vsid ^ (vsid << 25) ^ pteg) & old_hash_mask) - << base_pg_shift; - } - - hash = vsid ^ (vsid << 25) ^ (offset >> base_pg_shift); - } else { - error_report("rehash_pte: Bad segment size in HPTE"); + if (kvm_enabled()) { return H_HARDWARE; } - new_pteg = hash & new_hash_mask; - if (pte0 & HPTE64_V_SECONDARY) { - assert(~pteg == (hash & old_hash_mask)); - new_pteg = ~new_pteg; - } else { - assert(pteg == (hash & old_hash_mask)); - } - assert((oldsize != newsize) || (pteg == new_pteg)); - replace_pte0 = new_hpte_load0(new_hpt, new_pteg, slot); - /* - * Strictly speaking, we don't need all these tests, since we only - * ever rehash bolted HPTEs. We might in future handle non-bolted - * HPTEs, though so make the logic correct for those cases as - * well. - */ - if (replace_pte0 & HPTE64_V_VALID) { - assert(newsize < oldsize); - if (replace_pte0 & HPTE64_V_BOLTED) { - if (pte0 & HPTE64_V_BOLTED) { - /* Bolted collision, nothing we can do */ - return H_PTEG_FULL; - } else { - /* Discard this hpte */ - return H_SUCCESS; - } - } - } - - new_hpte_store(new_hpt, new_pteg, slot, pte0, pte1); - return H_SUCCESS; -} - -static int rehash_hpt(PowerPCCPU *cpu, - void *old_hpt, uint64_t oldsize, - void *new_hpt, uint64_t newsize) -{ - uint64_t n_ptegs = oldsize >> 7; - uint64_t pteg; - int slot; - int rc; - - for (pteg = 0; pteg < n_ptegs; pteg++) { - hwaddr ptex = pteg * HPTES_PER_GROUP; - const ppc_hash_pte64_t *hptes - = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); - - if (!hptes) { - return H_HARDWARE; - } - - for (slot = 0; slot < HPTES_PER_GROUP; slot++) { - rc = rehash_hpte(cpu, hptes, old_hpt, oldsize, new_hpt, newsize, - pteg, slot); - if (rc != H_SUCCESS) { - ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); - return rc; - } - } - ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); - } - - return H_SUCCESS; + return softmmu_resize_hpt_prepare(cpu, spapr, shift); } static void do_push_sregs_to_kvm_pr(CPUState *cs, run_on_cpu_data data) @@ -675,7 +137,7 @@ static void do_push_sregs_to_kvm_pr(CPUState *cs, run_on_cpu_data data) } } -static void push_sregs_to_kvm_pr(SpaprMachineState *spapr) +void push_sregs_to_kvm_pr(SpaprMachineState *spapr) { CPUState *cs; @@ -700,9 +162,7 @@ static target_ulong h_resize_hpt_commit(PowerPCCPU *cpu, { target_ulong flags = args[0]; target_ulong shift = args[1]; - SpaprPendingHpt *pending = spapr->pending_hpt; int rc; - size_t newsize; if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DISABLED) { return H_AUTHORITY; @@ -725,42 +185,14 @@ static target_ulong h_resize_hpt_commit(PowerPCCPU *cpu, return rc; } - if (flags != 0) { - return H_PARAMETER; - } - - if (!pending || (pending->shift != shift)) { - /* no matching prepare */ - return H_CLOSED; - } - - if (!pending->complete) { - /* prepare has not completed */ - return H_BUSY; + if (kvm_enabled()) { + return H_HARDWARE; } - /* Shouldn't have got past PREPARE without an HPT */ - g_assert(spapr->htab_shift); - - newsize = 1ULL << pending->shift; - rc = rehash_hpt(cpu, spapr->htab, HTAB_SIZE(spapr), - pending->hpt, newsize); - if (rc == H_SUCCESS) { - qemu_vfree(spapr->htab); - spapr->htab = pending->hpt; - spapr->htab_shift = pending->shift; - - push_sregs_to_kvm_pr(spapr); - - pending->hpt = NULL; /* so it's not free()d */ - } + return softmmu_resize_hpt_commit(cpu, spapr, flags, shift); +} - /* Clean up */ - spapr->pending_hpt = NULL; - free_pending_hpt(pending); - return rc; -} static target_ulong h_set_sprg0(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) @@ -774,12 +206,12 @@ static target_ulong h_set_sprg0(PowerPCCPU *cpu, SpaprMachineState *spapr, static target_ulong h_set_dabr(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { - if (!has_spr(cpu, SPR_DABR)) { + if (!ppc_has_spr(cpu, SPR_DABR)) { return H_HARDWARE; /* DABR register not available */ } cpu_synchronize_state(CPU(cpu)); - if (has_spr(cpu, SPR_DABRX)) { + if (ppc_has_spr(cpu, SPR_DABRX)) { cpu->env.spr[SPR_DABRX] = 0x3; /* Use Problem and Privileged state */ } else if (!(args[0] & 0x4)) { /* Breakpoint Translation set? */ return H_RESERVED_DABR; @@ -794,7 +226,7 @@ static target_ulong h_set_xdabr(PowerPCCPU *cpu, SpaprMachineState *spapr, { target_ulong dabrx = args[1]; - if (!has_spr(cpu, SPR_DABR) || !has_spr(cpu, SPR_DABRX)) { + if (!ppc_has_spr(cpu, SPR_DABR) || !ppc_has_spr(cpu, SPR_DABRX)) { return H_HARDWARE; } @@ -1760,18 +1192,8 @@ target_ulong do_client_architecture_support(PowerPCCPU *cpu, spapr_ovec_intersect(spapr->ov5_cas, spapr->ov5, ov5_guest); spapr_ovec_cleanup(ov5_guest); - if (guest_radix) { - if (kvm_enabled() && !kvmppc_has_cap_mmu_radix()) { - error_report("Guest requested unavailable MMU mode (radix)."); - exit(EXIT_FAILURE); - } - } else { - if (kvm_enabled() && kvmppc_has_cap_mmu_radix() - && !kvmppc_has_cap_mmu_hash_v3()) { - error_report("Guest requested unavailable MMU mode (hash)."); - exit(EXIT_FAILURE); - } - } + spapr_check_mmu_mode(guest_radix); + spapr->cas_pre_isa3_guest = !spapr_ovec_test(ov1_guest, OV1_PPC_3_00); spapr_ovec_cleanup(ov1_guest); @@ -2023,16 +1445,34 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, return H_FUNCTION; } -static void hypercall_register_types(void) +#ifndef CONFIG_TCG +static target_ulong h_softmmu(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + g_assert_not_reached(); +} + +static void hypercall_register_softmmu(void) { /* hcall-pft */ - spapr_register_hypercall(H_ENTER, h_enter); - spapr_register_hypercall(H_REMOVE, h_remove); - spapr_register_hypercall(H_PROTECT, h_protect); - spapr_register_hypercall(H_READ, h_read); + spapr_register_hypercall(H_ENTER, h_softmmu); + spapr_register_hypercall(H_REMOVE, h_softmmu); + spapr_register_hypercall(H_PROTECT, h_softmmu); + spapr_register_hypercall(H_READ, h_softmmu); /* hcall-bulk */ - spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); + spapr_register_hypercall(H_BULK_REMOVE, h_softmmu); +} +#else +static void hypercall_register_softmmu(void) +{ + /* DO NOTHING */ +} +#endif + +static void hypercall_register_types(void) +{ + hypercall_register_softmmu(); /* hcall-hpt-resize */ spapr_register_hypercall(H_RESIZE_HPT_PREPARE, h_resize_hpt_prepare); diff --git a/hw/ppc/spapr_softmmu.c b/hw/ppc/spapr_softmmu.c new file mode 100644 index 0000000000..6c6b86dd3c --- /dev/null +++ b/hw/ppc/spapr_softmmu.c @@ -0,0 +1,627 @@ +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "sysemu/hw_accel.h" +#include "sysemu/runstate.h" +#include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "helper_regs.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_cpu_core.h" +#include "mmu-hash64.h" +#include "cpu-models.h" +#include "trace.h" +#include "kvm_ppc.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/spapr_ovec.h" +#include "mmu-book3s-v3.h" +#include "hw/mem/memory-device.h" + +static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) +{ + /* + * hash value/pteg group index is normalized by HPT mask + */ + if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) { + return false; + } + return true; +} + +static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong ptex = args[1]; + target_ulong pteh = args[2]; + target_ulong ptel = args[3]; + unsigned apshift; + target_ulong raddr; + target_ulong slot; + const ppc_hash_pte64_t *hptes; + + apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel); + if (!apshift) { + /* Bad page size encoding */ + return H_PARAMETER; + } + + raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << apshift) - 1); + + if (is_ram_address(spapr, raddr)) { + /* Regular RAM - should have WIMG=0010 */ + if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { + return H_PARAMETER; + } + } else { + target_ulong wimg_flags; + /* 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? */ + wimg_flags = (ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)); + + if (wimg_flags != HPTE64_R_I && + wimg_flags != (HPTE64_R_I | HPTE64_R_M)) { + return H_PARAMETER; + } + } + + pteh &= ~0x60ULL; + + if (!valid_ptex(cpu, ptex)) { + return H_PARAMETER; + } + + slot = ptex & 7ULL; + ptex = ptex & ~7ULL; + + if (likely((flags & H_EXACT) == 0)) { + hptes = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); + for (slot = 0; slot < 8; slot++) { + if (!(ppc_hash64_hpte0(cpu, hptes, slot) & HPTE64_V_VALID)) { + break; + } + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); + if (slot == 8) { + return H_PTEG_FULL; + } + } else { + hptes = ppc_hash64_map_hptes(cpu, ptex + slot, 1); + if (ppc_hash64_hpte0(cpu, hptes, 0) & HPTE64_V_VALID) { + ppc_hash64_unmap_hptes(cpu, hptes, ptex + slot, 1); + return H_PTEG_FULL; + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); + } + + spapr_store_hpte(cpu, ptex + slot, pteh | HPTE64_V_HPTE_DIRTY, ptel); + + args[0] = ptex + slot; + return H_SUCCESS; +} + +typedef enum { + REMOVE_SUCCESS = 0, + REMOVE_NOT_FOUND = 1, + REMOVE_PARM = 2, + REMOVE_HW = 3, +} RemoveResult; + +static RemoveResult remove_hpte(PowerPCCPU *cpu + , target_ulong ptex, + target_ulong avpn, + target_ulong flags, + target_ulong *vp, target_ulong *rp) +{ + const ppc_hash_pte64_t *hptes; + target_ulong v, r; + + if (!valid_ptex(cpu, ptex)) { + return REMOVE_PARM; + } + + hptes = ppc_hash64_map_hptes(cpu, ptex, 1); + v = ppc_hash64_hpte0(cpu, hptes, 0); + r = ppc_hash64_hpte1(cpu, hptes, 0); + ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); + + if ((v & HPTE64_V_VALID) == 0 || + ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || + ((flags & H_ANDCOND) && (v & avpn) != 0)) { + return REMOVE_NOT_FOUND; + } + *vp = v; + *rp = r; + spapr_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0); + ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); + return REMOVE_SUCCESS; +} + +static target_ulong h_remove(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong ptex = args[1]; + target_ulong avpn = args[2]; + RemoveResult ret; + + ret = remove_hpte(cpu, ptex, avpn, flags, + &args[0], &args[1]); + + switch (ret) { + case REMOVE_SUCCESS: + check_tlb_flush(env, true); + return H_SUCCESS; + + case REMOVE_NOT_FOUND: + return H_NOT_FOUND; + + case REMOVE_PARM: + return H_PARAMETER; + + case REMOVE_HW: + return H_HARDWARE; + } + + g_assert_not_reached(); +} + +#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(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + int i; + target_ulong rc = H_SUCCESS; + + 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(cpu, *tsh & H_BULK_REMOVE_PTEX, tsl, + (*tsh & H_BULK_REMOVE_FLAGS) >> 26, + &v, &r); + + *tsh |= ret << 60; + + switch (ret) { + case REMOVE_SUCCESS: + *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43; + break; + + case REMOVE_PARM: + rc = H_PARAMETER; + goto exit; + + case REMOVE_HW: + rc = H_HARDWARE; + goto exit; + } + } + exit: + check_tlb_flush(env, true); + + return rc; +} + +static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong ptex = args[1]; + target_ulong avpn = args[2]; + const ppc_hash_pte64_t *hptes; + target_ulong v, r; + + if (!valid_ptex(cpu, ptex)) { + return H_PARAMETER; + } + + hptes = ppc_hash64_map_hptes(cpu, ptex, 1); + v = ppc_hash64_hpte0(cpu, hptes, 0); + r = ppc_hash64_hpte1(cpu, hptes, 0); + ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); + + if ((v & HPTE64_V_VALID) == 0 || + ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { + return H_NOT_FOUND; + } + + r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N | + HPTE64_R_KEY_HI | HPTE64_R_KEY_LO); + r |= (flags << 55) & HPTE64_R_PP0; + r |= (flags << 48) & HPTE64_R_KEY_HI; + r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); + spapr_store_hpte(cpu, ptex, + (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); + ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); + /* Flush the tlb */ + check_tlb_flush(env, true); + /* Don't need a memory barrier, due to qemu's global lock */ + spapr_store_hpte(cpu, ptex, v | HPTE64_V_HPTE_DIRTY, r); + return H_SUCCESS; +} + +static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong ptex = args[1]; + int i, ridx, n_entries = 1; + const ppc_hash_pte64_t *hptes; + + if (!valid_ptex(cpu, ptex)) { + return H_PARAMETER; + } + + if (flags & H_READ_4) { + /* Clear the two low order bits */ + ptex &= ~(3ULL); + n_entries = 4; + } + + hptes = ppc_hash64_map_hptes(cpu, ptex, n_entries); + for (i = 0, ridx = 0; i < n_entries; i++) { + args[ridx++] = ppc_hash64_hpte0(cpu, hptes, i); + args[ridx++] = ppc_hash64_hpte1(cpu, hptes, i); + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, n_entries); + + return H_SUCCESS; +} + +struct SpaprPendingHpt { + /* These fields are read-only after initialization */ + int shift; + QemuThread thread; + + /* These fields are protected by the BQL */ + bool complete; + + /* These fields are private to the preparation thread if + * !complete, otherwise protected by the BQL */ + int ret; + void *hpt; +}; + +static void free_pending_hpt(SpaprPendingHpt *pending) +{ + if (pending->hpt) { + qemu_vfree(pending->hpt); + } + + g_free(pending); +} + +static void *hpt_prepare_thread(void *opaque) +{ + SpaprPendingHpt *pending = opaque; + size_t size = 1ULL << pending->shift; + + pending->hpt = qemu_try_memalign(size, size); + if (pending->hpt) { + memset(pending->hpt, 0, size); + pending->ret = H_SUCCESS; + } else { + pending->ret = H_NO_MEM; + } + + qemu_mutex_lock_iothread(); + + if (SPAPR_MACHINE(qdev_get_machine())->pending_hpt == pending) { + /* Ready to go */ + pending->complete = true; + } else { + /* We've been cancelled, clean ourselves up */ + free_pending_hpt(pending); + } + + qemu_mutex_unlock_iothread(); + return NULL; +} + +/* Must be called with BQL held */ +static void cancel_hpt_prepare(SpaprMachineState *spapr) +{ + SpaprPendingHpt *pending = spapr->pending_hpt; + + /* Let the thread know it's cancelled */ + spapr->pending_hpt = NULL; + + if (!pending) { + /* Nothing to do */ + return; + } + + if (!pending->complete) { + /* thread will clean itself up */ + return; + } + + free_pending_hpt(pending); +} + +target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong shift) +{ + SpaprPendingHpt *pending = spapr->pending_hpt; + + if (pending) { + /* something already in progress */ + if (pending->shift == shift) { + /* and it's suitable */ + if (pending->complete) { + return pending->ret; + } else { + return H_LONG_BUSY_ORDER_100_MSEC; + } + } + + /* not suitable, cancel and replace */ + cancel_hpt_prepare(spapr); + } + + if (!shift) { + /* nothing to do */ + return H_SUCCESS; + } + + /* start new prepare */ + + pending = g_new0(SpaprPendingHpt, 1); + pending->shift = shift; + pending->ret = H_HARDWARE; + + qemu_thread_create(&pending->thread, "sPAPR HPT prepare", + hpt_prepare_thread, pending, QEMU_THREAD_DETACHED); + + spapr->pending_hpt = pending; + + /* In theory we could estimate the time more accurately based on + * the new size, but there's not much point */ + return H_LONG_BUSY_ORDER_100_MSEC; +} + +static uint64_t new_hpte_load0(void *htab, uint64_t pteg, int slot) +{ + uint8_t *addr = htab; + + addr += pteg * HASH_PTEG_SIZE_64; + addr += slot * HASH_PTE_SIZE_64; + return ldq_p(addr); +} + +static void new_hpte_store(void *htab, uint64_t pteg, int slot, + uint64_t pte0, uint64_t pte1) +{ + uint8_t *addr = htab; + + addr += pteg * HASH_PTEG_SIZE_64; + addr += slot * HASH_PTE_SIZE_64; + + stq_p(addr, pte0); + stq_p(addr + HASH_PTE_SIZE_64 / 2, pte1); +} + +static int rehash_hpte(PowerPCCPU *cpu, + const ppc_hash_pte64_t *hptes, + void *old_hpt, uint64_t oldsize, + void *new_hpt, uint64_t newsize, + uint64_t pteg, int slot) +{ + uint64_t old_hash_mask = (oldsize >> 7) - 1; + uint64_t new_hash_mask = (newsize >> 7) - 1; + target_ulong pte0 = ppc_hash64_hpte0(cpu, hptes, slot); + target_ulong pte1; + uint64_t avpn; + unsigned base_pg_shift; + uint64_t hash, new_pteg, replace_pte0; + + if (!(pte0 & HPTE64_V_VALID) || !(pte0 & HPTE64_V_BOLTED)) { + return H_SUCCESS; + } + + pte1 = ppc_hash64_hpte1(cpu, hptes, slot); + + base_pg_shift = ppc_hash64_hpte_page_shift_noslb(cpu, pte0, pte1); + assert(base_pg_shift); /* H_ENTER shouldn't allow a bad encoding */ + avpn = HPTE64_V_AVPN_VAL(pte0) & ~(((1ULL << base_pg_shift) - 1) >> 23); + + if (pte0 & HPTE64_V_SECONDARY) { + pteg = ~pteg; + } + + if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_256M) { + uint64_t offset, vsid; + + /* We only have 28 - 23 bits of offset in avpn */ + offset = (avpn & 0x1f) << 23; + vsid = avpn >> 5; + /* We can find more bits from the pteg value */ + if (base_pg_shift < 23) { + offset |= ((vsid ^ pteg) & old_hash_mask) << base_pg_shift; + } + + hash = vsid ^ (offset >> base_pg_shift); + } else if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_1T) { + uint64_t offset, vsid; + + /* We only have 40 - 23 bits of seg_off in avpn */ + offset = (avpn & 0x1ffff) << 23; + vsid = avpn >> 17; + if (base_pg_shift < 23) { + offset |= ((vsid ^ (vsid << 25) ^ pteg) & old_hash_mask) + << base_pg_shift; + } + + hash = vsid ^ (vsid << 25) ^ (offset >> base_pg_shift); + } else { + error_report("rehash_pte: Bad segment size in HPTE"); + return H_HARDWARE; + } + + new_pteg = hash & new_hash_mask; + if (pte0 & HPTE64_V_SECONDARY) { + assert(~pteg == (hash & old_hash_mask)); + new_pteg = ~new_pteg; + } else { + assert(pteg == (hash & old_hash_mask)); + } + assert((oldsize != newsize) || (pteg == new_pteg)); + replace_pte0 = new_hpte_load0(new_hpt, new_pteg, slot); + /* + * Strictly speaking, we don't need all these tests, since we only + * ever rehash bolted HPTEs. We might in future handle non-bolted + * HPTEs, though so make the logic correct for those cases as + * well. + */ + if (replace_pte0 & HPTE64_V_VALID) { + assert(newsize < oldsize); + if (replace_pte0 & HPTE64_V_BOLTED) { + if (pte0 & HPTE64_V_BOLTED) { + /* Bolted collision, nothing we can do */ + return H_PTEG_FULL; + } else { + /* Discard this hpte */ + return H_SUCCESS; + } + } + } + + new_hpte_store(new_hpt, new_pteg, slot, pte0, pte1); + return H_SUCCESS; +} + +static int rehash_hpt(PowerPCCPU *cpu, + void *old_hpt, uint64_t oldsize, + void *new_hpt, uint64_t newsize) +{ + uint64_t n_ptegs = oldsize >> 7; + uint64_t pteg; + int slot; + int rc; + + for (pteg = 0; pteg < n_ptegs; pteg++) { + hwaddr ptex = pteg * HPTES_PER_GROUP; + const ppc_hash_pte64_t *hptes + = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); + + if (!hptes) { + return H_HARDWARE; + } + + for (slot = 0; slot < HPTES_PER_GROUP; slot++) { + rc = rehash_hpte(cpu, hptes, old_hpt, oldsize, new_hpt, newsize, + pteg, slot); + if (rc != H_SUCCESS) { + ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); + return rc; + } + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); + } + + return H_SUCCESS; +} + +target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong flags, + target_ulong shift) +{ + SpaprPendingHpt *pending = spapr->pending_hpt; + int rc; + size_t newsize; + + if (flags != 0) { + return H_PARAMETER; + } + + if (!pending || (pending->shift != shift)) { + /* no matching prepare */ + return H_CLOSED; + } + + if (!pending->complete) { + /* prepare has not completed */ + return H_BUSY; + } + + /* Shouldn't have got past PREPARE without an HPT */ + g_assert(spapr->htab_shift); + + newsize = 1ULL << pending->shift; + rc = rehash_hpt(cpu, spapr->htab, HTAB_SIZE(spapr), + pending->hpt, newsize); + if (rc == H_SUCCESS) { + qemu_vfree(spapr->htab); + spapr->htab = pending->hpt; + spapr->htab_shift = pending->shift; + + push_sregs_to_kvm_pr(spapr); + + pending->hpt = NULL; /* so it's not free()d */ + } + + /* Clean up */ + spapr->pending_hpt = NULL; + free_pending_hpt(pending); + + return rc; +} + +static void hypercall_register_types(void) +{ + /* hcall-pft */ + spapr_register_hypercall(H_ENTER, h_enter); + spapr_register_hypercall(H_REMOVE, h_remove); + spapr_register_hypercall(H_PROTECT, h_protect); + spapr_register_hypercall(H_READ, h_read); + + /* hcall-bulk */ + spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); + +} + +type_init(hypercall_register_types) diff --git a/hw/tricore/meson.build b/hw/tricore/meson.build index 77ff6fd137..47e36bb077 100644 --- a/hw/tricore/meson.build +++ b/hw/tricore/meson.build @@ -1,5 +1,6 @@ tricore_ss = ss.source_set() tricore_ss.add(when: 'CONFIG_TRICORE', if_true: files('tricore_testboard.c')) +tricore_ss.add(when: 'CONFIG_TRICORE', if_true: files('tricore_testdevice.c')) tricore_ss.add(when: 'CONFIG_TRIBOARD', if_true: files('triboard.c')) tricore_ss.add(when: 'CONFIG_TC27X_SOC', if_true: files('tc27x_soc.c')) diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index 51658d9e37..b6810e3be0 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -27,6 +27,7 @@ #include "hw/loader.h" #include "elf.h" #include "hw/tricore/tricore.h" +#include "hw/tricore/tricore_testdevice.h" #include "qemu/error-report.h" @@ -56,6 +57,7 @@ static void tricore_testboard_init(MachineState *machine, int board_id) { TriCoreCPU *cpu; CPUTriCoreState *env; + TriCoreTestDeviceState *test_dev; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ext_cram = g_new(MemoryRegion, 1); @@ -87,6 +89,12 @@ static void tricore_testboard_init(MachineState *machine, int board_id) memory_region_add_subregion(sysmem, 0xf0050000, pcp_data); memory_region_add_subregion(sysmem, 0xf0060000, pcp_text); + test_dev = g_new(TriCoreTestDeviceState, 1); + object_initialize(test_dev, sizeof(TriCoreTestDeviceState), + TYPE_TRICORE_TESTDEVICE); + memory_region_add_subregion(sysmem, 0xf0000000, &test_dev->iomem); + + tricoretb_binfo.ram_size = machine->ram_size; tricoretb_binfo.kernel_filename = machine->kernel_filename; diff --git a/hw/tricore/tricore_testdevice.c b/hw/tricore/tricore_testdevice.c new file mode 100644 index 0000000000..a1563aa568 --- /dev/null +++ b/hw/tricore/tricore_testdevice.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2021 Bastian Koppelmann Paderborn University + * + * 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/>. + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/qdev-properties.h" +#include "hw/tricore/tricore_testdevice.h" + +static void tricore_testdevice_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + exit(value); +} + +static uint64_t tricore_testdevice_read(void *opaque, hwaddr offset, + unsigned size) +{ + return 0xdeadbeef; +} + +static void tricore_testdevice_reset(DeviceState *dev) +{ +} + +static const MemoryRegionOps tricore_testdevice_ops = { + .read = tricore_testdevice_read, + .write = tricore_testdevice_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void tricore_testdevice_init(Object *obj) +{ + TriCoreTestDeviceState *s = TRICORE_TESTDEVICE(obj); + /* map memory */ + memory_region_init_io(&s->iomem, OBJECT(s), &tricore_testdevice_ops, s, + "tricore_testdevice", 0x4); +} + +static Property tricore_testdevice_properties[] = { + DEFINE_PROP_END_OF_LIST() +}; + +static void tricore_testdevice_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, tricore_testdevice_properties); + dc->reset = tricore_testdevice_reset; +} + +static const TypeInfo tricore_testdevice_info = { + .name = TYPE_TRICORE_TESTDEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TriCoreTestDeviceState), + .instance_init = tricore_testdevice_init, + .class_init = tricore_testdevice_class_init, +}; + +static void tricore_testdevice_register_types(void) +{ + type_register_static(&tricore_testdevice_info); +} + +type_init(tricore_testdevice_register_types) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index ded0c10453..ee57abe045 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -1909,6 +1909,11 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque) return err; } } + if (dev->num_queues && dev->max_queues < dev->num_queues) { + error_report("The maximum number of queues supported by the " + "backend is %" PRIu64, dev->max_queues); + return -EINVAL; + } if (virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM) && !(virtio_has_feature(dev->protocol_features, diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d6332d45c3..859978d248 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -69,6 +69,11 @@ void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) return; } + if (has_iommu && !virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { + error_setg(errp, "iommu_platform=true is not supported by the device"); + return; + } + if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent, &local_err); } diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 7f40a158f4..bbf817af46 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -582,6 +582,12 @@ typedef target_ulong (*spapr_hcall_fn)(PowerPCCPU *cpu, SpaprMachineState *sm, void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn); target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, target_ulong *args); +target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong shift); +target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong flags, target_ulong shift); +bool is_ram_address(SpaprMachineState *spapr, hwaddr addr); +void push_sregs_to_kvm_pr(SpaprMachineState *spapr); /* Virtual Processor Area structure constants */ #define VPA_MIN_SIZE 640 @@ -821,6 +827,7 @@ void spapr_dt_events(SpaprMachineState *sm, void *fdt); void close_htab_fd(SpaprMachineState *spapr); void spapr_setup_hpt(SpaprMachineState *spapr); void spapr_free_hpt(SpaprMachineState *spapr); +void spapr_check_mmu_mode(bool guest_radix); SpaprTceTable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(SpaprTceTable *tcet, uint32_t page_shift, uint64_t bus_offset, diff --git a/include/hw/tricore/tricore_testdevice.h b/include/hw/tricore/tricore_testdevice.h new file mode 100644 index 0000000000..2c56c51bcb --- /dev/null +++ b/include/hw/tricore/tricore_testdevice.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018-2021 Bastian Koppelmann Paderborn University + * + * 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/>. + */ + + +#ifndef HW_TRICORE_TESTDEV_H +#define HW_TRICORE_TESTDEV_H + +#include "hw/sysbus.h" +#include "hw/hw.h" + +#define TYPE_TRICORE_TESTDEVICE "tricore_testdevice" +#define TRICORE_TESTDEVICE(obj) \ + OBJECT_CHECK(TriCoreTestDeviceState, (obj), TYPE_TRICORE_TESTDEVICE) + +typedef struct { + /* <private> */ + SysBusDevice parent_obj; + + /* <public> */ + MemoryRegion iomem; + +} TriCoreTestDeviceState; + +#endif diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 4a8bc75415..21a9a52088 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -74,6 +74,8 @@ struct vhost_dev { int nvqs; /* the first virtqueue which would be used by this vhost dev */ int vq_index; + /* if non-zero, minimum required value for max_queues */ + int num_queues; uint64_t features; uint64_t acked_features; uint64_t backend_features; diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 4a0f6c8dc2..fa91ea0eed 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -423,12 +423,6 @@ void cpu_loop(CPUPPCState *env) cpu_abort(cs, "Maintenance exception while in user mode. " "Aborting\n"); break; - case POWERPC_EXCP_STOP: /* stop translation */ - /* We did invalidate the instruction cache. Go on */ - break; - case POWERPC_EXCP_BRANCH: /* branch instruction: */ - /* We just stopped because of a branch. Go on */ - break; case POWERPC_EXCP_SYSCALL_USER: /* system call in user-mode emulation */ /* WARNING: diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index 9ab04b2c38..9210e61ef4 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -17,7 +17,6 @@ #include "elf.h" #include "sysemu/dump.h" #include "sysemu/kvm.h" -#include "exec/helper-proto.h" #ifdef TARGET_PPC64 #define ELFCLASS ELFCLASS64 @@ -176,7 +175,7 @@ static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) vmxregset->avr[i].u64[1] = avr->u64[1]; } } - vmxregset->vscr.u32[3] = cpu_to_dump32(s, helper_mfvscr(&cpu->env)); + vmxregset->vscr.u32[3] = cpu_to_dump32(s, ppc_get_vscr(&cpu->env)); } static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index e501a7ff6f..d957d1a687 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -20,6 +20,10 @@ #include "qemu/osdep.h" #include "cpu.h" #include "cpu-models.h" +#include "cpu-qom.h" +#include "exec/log.h" +#include "fpu/softfloat-helpers.h" +#include "mmu-hash64.h" target_ulong cpu_read_xer(CPUPPCState *env) { @@ -45,3 +49,46 @@ void cpu_write_xer(CPUPPCState *env, target_ulong xer) (1ul << XER_OV) | (1ul << XER_CA) | (1ul << XER_OV32) | (1ul << XER_CA32)); } + +void ppc_store_vscr(CPUPPCState *env, uint32_t vscr) +{ + env->vscr = vscr & ~(1u << VSCR_SAT); + /* Which bit we set is completely arbitrary, but clear the rest. */ + env->vscr_sat.u64[0] = vscr & (1u << VSCR_SAT); + env->vscr_sat.u64[1] = 0; + set_flush_to_zero((vscr >> VSCR_NJ) & 1, &env->vec_status); +} + +uint32_t ppc_get_vscr(CPUPPCState *env) +{ + uint32_t sat = (env->vscr_sat.u64[0] | env->vscr_sat.u64[1]) != 0; + return env->vscr | (sat << VSCR_SAT); +} + +#ifdef CONFIG_SOFTMMU +void ppc_store_sdr1(CPUPPCState *env, target_ulong value) +{ + PowerPCCPU *cpu = env_archcpu(env); + qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); + assert(!cpu->vhyp); +#if defined(TARGET_PPC64) + if (mmu_is_64bit(env->mmu_model)) { + target_ulong sdr_mask = SDR_64_HTABORG | SDR_64_HTABSIZE; + target_ulong htabsize = value & SDR_64_HTABSIZE; + + if (value & ~sdr_mask) { + error_report("Invalid bits 0x"TARGET_FMT_lx" set in SDR1", + value & ~sdr_mask); + value &= sdr_mask; + } + if (htabsize > 28) { + error_report("Invalid HTABSIZE 0x" TARGET_FMT_lx" stored in SDR1", + htabsize); + return; + } + } +#endif /* defined(TARGET_PPC64) */ + /* FIXME: Should check for valid HTABMASK values in 32-bit case */ + env->spr[SPR_SDR1] = value; +} +#endif /* CONFIG_SOFTMMU */ diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 733a2168c4..cab33a3680 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -131,11 +131,7 @@ enum { POWERPC_EXCP_SYSCALL_VECTORED = 102, /* scv exception */ /* EOL */ POWERPC_EXCP_NB = 103, - /* QEMU exceptions: used internally during code translation */ - POWERPC_EXCP_STOP = 0x200, /* stop translation */ - POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */ /* QEMU exceptions: special cases we want to stop translation */ - POWERPC_EXCP_SYNC = 0x202, /* context synchronizing instruction */ POWERPC_EXCP_SYSCALL_USER = 0x203, /* System call in user mode only */ }; @@ -1297,6 +1293,7 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value); void ppc_store_ptcr(CPUPPCState *env, target_ulong value); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr(CPUPPCState *env, target_ulong value); +void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val); void ppc_cpu_list(void); @@ -2641,7 +2638,15 @@ static inline ppc_avr_t *cpu_avr_ptr(CPUPPCState *env, int i) return (ppc_avr_t *)((uintptr_t)env + avr_full_offset(i)); } +static inline bool ppc_has_spr(PowerPCCPU *cpu, int spr) +{ + /* We can test whether the SPR is defined by checking for a valid name */ + return cpu->env.spr_cb[spr].name != NULL; +} + void dump_mmu(CPUPPCState *env); void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len); +void ppc_store_vscr(CPUPPCState *env, uint32_t vscr); +uint32_t ppc_get_vscr(CPUPPCState *env); #endif /* PPC_CPU_H */ diff --git a/target/ppc/translate_init.c.inc b/target/ppc/cpu_init.c index 66e6a4a746..22ecbccad8 100644 --- a/target/ppc/translate_init.c.inc +++ b/target/ppc/cpu_init.c @@ -18,6 +18,7 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "disas/dis-asm.h" #include "exec/gdbstub.h" #include "kvm_ppc.h" @@ -42,682 +43,19 @@ #include "fpu/softfloat.h" #include "qapi/qapi-commands-machine-target.h" +#include "exec/helper-proto.h" +#include "helper_regs.h" +#include "internal.h" +#include "spr_tcg.h" + /* #define PPC_DEBUG_SPR */ -/* #define PPC_DUMP_SPR_ACCESSES */ /* #define USE_APPLE_GDB */ -/* - * Generic callbacks: - * do nothing but store/retrieve spr value - */ -static void spr_load_dump_spr(int sprn) -{ -#ifdef PPC_DUMP_SPR_ACCESSES - TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_load_dump_spr(cpu_env, t0); - tcg_temp_free_i32(t0); -#endif -} - -static void spr_read_generic(DisasContext *ctx, int gprn, int sprn) -{ - gen_load_spr(cpu_gpr[gprn], sprn); - spr_load_dump_spr(sprn); -} - -static void spr_store_dump_spr(int sprn) -{ -#ifdef PPC_DUMP_SPR_ACCESSES - TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_store_dump_spr(cpu_env, t0); - tcg_temp_free_i32(t0); -#endif -} - -static void spr_write_generic(DisasContext *ctx, int sprn, int gprn) -{ - gen_store_spr(sprn, cpu_gpr[gprn]); - spr_store_dump_spr(sprn); -} - -#if !defined(CONFIG_USER_ONLY) -static void spr_write_generic32(DisasContext *ctx, int sprn, int gprn) -{ -#ifdef TARGET_PPC64 - TCGv t0 = tcg_temp_new(); - tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); - gen_store_spr(sprn, t0); - tcg_temp_free(t0); - spr_store_dump_spr(sprn); -#else - spr_write_generic(ctx, sprn, gprn); -#endif -} - -static void spr_write_clear(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - gen_load_spr(t0, sprn); - tcg_gen_neg_tl(t1, cpu_gpr[gprn]); - tcg_gen_and_tl(t0, t0, t1); - gen_store_spr(sprn, t0); - tcg_temp_free(t0); - tcg_temp_free(t1); -} - -static void spr_access_nop(DisasContext *ctx, int sprn, int gprn) -{ -} - -#endif - -/* SPR common to all PowerPC */ -/* XER */ -static void spr_read_xer(DisasContext *ctx, int gprn, int sprn) -{ - gen_read_xer(ctx, cpu_gpr[gprn]); -} - -static void spr_write_xer(DisasContext *ctx, int sprn, int gprn) -{ - gen_write_xer(cpu_gpr[gprn]); -} - -/* LR */ -static void spr_read_lr(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_mov_tl(cpu_gpr[gprn], cpu_lr); -} - -static void spr_write_lr(DisasContext *ctx, 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(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar); -} - -static void spr_write_cfar(DisasContext *ctx, 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(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_mov_tl(cpu_gpr[gprn], cpu_ctr); -} - -static void spr_write_ctr(DisasContext *ctx, int sprn, int gprn) -{ - tcg_gen_mov_tl(cpu_ctr, cpu_gpr[gprn]); -} - -/* User read access to SPR */ -/* USPRx */ -/* UMMCRx */ -/* UPMCx */ -/* USIA */ -/* UDECR */ -static void spr_read_ureg(DisasContext *ctx, int gprn, int sprn) -{ - gen_load_spr(cpu_gpr[gprn], sprn + 0x10); -} - -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) -static void spr_write_ureg(DisasContext *ctx, int sprn, int gprn) -{ - gen_store_spr(sprn + 0x10, cpu_gpr[gprn]); -} -#endif - -/* SPR common to all non-embedded PowerPC */ -/* DECR */ -#if !defined(CONFIG_USER_ONLY) -static void spr_read_decr(DisasContext *ctx, int gprn, int sprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_load_decr(cpu_gpr[gprn], cpu_env); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_decr(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_decr(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} -#endif - -/* SPR common to all non-embedded PowerPC, except 601 */ -/* Time base */ -static void spr_read_tbl(DisasContext *ctx, int gprn, int sprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_load_tbl(cpu_gpr[gprn], cpu_env); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - gen_stop_exception(ctx); - } -} - -static void spr_read_tbu(DisasContext *ctx, int gprn, int sprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_load_tbu(cpu_gpr[gprn], cpu_env); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - gen_stop_exception(ctx); - } -} - -ATTRIBUTE_UNUSED -static void spr_read_atbl(DisasContext *ctx, int gprn, int sprn) -{ - gen_helper_load_atbl(cpu_gpr[gprn], cpu_env); -} - -ATTRIBUTE_UNUSED -static void spr_read_atbu(DisasContext *ctx, int gprn, int sprn) -{ - gen_helper_load_atbu(cpu_gpr[gprn], cpu_env); -} - -#if !defined(CONFIG_USER_ONLY) -static void spr_write_tbl(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - gen_stop_exception(ctx); - } -} - -static void spr_write_tbu(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - gen_stop_exception(ctx); - } -} - -ATTRIBUTE_UNUSED -static void spr_write_atbl(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_atbl(cpu_env, cpu_gpr[gprn]); -} - -ATTRIBUTE_UNUSED -static void spr_write_atbu(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_atbu(cpu_env, cpu_gpr[gprn]); -} - -#if defined(TARGET_PPC64) -ATTRIBUTE_UNUSED -static void spr_read_purr(DisasContext *ctx, int gprn, int sprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_load_purr(cpu_gpr[gprn], cpu_env); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_purr(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_purr(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -/* HDECR */ -static void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - gen_stop_exception(ctx); - } -} - -static void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - gen_stop_exception(ctx); - } -} - -static void spr_read_vtb(DisasContext *ctx, int gprn, int sprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_load_vtb(cpu_gpr[gprn], cpu_env); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_vtb(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_vtb(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_tbu40(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -#endif -#endif - -#if !defined(CONFIG_USER_ONLY) -/* IBAT0U...IBAT0U */ -/* IBAT0L...IBAT7L */ -static void spr_read_ibat(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, - offsetof(CPUPPCState, - IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); -} - -static void spr_read_ibat_h(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, - offsetof(CPUPPCState, - IBAT[sprn & 1][((sprn - SPR_IBAT4U) / 2) + 4])); -} - -static void spr_write_ibatu(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -static void spr_write_ibatu_h(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4); - gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -static void spr_write_ibatl(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2); - gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -static void spr_write_ibatl_h(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4); - gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -/* DBAT0U...DBAT7U */ -/* DBAT0L...DBAT7L */ -static void spr_read_dbat(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, - offsetof(CPUPPCState, - DBAT[sprn & 1][(sprn - SPR_DBAT0U) / 2])); -} - -static void spr_read_dbat_h(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, - offsetof(CPUPPCState, - DBAT[sprn & 1][((sprn - SPR_DBAT4U) / 2) + 4])); -} - -static void spr_write_dbatu(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2); - gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -static void spr_write_dbatu_h(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4); - gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -static void spr_write_dbatl(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2); - gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -static void spr_write_dbatl_h(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4); - gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -/* SDR1 */ -static void spr_write_sdr1(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_sdr1(cpu_env, cpu_gpr[gprn]); -} - -#if defined(TARGET_PPC64) -/* 64 bits PowerPC specific SPRs */ -/* PIDR */ -static void spr_write_pidr(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_pidr(cpu_env, cpu_gpr[gprn]); -} - -static void spr_write_lpidr(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_lpidr(cpu_env, cpu_gpr[gprn]); -} - -static void spr_read_hior(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix)); -} - -static void spr_write_hior(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0x3FFFFF00000ULL); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); - tcg_temp_free(t0); -} -static void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_ptcr(cpu_env, cpu_gpr[gprn]); -} - -static void spr_write_pcr(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_pcr(cpu_env, cpu_gpr[gprn]); -} - -/* DPDES */ -static void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn) -{ - gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env); -} - -static void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]); -} -#endif -#endif - -/* PowerPC 601 specific registers */ -/* RTC */ -static void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn) -{ - gen_helper_load_601_rtcl(cpu_gpr[gprn], cpu_env); -} - -static void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn) -{ - gen_helper_load_601_rtcu(cpu_gpr[gprn], cpu_env); -} - -#if !defined(CONFIG_USER_ONLY) -static void spr_write_601_rtcu(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_601_rtcu(cpu_env, cpu_gpr[gprn]); -} - -static void spr_write_601_rtcl(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_601_rtcl(cpu_env, cpu_gpr[gprn]); -} - -static void spr_write_hid0_601(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_hid0_601(cpu_env, cpu_gpr[gprn]); - /* Must stop the translation as endianness may have changed */ - gen_stop_exception(ctx); -} -#endif - -/* Unified bats */ -#if !defined(CONFIG_USER_ONLY) -static void spr_read_601_ubat(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, - offsetof(CPUPPCState, - IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); -} - -static void spr_write_601_ubatu(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_601_batl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -static void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_601_batu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} -#endif - -/* PowerPC 40x specific registers */ -#if !defined(CONFIG_USER_ONLY) -static void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_store_spr(sprn, cpu_gpr[gprn]); - gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); - /* We must stop translation as we may have rebooted */ - gen_stop_exception(ctx); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} - -static void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn) -{ - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } -} -#endif - -/* PowerPC 403 specific registers */ -/* PBL1 / PBU1 / PBL2 / PBU2 */ -#if !defined(CONFIG_USER_ONLY) -static void spr_read_403_pbr(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, - offsetof(CPUPPCState, pb[sprn - SPR_403_PBL1])); -} - -static void spr_write_403_pbr(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1); - gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -static void spr_write_pir(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xF); - gen_store_spr(SPR_PIR, t0); - tcg_temp_free(t0); -} -#endif - -/* SPE specific registers */ -static void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_ld_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); - tcg_gen_extu_i32_tl(cpu_gpr[gprn], t0); - tcg_temp_free_i32(t0); -} - -static void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t0, cpu_gpr[gprn]); - tcg_gen_st_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); - tcg_temp_free_i32(t0); -} - -#if !defined(CONFIG_USER_ONLY) -/* Callback used to write the exception vector base */ -static void spr_write_excp_prefix(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivpr_mask)); - tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); - gen_store_spr(sprn, t0); - tcg_temp_free(t0); -} - -static void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn) -{ - int sprn_offs; - - if (sprn >= SPR_BOOKE_IVOR0 && sprn <= SPR_BOOKE_IVOR15) { - sprn_offs = sprn - SPR_BOOKE_IVOR0; - } else if (sprn >= SPR_BOOKE_IVOR32 && sprn <= SPR_BOOKE_IVOR37) { - sprn_offs = sprn - SPR_BOOKE_IVOR32 + 32; - } else if (sprn >= SPR_BOOKE_IVOR38 && sprn <= SPR_BOOKE_IVOR42) { - sprn_offs = sprn - SPR_BOOKE_IVOR38 + 38; - } else { - printf("Trying to write an unknown exception vector %d %03x\n", - sprn, sprn); - gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); - return; - } - - TCGv t0 = tcg_temp_new(); - tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivor_mask)); - tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_vectors[sprn_offs])); - gen_store_spr(sprn, t0); - tcg_temp_free(t0); -} -#endif - static inline void vscr_init(CPUPPCState *env, uint32_t val) { /* Altivec always uses round-to-nearest */ set_float_rounding_mode(float_round_nearest_even, &env->vec_status); - helper_mtvscr(env, val); + ppc_store_vscr(env, val); } /** @@ -813,7 +151,7 @@ static void _spr_register(CPUPPCState *env, int num, const char *name, oea_read, oea_write, 0, ival) /* Generic PowerPC SPRs */ -static void gen_spr_generic(CPUPPCState *env) +static void register_generic_sprs(CPUPPCState *env) { /* Integer processing */ spr_register(env, SPR_XER, "XER", @@ -858,7 +196,7 @@ static void gen_spr_generic(CPUPPCState *env) } /* SPR common to all non-embedded PowerPC, including 601 */ -static void gen_spr_ne_601(CPUPPCState *env) +static void register_ne_601_sprs(CPUPPCState *env) { /* Exception processing */ spr_register_kvm(env, SPR_DSISR, "DSISR", @@ -877,7 +215,7 @@ static void gen_spr_ne_601(CPUPPCState *env) } /* Storage Description Register 1 */ -static void gen_spr_sdr1(CPUPPCState *env) +static void register_sdr1_sprs(CPUPPCState *env) { #ifndef CONFIG_USER_ONLY if (env->has_hv_mode) { @@ -900,7 +238,7 @@ static void gen_spr_sdr1(CPUPPCState *env) } /* BATs 0-3 */ -static void gen_low_BATs(CPUPPCState *env) +static void register_low_BATs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) spr_register(env, SPR_IBAT0U, "IBAT0U", @@ -972,7 +310,7 @@ static void gen_low_BATs(CPUPPCState *env) } /* BATs 4-7 */ -static void gen_high_BATs(CPUPPCState *env) +static void register_high_BATs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) spr_register(env, SPR_IBAT4U, "IBAT4U", @@ -1044,7 +382,7 @@ static void gen_high_BATs(CPUPPCState *env) } /* Generic PowerPC time base */ -static void gen_tbl(CPUPPCState *env) +static void register_tbl(CPUPPCState *env) { spr_register(env, SPR_VTBL, "TBL", &spr_read_tbl, SPR_NOACCESS, @@ -1065,7 +403,7 @@ static void gen_tbl(CPUPPCState *env) } /* Softare table search registers */ -static void gen_6xx_7xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) +static void register_6xx_7xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) { #if !defined(CONFIG_USER_ONLY) env->nb_tlb = nb_tlbs; @@ -1104,7 +442,7 @@ static void gen_6xx_7xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) } /* SPR common to MPC755 and G2 */ -static void gen_spr_G2_755(CPUPPCState *env) +static void register_G2_755_sprs(CPUPPCState *env) { /* SGPRs */ spr_register(env, SPR_SPRG4, "SPRG4", @@ -1126,7 +464,7 @@ static void gen_spr_G2_755(CPUPPCState *env) } /* SPR common to all 7xx PowerPC implementations */ -static void gen_spr_7xx(CPUPPCState *env) +static void register_7xx_sprs(CPUPPCState *env) { /* Breakpoints */ /* XXX : not implemented */ @@ -1225,106 +563,7 @@ static void gen_spr_7xx(CPUPPCState *env) } #ifdef TARGET_PPC64 -#ifndef CONFIG_USER_ONLY -static void spr_write_amr(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - - /* - * Note, the HV=1 PR=0 case is handled earlier by simply using - * spr_write_generic for HV mode in the SPR table - */ - - /* Build insertion mask into t1 based on context */ - if (ctx->pr) { - gen_load_spr(t1, SPR_UAMOR); - } else { - gen_load_spr(t1, SPR_AMOR); - } - - /* Mask new bits into t2 */ - tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); - - /* Load AMR and clear new bits in t0 */ - gen_load_spr(t0, SPR_AMR); - tcg_gen_andc_tl(t0, t0, t1); - - /* Or'in new bits and write it out */ - tcg_gen_or_tl(t0, t0, t2); - gen_store_spr(SPR_AMR, t0); - spr_store_dump_spr(SPR_AMR); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); -} - -static void spr_write_uamor(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - - /* - * Note, the HV=1 case is handled earlier by simply using - * spr_write_generic for HV mode in the SPR table - */ - - /* Build insertion mask into t1 based on context */ - gen_load_spr(t1, SPR_AMOR); - - /* Mask new bits into t2 */ - tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); - - /* Load AMR and clear new bits in t0 */ - gen_load_spr(t0, SPR_UAMOR); - tcg_gen_andc_tl(t0, t0, t1); - - /* Or'in new bits and write it out */ - tcg_gen_or_tl(t0, t0, t2); - gen_store_spr(SPR_UAMOR, t0); - spr_store_dump_spr(SPR_UAMOR); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); -} - -static void spr_write_iamr(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - - /* - * Note, the HV=1 case is handled earlier by simply using - * spr_write_generic for HV mode in the SPR table - */ - - /* Build insertion mask into t1 based on context */ - gen_load_spr(t1, SPR_AMOR); - - /* Mask new bits into t2 */ - tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); - - /* Load AMR and clear new bits in t0 */ - gen_load_spr(t0, SPR_IAMR); - tcg_gen_andc_tl(t0, t0, t1); - - /* Or'in new bits and write it out */ - tcg_gen_or_tl(t0, t0, t2); - gen_store_spr(SPR_IAMR, t0); - spr_store_dump_spr(SPR_IAMR); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); -} -#endif /* CONFIG_USER_ONLY */ - -static void gen_spr_amr(CPUPPCState *env) +static void register_amr_sprs(CPUPPCState *env) { #ifndef CONFIG_USER_ONLY /* @@ -1356,7 +595,7 @@ static void gen_spr_amr(CPUPPCState *env) #endif /* !CONFIG_USER_ONLY */ } -static void gen_spr_iamr(CPUPPCState *env) +static void register_iamr_sprs(CPUPPCState *env) { #ifndef CONFIG_USER_ONLY spr_register_kvm_hv(env, SPR_IAMR, "IAMR", @@ -1368,16 +607,7 @@ static void gen_spr_iamr(CPUPPCState *env) } #endif /* TARGET_PPC64 */ -#ifndef CONFIG_USER_ONLY -static void spr_read_thrm(DisasContext *ctx, int gprn, int sprn) -{ - gen_helper_fixup_thrm(cpu_env); - gen_load_spr(cpu_gpr[gprn], sprn); - spr_load_dump_spr(sprn); -} -#endif /* !CONFIG_USER_ONLY */ - -static void gen_spr_thrm(CPUPPCState *env) +static void register_thrm_sprs(CPUPPCState *env) { /* Thermal management */ /* XXX : not implemented */ @@ -1398,7 +628,7 @@ static void gen_spr_thrm(CPUPPCState *env) } /* SPR specific to PowerPC 604 implementation */ -static void gen_spr_604(CPUPPCState *env) +static void register_604_sprs(CPUPPCState *env) { /* Processor identification */ spr_register(env, SPR_PIR, "PIR", @@ -1451,7 +681,7 @@ static void gen_spr_604(CPUPPCState *env) } /* SPR specific to PowerPC 603 implementation */ -static void gen_spr_603(CPUPPCState *env) +static void register_603_sprs(CPUPPCState *env) { /* External access control */ /* XXX : not implemented */ @@ -1469,7 +699,7 @@ static void gen_spr_603(CPUPPCState *env) } /* SPR specific to PowerPC G2 implementation */ -static void gen_spr_G2(CPUPPCState *env) +static void register_G2_sprs(CPUPPCState *env) { /* Memory base address */ /* MBAR */ @@ -1521,7 +751,7 @@ static void gen_spr_G2(CPUPPCState *env) } /* SPR specific to PowerPC 602 implementation */ -static void gen_spr_602(CPUPPCState *env) +static void register_602_sprs(CPUPPCState *env) { /* ESA registers */ /* XXX : not implemented */ @@ -1569,7 +799,7 @@ static void gen_spr_602(CPUPPCState *env) } /* SPR specific to PowerPC 601 implementation */ -static void gen_spr_601(CPUPPCState *env) +static void register_601_sprs(CPUPPCState *env) { /* Multiplication/division register */ /* MQ */ @@ -1645,7 +875,7 @@ static void gen_spr_601(CPUPPCState *env) #endif } -static void gen_spr_74xx(CPUPPCState *env) +static void register_74xx_sprs(CPUPPCState *env) { /* Processor identification */ spr_register(env, SPR_PIR, "PIR", @@ -1695,7 +925,7 @@ static void gen_spr_74xx(CPUPPCState *env) 0x00000000); } -static void gen_l3_ctrl(CPUPPCState *env) +static void register_l3_ctrl(CPUPPCState *env) { /* L3CR */ /* XXX : not implemented */ @@ -1717,7 +947,7 @@ static void gen_l3_ctrl(CPUPPCState *env) 0x00000000); } -static void gen_74xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) +static void register_74xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) { #if !defined(CONFIG_USER_ONLY) env->nb_tlb = nb_tlbs; @@ -1742,58 +972,7 @@ static void gen_74xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) #endif } -#if !defined(CONFIG_USER_ONLY) -static void spr_write_e500_l1csr0(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - - tcg_gen_andi_tl(t0, cpu_gpr[gprn], L1CSR0_DCE | L1CSR0_CPE); - gen_store_spr(sprn, t0); - tcg_temp_free(t0); -} - -static void spr_write_e500_l1csr1(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - - tcg_gen_andi_tl(t0, cpu_gpr[gprn], L1CSR1_ICE | L1CSR1_CPE); - gen_store_spr(sprn, t0); - tcg_temp_free(t0); -} - -static void spr_write_e500_l2csr0(DisasContext *ctx, int sprn, int gprn) -{ - TCGv t0 = tcg_temp_new(); - - tcg_gen_andi_tl(t0, cpu_gpr[gprn], - ~(E500_L2CSR0_L2FI | E500_L2CSR0_L2FL | E500_L2CSR0_L2LFC)); - gen_store_spr(sprn, t0); - tcg_temp_free(t0); -} - -static void spr_write_booke206_mmucsr0(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_booke206_tlbflush(cpu_env, cpu_gpr[gprn]); -} - -static void spr_write_booke_pid(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32(sprn); - gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} -static void spr_write_eplc(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_booke_set_eplc(cpu_env, cpu_gpr[gprn]); -} -static void spr_write_epsc(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_booke_set_epsc(cpu_env, cpu_gpr[gprn]); -} - -#endif - -static void gen_spr_usprg3(CPUPPCState *env) +static void register_usprg3_sprs(CPUPPCState *env) { spr_register(env, SPR_USPRG3, "USPRG3", &spr_read_ureg, SPR_NOACCESS, @@ -1801,7 +980,7 @@ static void gen_spr_usprg3(CPUPPCState *env) 0x00000000); } -static void gen_spr_usprgh(CPUPPCState *env) +static void register_usprgh_sprs(CPUPPCState *env) { spr_register(env, SPR_USPRG4, "USPRG4", &spr_read_ureg, SPR_NOACCESS, @@ -1822,7 +1001,7 @@ static void gen_spr_usprgh(CPUPPCState *env) } /* PowerPC BookE SPR */ -static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask) +static void register_BookE_sprs(CPUPPCState *env, uint64_t ivor_mask) { const char *ivor_names[64] = { "IVOR0", "IVOR1", "IVOR2", "IVOR3", @@ -1998,7 +1177,8 @@ static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask) 0x00000000); } -static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize, +#if !defined(CONFIG_USER_ONLY) +static inline uint32_t register_tlbncfg(uint32_t assoc, uint32_t minsize, uint32_t maxsize, uint32_t flags, uint32_t nentries) { @@ -2007,9 +1187,10 @@ static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize, (maxsize << TLBnCFG_MAXSIZE_SHIFT) | flags | nentries; } +#endif /* !CONFIG_USER_ONLY */ /* BookE 2.06 storage control registers */ -static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask, +static void register_BookE206_sprs(CPUPPCState *env, uint32_t mas_mask, uint32_t *tlbncfg, uint32_t mmucfg) { #if !defined(CONFIG_USER_ONLY) @@ -2097,11 +1278,11 @@ static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask, } #endif - gen_spr_usprgh(env); + register_usprgh_sprs(env); } /* SPR specific to PowerPC 440 implementation */ -static void gen_spr_440(CPUPPCState *env) +static void register_440_sprs(CPUPPCState *env) { /* Cache control */ /* XXX : not implemented */ @@ -2242,7 +1423,7 @@ static void gen_spr_440(CPUPPCState *env) } /* SPR shared between PowerPC 40x implementations */ -static void gen_spr_40x(CPUPPCState *env) +static void register_40x_sprs(CPUPPCState *env) { /* Cache */ /* not emulated, as QEMU do not emulate caches */ @@ -2297,7 +1478,7 @@ static void gen_spr_40x(CPUPPCState *env) } /* SPR specific to PowerPC 405 implementation */ -static void gen_spr_405(CPUPPCState *env) +static void register_405_sprs(CPUPPCState *env) { /* MMU */ spr_register(env, SPR_40x_PID, "PID", @@ -2399,11 +1580,11 @@ static void gen_spr_405(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, spr_read_generic, &spr_write_generic, 0x00000000); - gen_spr_usprgh(env); + register_usprgh_sprs(env); } /* SPR shared between PowerPC 401 & 403 implementations */ -static void gen_spr_401_403(CPUPPCState *env) +static void register_401_403_sprs(CPUPPCState *env) { /* Time base */ spr_register(env, SPR_403_VTBL, "TBL", @@ -2431,7 +1612,7 @@ static void gen_spr_401_403(CPUPPCState *env) } /* SPR specific to PowerPC 401 implementation */ -static void gen_spr_401(CPUPPCState *env) +static void register_401_sprs(CPUPPCState *env) { /* Debug interface */ /* XXX : not implemented */ @@ -2473,9 +1654,9 @@ static void gen_spr_401(CPUPPCState *env) 0x00000000); } -static void gen_spr_401x2(CPUPPCState *env) +static void register_401x2_sprs(CPUPPCState *env) { - gen_spr_401(env); + register_401_sprs(env); spr_register(env, SPR_40x_PID, "PID", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, @@ -2487,7 +1668,7 @@ static void gen_spr_401x2(CPUPPCState *env) } /* SPR specific to PowerPC 403 implementation */ -static void gen_spr_403(CPUPPCState *env) +static void register_403_sprs(CPUPPCState *env) { /* Debug interface */ /* XXX : not implemented */ @@ -2523,7 +1704,7 @@ static void gen_spr_403(CPUPPCState *env) 0x00000000); } -static void gen_spr_403_real(CPUPPCState *env) +static void register_403_real_sprs(CPUPPCState *env) { spr_register(env, SPR_403_PBL1, "PBL1", SPR_NOACCESS, SPR_NOACCESS, @@ -2543,7 +1724,7 @@ static void gen_spr_403_real(CPUPPCState *env) 0x00000000); } -static void gen_spr_403_mmu(CPUPPCState *env) +static void register_403_mmu_sprs(CPUPPCState *env) { /* MMU */ spr_register(env, SPR_40x_PID, "PID", @@ -2557,7 +1738,7 @@ static void gen_spr_403_mmu(CPUPPCState *env) } /* SPR specific to PowerPC compression coprocessor extension */ -static void gen_spr_compress(CPUPPCState *env) +static void register_compress_sprs(CPUPPCState *env) { /* XXX : not implemented */ spr_register(env, SPR_401_SKR, "SKR", @@ -2566,7 +1747,7 @@ static void gen_spr_compress(CPUPPCState *env) 0x00000000); } -static void gen_spr_5xx_8xx(CPUPPCState *env) +static void register_5xx_8xx_sprs(CPUPPCState *env) { /* Exception processing */ spr_register_kvm(env, SPR_DSISR, "DSISR", @@ -2684,7 +1865,7 @@ static void gen_spr_5xx_8xx(CPUPPCState *env) 0x00000000); } -static void gen_spr_5xx(CPUPPCState *env) +static void register_5xx_sprs(CPUPPCState *env) { /* XXX : not implemented */ spr_register(env, SPR_RCPU_MI_GRA, "MI_GRA", @@ -2793,7 +1974,7 @@ static void gen_spr_5xx(CPUPPCState *env) 0x00000000); } -static void gen_spr_8xx(CPUPPCState *env) +static void register_8xx_sprs(CPUPPCState *env) { /* XXX : not implemented */ spr_register(env, SPR_MPC_IC_CST, "IC_CST", @@ -3528,9 +2709,9 @@ static bool ppc_cpu_interrupts_big_endian_lpcr(PowerPCCPU *cpu) static void init_proc_401(CPUPPCState *env) { - gen_spr_40x(env); - gen_spr_401_403(env); - gen_spr_401(env); + register_40x_sprs(env); + register_401_403_sprs(env); + register_401_sprs(env); init_excp_4xx_real(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -3574,10 +2755,10 @@ POWERPC_FAMILY(401)(ObjectClass *oc, void *data) static void init_proc_401x2(CPUPPCState *env) { - gen_spr_40x(env); - gen_spr_401_403(env); - gen_spr_401x2(env); - gen_spr_compress(env); + register_40x_sprs(env); + register_401_403_sprs(env); + register_401x2_sprs(env); + register_compress_sprs(env); /* Memory management */ #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; @@ -3632,11 +2813,11 @@ POWERPC_FAMILY(401x2)(ObjectClass *oc, void *data) static void init_proc_401x3(CPUPPCState *env) { - gen_spr_40x(env); - gen_spr_401_403(env); - gen_spr_401(env); - gen_spr_401x2(env); - gen_spr_compress(env); + register_40x_sprs(env); + register_401_403_sprs(env); + register_401_sprs(env); + register_401x2_sprs(env); + register_compress_sprs(env); init_excp_4xx_softmmu(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -3685,10 +2866,10 @@ POWERPC_FAMILY(401x3)(ObjectClass *oc, void *data) static void init_proc_IOP480(CPUPPCState *env) { - gen_spr_40x(env); - gen_spr_401_403(env); - gen_spr_401x2(env); - gen_spr_compress(env); + register_40x_sprs(env); + register_401_403_sprs(env); + register_401x2_sprs(env); + register_compress_sprs(env); /* Memory management */ #if !defined(CONFIG_USER_ONLY) env->nb_tlb = 64; @@ -3743,10 +2924,10 @@ POWERPC_FAMILY(IOP480)(ObjectClass *oc, void *data) static void init_proc_403(CPUPPCState *env) { - gen_spr_40x(env); - gen_spr_401_403(env); - gen_spr_403(env); - gen_spr_403_real(env); + register_40x_sprs(env); + register_401_403_sprs(env); + register_403_sprs(env); + register_403_real_sprs(env); init_excp_4xx_real(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -3790,11 +2971,11 @@ POWERPC_FAMILY(403)(ObjectClass *oc, void *data) static void init_proc_403GCX(CPUPPCState *env) { - gen_spr_40x(env); - gen_spr_401_403(env); - gen_spr_403(env); - gen_spr_403_real(env); - gen_spr_403_mmu(env); + register_40x_sprs(env); + register_401_403_sprs(env); + register_403_sprs(env); + register_403_real_sprs(env); + register_403_mmu_sprs(env); /* Bus access control */ /* not emulated, as QEMU never does speculative access */ spr_register(env, SPR_40x_SGR, "SGR", @@ -3858,9 +3039,9 @@ POWERPC_FAMILY(403GCX)(ObjectClass *oc, void *data) static void init_proc_405(CPUPPCState *env) { /* Time base */ - gen_tbl(env); - gen_spr_40x(env); - gen_spr_405(env); + register_tbl(env); + register_40x_sprs(env); + register_405_sprs(env); /* Bus access control */ /* not emulated, as QEMU never does speculative access */ spr_register(env, SPR_40x_SGR, "SGR", @@ -3924,10 +3105,10 @@ POWERPC_FAMILY(405)(ObjectClass *oc, void *data) static void init_proc_440EP(CPUPPCState *env) { /* Time base */ - gen_tbl(env); - gen_spr_BookE(env, 0x000000000000FFFFULL); - gen_spr_440(env); - gen_spr_usprgh(env); + register_tbl(env); + register_BookE_sprs(env, 0x000000000000FFFFULL); + register_440_sprs(env); + register_usprgh_sprs(env); /* Processor identification */ spr_register(env, SPR_BOOKE_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, @@ -4066,10 +3247,10 @@ POWERPC_FAMILY(460EX)(ObjectClass *oc, void *data) static void init_proc_440GP(CPUPPCState *env) { /* Time base */ - gen_tbl(env); - gen_spr_BookE(env, 0x000000000000FFFFULL); - gen_spr_440(env); - gen_spr_usprgh(env); + register_tbl(env); + register_BookE_sprs(env, 0x000000000000FFFFULL); + register_440_sprs(env); + register_usprgh_sprs(env); /* Processor identification */ spr_register(env, SPR_BOOKE_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, @@ -4149,10 +3330,10 @@ POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) static void init_proc_440x4(CPUPPCState *env) { /* Time base */ - gen_tbl(env); - gen_spr_BookE(env, 0x000000000000FFFFULL); - gen_spr_440(env); - gen_spr_usprgh(env); + register_tbl(env); + register_BookE_sprs(env, 0x000000000000FFFFULL); + register_440_sprs(env); + register_usprgh_sprs(env); /* Processor identification */ spr_register(env, SPR_BOOKE_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, @@ -4232,10 +3413,10 @@ POWERPC_FAMILY(440x4)(ObjectClass *oc, void *data) static void init_proc_440x5(CPUPPCState *env) { /* Time base */ - gen_tbl(env); - gen_spr_BookE(env, 0x000000000000FFFFULL); - gen_spr_440(env); - gen_spr_usprgh(env); + register_tbl(env); + register_BookE_sprs(env, 0x000000000000FFFFULL); + register_440_sprs(env); + register_usprgh_sprs(env); /* Processor identification */ spr_register(env, SPR_BOOKE_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, @@ -4371,9 +3552,9 @@ POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, void *data) static void init_proc_MPC5xx(CPUPPCState *env) { /* Time base */ - gen_tbl(env); - gen_spr_5xx_8xx(env); - gen_spr_5xx(env); + register_tbl(env); + register_5xx_8xx_sprs(env); + register_5xx_sprs(env); init_excp_MPC5xx(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -4415,9 +3596,9 @@ POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) static void init_proc_MPC8xx(CPUPPCState *env) { /* Time base */ - gen_tbl(env); - gen_spr_5xx_8xx(env); - gen_spr_8xx(env); + register_tbl(env); + register_5xx_8xx_sprs(env); + register_8xx_sprs(env); init_excp_MPC8xx(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -4459,12 +3640,12 @@ POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) static void init_proc_G2(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_G2_755(env); - gen_spr_G2(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_G2_755_sprs(env); + register_G2_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* External access control */ /* XXX : not implemented */ spr_register(env, SPR_EAR, "EAR", @@ -4488,9 +3669,9 @@ static void init_proc_G2(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_6xx_7xx_soft_tlb(env, 64, 2); + register_low_BATs(env); + register_high_BATs(env); + register_6xx_7xx_soft_tlb(env, 64, 2); init_excp_G2(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -4538,12 +3719,12 @@ POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) static void init_proc_G2LE(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_G2_755(env); - gen_spr_G2(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_G2_755_sprs(env); + register_G2_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* External access control */ /* XXX : not implemented */ spr_register(env, SPR_EAR, "EAR", @@ -4568,9 +3749,9 @@ static void init_proc_G2LE(CPUPPCState *env) 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_6xx_7xx_soft_tlb(env, 64, 2); + register_low_BATs(env); + register_high_BATs(env); + register_6xx_7xx_soft_tlb(env, 64, 2); init_excp_G2(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -4621,15 +3802,15 @@ POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) static void init_proc_e200(CPUPPCState *env) { /* Time base */ - gen_tbl(env); - gen_spr_BookE(env, 0x000000070000FFFFULL); + register_tbl(env); + register_BookE_sprs(env, 0x000000070000FFFFULL); /* XXX : not implemented */ spr_register(env, SPR_BOOKE_SPEFSCR, "SPEFSCR", &spr_read_spefscr, &spr_write_spefscr, &spr_read_spefscr, &spr_write_spefscr, 0x00000000); /* Memory management */ - gen_spr_BookE206(env, 0x0000005D, NULL, 0); + register_BookE206_sprs(env, 0x0000005D, NULL, 0); /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", SPR_NOACCESS, SPR_NOACCESS, @@ -4775,11 +3956,11 @@ POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) static void init_proc_e300(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_603(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_603_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -4823,9 +4004,9 @@ static void init_proc_e300(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_6xx_7xx_soft_tlb(env, 64, 2); + register_low_BATs(env); + register_high_BATs(env); + register_6xx_7xx_soft_tlb(env, 64, 2); init_excp_603(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -4873,31 +4054,6 @@ POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -#if !defined(CONFIG_USER_ONLY) -static void spr_write_mas73(DisasContext *ctx, int sprn, int gprn) -{ - TCGv val = tcg_temp_new(); - tcg_gen_ext32u_tl(val, cpu_gpr[gprn]); - gen_store_spr(SPR_BOOKE_MAS3, val); - tcg_gen_shri_tl(val, cpu_gpr[gprn], 32); - gen_store_spr(SPR_BOOKE_MAS7, val); - tcg_temp_free(val); -} - -static void spr_read_mas73(DisasContext *ctx, int gprn, int sprn) -{ - TCGv mas7 = tcg_temp_new(); - TCGv mas3 = tcg_temp_new(); - gen_load_spr(mas7, SPR_BOOKE_MAS7); - tcg_gen_shli_tl(mas7, mas7, 32); - gen_load_spr(mas3, SPR_BOOKE_MAS3); - tcg_gen_or_tl(cpu_gpr[gprn], mas3, mas7); - tcg_temp_free(mas3); - tcg_temp_free(mas7); -} - -#endif - enum fsl_e500_version { fsl_e500v1, fsl_e500v2, @@ -4921,11 +4077,11 @@ static void init_proc_e500(CPUPPCState *env, int version) #endif /* Time base */ - gen_tbl(env); + register_tbl(env); /* * XXX The e500 doesn't implement IVOR7 and IVOR9, but doesn't * complain when accessing them. - * gen_spr_BookE(env, 0x0000000F0000FD7FULL); + * register_BookE_sprs(env, 0x0000000F0000FD7FULL); */ switch (version) { case fsl_e500v1: @@ -4941,8 +4097,8 @@ static void init_proc_e500(CPUPPCState *env, int version) ivor_mask = 0x000003FF0000FFFFULL; break; } - gen_spr_BookE(env, ivor_mask); - gen_spr_usprg3(env); + register_BookE_sprs(env, ivor_mask); + register_usprg3_sprs(env); /* Processor identification */ spr_register(env, SPR_BOOKE_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, @@ -4960,17 +4116,17 @@ static void init_proc_e500(CPUPPCState *env, int version) env->id_tlbs = 0; switch (version) { case fsl_e500v1: - tlbncfg[0] = gen_tlbncfg(2, 1, 1, 0, 256); - tlbncfg[1] = gen_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); + tlbncfg[0] = register_tlbncfg(2, 1, 1, 0, 256); + tlbncfg[1] = register_tlbncfg(16, 1, 9, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); break; case fsl_e500v2: - tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, 512); - tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); + tlbncfg[0] = register_tlbncfg(4, 1, 1, 0, 512); + tlbncfg[1] = register_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16); break; case fsl_e500mc: case fsl_e5500: - tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, 512); - tlbncfg[1] = gen_tlbncfg(64, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 64); + tlbncfg[0] = register_tlbncfg(4, 1, 1, 0, 512); + tlbncfg[1] = register_tlbncfg(64, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 64); break; case fsl_e6500: mmucfg = 0x6510B45; @@ -5007,7 +4163,7 @@ static void init_proc_e500(CPUPPCState *env, int version) cpu_abort(env_cpu(env), "Unknown CPU: " TARGET_FMT_lx "\n", env->spr[SPR_PVR]); } - gen_spr_BookE206(env, 0x000000DF, tlbncfg, mmucfg); + register_BookE206_sprs(env, 0x000000DF, tlbncfg, mmucfg); /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", SPR_NOACCESS, SPR_NOACCESS, @@ -5365,9 +4521,9 @@ POWERPC_FAMILY(e6500)(ObjectClass *oc, void *data) static void init_proc_601(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_601(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_601_sprs(env); /* Hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -5481,11 +4637,11 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data) static void init_proc_602(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_602(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_602_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -5498,8 +4654,8 @@ static void init_proc_602(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_6xx_7xx_soft_tlb(env, 64, 2); + register_low_BATs(env); + register_6xx_7xx_soft_tlb(env, 64, 2); init_excp_602(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -5551,11 +4707,11 @@ POWERPC_FAMILY(602)(ObjectClass *oc, void *data) static void init_proc_603(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_603(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_603_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -5568,8 +4724,8 @@ static void init_proc_603(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_6xx_7xx_soft_tlb(env, 64, 2); + register_low_BATs(env); + register_6xx_7xx_soft_tlb(env, 64, 2); init_excp_603(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -5618,11 +4774,11 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data) static void init_proc_603E(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_603(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_603_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -5635,8 +4791,8 @@ static void init_proc_603E(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_6xx_7xx_soft_tlb(env, 64, 2); + register_low_BATs(env); + register_6xx_7xx_soft_tlb(env, 64, 2); init_excp_603(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -5685,11 +4841,11 @@ POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) static void init_proc_604(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_604(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_604_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -5697,7 +4853,7 @@ static void init_proc_604(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); init_excp_604(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -5749,9 +4905,9 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data) static void init_proc_604E(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_604(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_604_sprs(env); /* XXX : not implemented */ spr_register(env, SPR_7XX_MMCR1, "MMCR1", SPR_NOACCESS, SPR_NOACCESS, @@ -5768,7 +4924,7 @@ static void init_proc_604E(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -5781,7 +4937,7 @@ static void init_proc_604E(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); init_excp_604(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -5833,13 +4989,13 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) static void init_proc_740(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* Hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -5852,7 +5008,7 @@ static void init_proc_740(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); init_excp_7x0(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -5904,18 +5060,18 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data) static void init_proc_750(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* Hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -5928,7 +5084,7 @@ static void init_proc_750(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); /* * XXX: high BATs are also present but are known to be bugged on * die version 1.x @@ -5984,16 +5140,16 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data) static void init_proc_750cl(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Thermal management */ /* Those registers are fake on 750CL */ spr_register(env, SPR_THRM1, "THRM1", @@ -6094,9 +5250,9 @@ static void init_proc_750cl(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); /* PowerPC 750cl has 8 DBATs and 8 IBATs */ - gen_high_BATs(env); + register_high_BATs(env); init_excp_750cl(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6187,18 +5343,18 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) static void init_proc_750cx(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* This register is not implemented but is present for compatibility */ spr_register(env, SPR_SDA, "SDA", SPR_NOACCESS, SPR_NOACCESS, @@ -6216,9 +5372,9 @@ static void init_proc_750cx(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); /* PowerPC 750cx has 8 DBATs and 8 IBATs */ - gen_high_BATs(env); + register_high_BATs(env); init_excp_750cx(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6270,18 +5426,18 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) static void init_proc_750fx(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* XXX : not implemented */ spr_register(env, SPR_750_THRM4, "THRM4", SPR_NOACCESS, SPR_NOACCESS, @@ -6304,9 +5460,9 @@ static void init_proc_750fx(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */ - gen_high_BATs(env); + register_high_BATs(env); init_excp_7x0(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6358,18 +5514,18 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) static void init_proc_750gx(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* XXX : not implemented (XXX: different from 750fx) */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* XXX : not implemented */ spr_register(env, SPR_750_THRM4, "THRM4", SPR_NOACCESS, SPR_NOACCESS, @@ -6392,9 +5548,9 @@ static void init_proc_750gx(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */ - gen_high_BATs(env); + register_high_BATs(env); init_excp_7x0(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6446,14 +5602,14 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) static void init_proc_745(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); - gen_spr_G2_755(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); + register_G2_755_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* Hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -6471,9 +5627,9 @@ static void init_proc_745(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_6xx_7xx_soft_tlb(env, 64, 2); + register_low_BATs(env); + register_high_BATs(env); + register_6xx_7xx_soft_tlb(env, 64, 2); init_excp_7x5(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6522,12 +5678,12 @@ POWERPC_FAMILY(745)(ObjectClass *oc, void *data) static void init_proc_755(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); - gen_spr_G2_755(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); + register_G2_755_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* L2 cache control */ /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", @@ -6540,7 +5696,7 @@ static void init_proc_755(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* Hardware implementation registers */ /* XXX : not implemented */ spr_register(env, SPR_HID0, "HID0", @@ -6558,9 +5714,9 @@ static void init_proc_755(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_6xx_7xx_soft_tlb(env, 64, 2); + register_low_BATs(env); + register_high_BATs(env); + register_6xx_7xx_soft_tlb(env, 64, 2); init_excp_7x5(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6609,13 +5765,13 @@ POWERPC_FAMILY(755)(ObjectClass *oc, void *data) static void init_proc_7400(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* 74xx specific SPR */ - gen_spr_74xx(env); + register_74xx_sprs(env); vscr_init(env, 0x00010000); /* XXX : not implemented */ spr_register(env, SPR_UBAMR, "UBAMR", @@ -6629,9 +5785,9 @@ static void init_proc_7400(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); init_excp_7400(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6689,13 +5845,13 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) static void init_proc_7410(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* 74xx specific SPR */ - gen_spr_74xx(env); + register_74xx_sprs(env); vscr_init(env, 0x00010000); /* XXX : not implemented */ spr_register(env, SPR_UBAMR, "UBAMR", @@ -6703,7 +5859,7 @@ static void init_proc_7410(CPUPPCState *env) &spr_read_ureg, SPR_NOACCESS, 0x00000000); /* Thermal management */ - gen_spr_thrm(env); + register_thrm_sprs(env); /* L2PMCR */ /* XXX : not implemented */ spr_register(env, SPR_L2PMCR, "L2PMCR", @@ -6717,7 +5873,7 @@ static void init_proc_7410(CPUPPCState *env) &spr_read_generic, &spr_write_generic, 0x00000000); /* Memory management */ - gen_low_BATs(env); + register_low_BATs(env); init_excp_7400(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6775,13 +5931,13 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) static void init_proc_7440(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* 74xx specific SPR */ - gen_spr_74xx(env); + register_74xx_sprs(env); vscr_init(env, 0x00010000); /* XXX : not implemented */ spr_register(env, SPR_UBAMR, "UBAMR", @@ -6828,8 +5984,8 @@ static void init_proc_7440(CPUPPCState *env) &spr_read_ureg, SPR_NOACCESS, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_74xx_soft_tlb(env, 128, 2); + register_low_BATs(env); + register_74xx_soft_tlb(env, 128, 2); init_excp_7450(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -6884,16 +6040,16 @@ POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) static void init_proc_7450(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* 74xx specific SPR */ - gen_spr_74xx(env); + register_74xx_sprs(env); vscr_init(env, 0x00010000); /* Level 3 cache control */ - gen_l3_ctrl(env); + register_l3_ctrl(env); /* L3ITCR1 */ /* XXX : not implemented */ spr_register(env, SPR_L3ITCR1, "L3ITCR1", @@ -6963,8 +6119,8 @@ static void init_proc_7450(CPUPPCState *env) &spr_read_ureg, SPR_NOACCESS, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_74xx_soft_tlb(env, 128, 2); + register_low_BATs(env); + register_74xx_soft_tlb(env, 128, 2); init_excp_7450(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -7019,13 +6175,13 @@ POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) static void init_proc_7445(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* 74xx specific SPR */ - gen_spr_74xx(env); + register_74xx_sprs(env); vscr_init(env, 0x00010000); /* LDSTCR */ /* XXX : not implemented */ @@ -7100,9 +6256,9 @@ static void init_proc_7445(CPUPPCState *env) &spr_read_ureg, SPR_NOACCESS, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_74xx_soft_tlb(env, 128, 2); + register_low_BATs(env); + register_high_BATs(env); + register_74xx_soft_tlb(env, 128, 2); init_excp_7450(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -7157,16 +6313,16 @@ POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) static void init_proc_7455(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* 74xx specific SPR */ - gen_spr_74xx(env); + register_74xx_sprs(env); vscr_init(env, 0x00010000); /* Level 3 cache control */ - gen_l3_ctrl(env); + register_l3_ctrl(env); /* LDSTCR */ /* XXX : not implemented */ spr_register(env, SPR_LDSTCR, "LDSTCR", @@ -7240,9 +6396,9 @@ static void init_proc_7455(CPUPPCState *env) &spr_read_ureg, SPR_NOACCESS, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_74xx_soft_tlb(env, 128, 2); + register_low_BATs(env); + register_high_BATs(env); + register_74xx_soft_tlb(env, 128, 2); init_excp_7450(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -7297,16 +6453,16 @@ POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) static void init_proc_7457(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* 74xx specific SPR */ - gen_spr_74xx(env); + register_74xx_sprs(env); vscr_init(env, 0x00010000); /* Level 3 cache control */ - gen_l3_ctrl(env); + register_l3_ctrl(env); /* L3ITCR1 */ /* XXX : not implemented */ spr_register(env, SPR_L3ITCR1, "L3ITCR1", @@ -7404,9 +6560,9 @@ static void init_proc_7457(CPUPPCState *env) &spr_read_ureg, SPR_NOACCESS, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_74xx_soft_tlb(env, 128, 2); + register_low_BATs(env); + register_high_BATs(env); + register_74xx_soft_tlb(env, 128, 2); init_excp_7450(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -7461,13 +6617,13 @@ POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) static void init_proc_e600(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_spr_sdr1(env); - gen_spr_7xx(env); + register_ne_601_sprs(env); + register_sdr1_sprs(env); + register_7xx_sprs(env); /* Time base */ - gen_tbl(env); + register_tbl(env); /* 74xx specific SPR */ - gen_spr_74xx(env); + register_74xx_sprs(env); vscr_init(env, 0x00010000); /* XXX : not implemented */ spr_register(env, SPR_UBAMR, "UBAMR", @@ -7543,9 +6699,9 @@ static void init_proc_e600(CPUPPCState *env) &spr_read_ureg, SPR_NOACCESS, 0x00000000); /* Memory management */ - gen_low_BATs(env); - gen_high_BATs(env); - gen_74xx_soft_tlb(env, 128, 2); + register_low_BATs(env); + register_high_BATs(env); + register_74xx_soft_tlb(env, 128, 2); init_excp_7450(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -7609,58 +6765,6 @@ POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) #define POWERPC970_HID5_INIT 0x00000000 #endif -static void gen_fscr_facility_check(DisasContext *ctx, int facility_sprn, - int bit, int sprn, int cause) -{ - TCGv_i32 t1 = tcg_const_i32(bit); - TCGv_i32 t2 = tcg_const_i32(sprn); - TCGv_i32 t3 = tcg_const_i32(cause); - - gen_helper_fscr_facility_check(cpu_env, t1, t2, t3); - - tcg_temp_free_i32(t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t1); -} - -static void gen_msr_facility_check(DisasContext *ctx, int facility_sprn, - int bit, int sprn, int cause) -{ - TCGv_i32 t1 = tcg_const_i32(bit); - TCGv_i32 t2 = tcg_const_i32(sprn); - TCGv_i32 t3 = tcg_const_i32(cause); - - gen_helper_msr_facility_check(cpu_env, t1, t2, t3); - - tcg_temp_free_i32(t3); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t1); -} - -static void spr_read_prev_upper32(DisasContext *ctx, int gprn, int sprn) -{ - TCGv spr_up = tcg_temp_new(); - TCGv spr = tcg_temp_new(); - - gen_load_spr(spr, sprn - 1); - tcg_gen_shri_tl(spr_up, spr, 32); - tcg_gen_ext32u_tl(cpu_gpr[gprn], spr_up); - - tcg_temp_free(spr); - tcg_temp_free(spr_up); -} - -static void spr_write_prev_upper32(DisasContext *ctx, int sprn, int gprn) -{ - TCGv spr = tcg_temp_new(); - - gen_load_spr(spr, sprn - 1); - tcg_gen_deposit_tl(spr, spr, cpu_gpr[gprn], 32, 32); - gen_store_spr(sprn - 1, spr); - - tcg_temp_free(spr); -} - static int check_pow_970(CPUPPCState *env) { if (env->spr[SPR_HID0] & (HID0_DEEPNAP | HID0_DOZE | HID0_NAP)) { @@ -7670,7 +6774,7 @@ static int check_pow_970(CPUPPCState *env) return 0; } -static void gen_spr_970_hid(CPUPPCState *env) +static void register_970_hid_sprs(CPUPPCState *env) { /* Hardware implementation registers */ /* XXX : not implemented */ @@ -7688,7 +6792,7 @@ static void gen_spr_970_hid(CPUPPCState *env) POWERPC970_HID5_INIT); } -static void gen_spr_970_hior(CPUPPCState *env) +static void register_970_hior_sprs(CPUPPCState *env) { spr_register(env, SPR_HIOR, "SPR_HIOR", SPR_NOACCESS, SPR_NOACCESS, @@ -7696,7 +6800,7 @@ static void gen_spr_970_hior(CPUPPCState *env) 0x00000000); } -static void gen_spr_book3s_ctrl(CPUPPCState *env) +static void register_book3s_ctrl_sprs(CPUPPCState *env) { spr_register(env, SPR_CTRL, "SPR_CTRL", SPR_NOACCESS, SPR_NOACCESS, @@ -7708,7 +6812,7 @@ static void gen_spr_book3s_ctrl(CPUPPCState *env) 0x00000000); } -static void gen_spr_book3s_altivec(CPUPPCState *env) +static void register_book3s_altivec_sprs(CPUPPCState *env) { if (!(env->insns_flags & PPC_ALTIVEC)) { return; @@ -7721,7 +6825,7 @@ static void gen_spr_book3s_altivec(CPUPPCState *env) } -static void gen_spr_book3s_dbg(CPUPPCState *env) +static void register_book3s_dbg_sprs(CPUPPCState *env) { /* * TODO: different specs define different scopes for these, @@ -7740,7 +6844,7 @@ static void gen_spr_book3s_dbg(CPUPPCState *env) KVM_REG_PPC_DABRX, 0x00000000); } -static void gen_spr_book3s_207_dbg(CPUPPCState *env) +static void register_book3s_207_dbg_sprs(CPUPPCState *env) { spr_register_kvm_hv(env, SPR_DAWR0, "DAWR0", SPR_NOACCESS, SPR_NOACCESS, @@ -7759,7 +6863,7 @@ static void gen_spr_book3s_207_dbg(CPUPPCState *env) KVM_REG_PPC_CIABR, 0x00000000); } -static void gen_spr_970_dbg(CPUPPCState *env) +static void register_970_dbg_sprs(CPUPPCState *env) { /* Breakpoints */ spr_register(env, SPR_IABR, "IABR", @@ -7768,7 +6872,7 @@ static void gen_spr_970_dbg(CPUPPCState *env) 0x00000000); } -static void gen_spr_book3s_pmu_sup(CPUPPCState *env) +static void register_book3s_pmu_sup_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_POWER_MMCR0, "MMCR0", SPR_NOACCESS, SPR_NOACCESS, @@ -7816,7 +6920,7 @@ static void gen_spr_book3s_pmu_sup(CPUPPCState *env) KVM_REG_PPC_SDAR, 0x00000000); } -static void gen_spr_book3s_pmu_user(CPUPPCState *env) +static void register_book3s_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_POWER_UMMCR0, "UMMCR0", &spr_read_ureg, SPR_NOACCESS, @@ -7864,7 +6968,7 @@ static void gen_spr_book3s_pmu_user(CPUPPCState *env) 0x00000000); } -static void gen_spr_970_pmu_sup(CPUPPCState *env) +static void register_970_pmu_sup_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_970_PMC7, "PMC7", SPR_NOACCESS, SPR_NOACCESS, @@ -7876,7 +6980,7 @@ static void gen_spr_970_pmu_sup(CPUPPCState *env) KVM_REG_PPC_PMC8, 0x00000000); } -static void gen_spr_970_pmu_user(CPUPPCState *env) +static void register_970_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_970_UPMC7, "UPMC7", &spr_read_ureg, SPR_NOACCESS, @@ -7888,7 +6992,7 @@ static void gen_spr_970_pmu_user(CPUPPCState *env) 0x00000000); } -static void gen_spr_power8_pmu_sup(CPUPPCState *env) +static void register_power8_pmu_sup_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_POWER_MMCR2, "MMCR2", SPR_NOACCESS, SPR_NOACCESS, @@ -7924,7 +7028,7 @@ static void gen_spr_power8_pmu_sup(CPUPPCState *env) KVM_REG_PPC_CSIGR, 0x00000000); } -static void gen_spr_power8_pmu_user(CPUPPCState *env) +static void register_power8_pmu_user_sprs(CPUPPCState *env) { spr_register(env, SPR_POWER_UMMCR2, "UMMCR2", &spr_read_ureg, SPR_NOACCESS, @@ -7936,7 +7040,7 @@ static void gen_spr_power8_pmu_user(CPUPPCState *env) 0x00000000); } -static void gen_spr_power5p_ear(CPUPPCState *env) +static void register_power5p_ear_sprs(CPUPPCState *env) { /* External access control */ spr_register(env, SPR_EAR, "EAR", @@ -7945,7 +7049,7 @@ static void gen_spr_power5p_ear(CPUPPCState *env) 0x00000000); } -static void gen_spr_power5p_tb(CPUPPCState *env) +static void register_power5p_tb_sprs(CPUPPCState *env) { /* TBU40 (High 40 bits of the Timebase register */ spr_register_hv(env, SPR_TBU40, "TBU40", @@ -7955,25 +7059,7 @@ static void gen_spr_power5p_tb(CPUPPCState *env) 0x00000000); } -#if !defined(CONFIG_USER_ONLY) -static void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) -{ - TCGv hmer = tcg_temp_new(); - - gen_load_spr(hmer, sprn); - tcg_gen_and_tl(hmer, cpu_gpr[gprn], hmer); - gen_store_spr(sprn, hmer); - spr_store_dump_spr(sprn); - tcg_temp_free(hmer); -} - -static void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); -} -#endif /* !defined(CONFIG_USER_ONLY) */ - -static void gen_spr_970_lpar(CPUPPCState *env) +static void register_970_lpar_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* @@ -7990,7 +7076,7 @@ static void gen_spr_970_lpar(CPUPPCState *env) #endif } -static void gen_spr_power5p_lpar(CPUPPCState *env) +static void register_power5p_lpar_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* Logical partitionning */ @@ -8006,7 +7092,7 @@ static void gen_spr_power5p_lpar(CPUPPCState *env) #endif } -static void gen_spr_book3s_ids(CPUPPCState *env) +static void register_book3s_ids_sprs(CPUPPCState *env) { /* FIXME: Will need to deal with thread vs core only SPRs */ @@ -8098,7 +7184,7 @@ static void gen_spr_book3s_ids(CPUPPCState *env) 0x00000000); } -static void gen_spr_rmor(CPUPPCState *env) +static void register_rmor_sprs(CPUPPCState *env) { spr_register_hv(env, SPR_RMOR, "RMOR", SPR_NOACCESS, SPR_NOACCESS, @@ -8107,7 +7193,7 @@ static void gen_spr_rmor(CPUPPCState *env) 0x00000000); } -static void gen_spr_power8_ids(CPUPPCState *env) +static void register_power8_ids_sprs(CPUPPCState *env) { /* Thread identification */ spr_register(env, SPR_TIR, "TIR", @@ -8116,7 +7202,7 @@ static void gen_spr_power8_ids(CPUPPCState *env) 0x00000000); } -static void gen_spr_book3s_purr(CPUPPCState *env) +static void register_book3s_purr_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* PURR & SPURR: Hack - treat these as aliases for the TB for now */ @@ -8133,7 +7219,7 @@ static void gen_spr_book3s_purr(CPUPPCState *env) #endif } -static void gen_spr_power6_dbg(CPUPPCState *env) +static void register_power6_dbg_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) spr_register(env, SPR_CFAR, "SPR_CFAR", @@ -8143,7 +7229,7 @@ static void gen_spr_power6_dbg(CPUPPCState *env) #endif } -static void gen_spr_power5p_common(CPUPPCState *env) +static void register_power5p_common_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_PPR, "PPR", &spr_read_generic, &spr_write_generic, @@ -8151,7 +7237,7 @@ static void gen_spr_power5p_common(CPUPPCState *env) KVM_REG_PPC_PPR, 0x00000000); } -static void gen_spr_power6_common(CPUPPCState *env) +static void register_power6_common_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) spr_register_kvm(env, SPR_DSCR, "SPR_DSCR", @@ -8170,19 +7256,7 @@ static void gen_spr_power6_common(CPUPPCState *env) 0x00000000); } -static void spr_read_tar(DisasContext *ctx, int gprn, int sprn) -{ - gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); - spr_read_generic(ctx, gprn, sprn); -} - -static void spr_write_tar(DisasContext *ctx, int sprn, int gprn) -{ - gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); - spr_write_generic(ctx, sprn, gprn); -} - -static void gen_spr_power8_tce_address_control(CPUPPCState *env) +static void register_power8_tce_address_control_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_TAR, "TAR", &spr_read_tar, &spr_write_tar, @@ -8190,31 +7264,7 @@ static void gen_spr_power8_tce_address_control(CPUPPCState *env) KVM_REG_PPC_TAR, 0x00000000); } -static void spr_read_tm(DisasContext *ctx, int gprn, int sprn) -{ - gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); - spr_read_generic(ctx, gprn, sprn); -} - -static void spr_write_tm(DisasContext *ctx, int sprn, int gprn) -{ - gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); - spr_write_generic(ctx, sprn, gprn); -} - -static void spr_read_tm_upper32(DisasContext *ctx, int gprn, int sprn) -{ - gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); - spr_read_prev_upper32(ctx, gprn, sprn); -} - -static void spr_write_tm_upper32(DisasContext *ctx, int sprn, int gprn) -{ - gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); - spr_write_prev_upper32(ctx, sprn, gprn); -} - -static void gen_spr_power8_tm(CPUPPCState *env) +static void register_power8_tm_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_TFHAR, "TFHAR", &spr_read_tm, &spr_write_tm, @@ -8234,31 +7284,7 @@ static void gen_spr_power8_tm(CPUPPCState *env) 0x00000000); } -static void spr_read_ebb(DisasContext *ctx, int gprn, int sprn) -{ - gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); - spr_read_generic(ctx, gprn, sprn); -} - -static void spr_write_ebb(DisasContext *ctx, int sprn, int gprn) -{ - gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); - spr_write_generic(ctx, sprn, gprn); -} - -static void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn) -{ - gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); - spr_read_prev_upper32(ctx, gprn, sprn); -} - -static void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn) -{ - gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); - spr_write_prev_upper32(ctx, sprn, gprn); -} - -static void gen_spr_power8_ebb(CPUPPCState *env) +static void register_power8_ebb_sprs(CPUPPCState *env) { spr_register(env, SPR_BESCRS, "BESCRS", &spr_read_ebb, &spr_write_ebb, @@ -8291,7 +7317,7 @@ static void gen_spr_power8_ebb(CPUPPCState *env) } /* Virtual Time Base */ -static void gen_spr_vtb(CPUPPCState *env) +static void register_vtb_sprs(CPUPPCState *env) { spr_register_kvm_hv(env, SPR_VTB, "VTB", SPR_NOACCESS, SPR_NOACCESS, @@ -8300,7 +7326,7 @@ static void gen_spr_vtb(CPUPPCState *env) KVM_REG_PPC_VTB, 0x00000000); } -static void gen_spr_power8_fscr(CPUPPCState *env) +static void register_power8_fscr_sprs(CPUPPCState *env) { #if defined(CONFIG_USER_ONLY) target_ulong initval = 1ULL << FSCR_TAR; @@ -8313,7 +7339,7 @@ static void gen_spr_power8_fscr(CPUPPCState *env) KVM_REG_PPC_FSCR, initval); } -static void gen_spr_power8_pspb(CPUPPCState *env) +static void register_power8_pspb_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_PSPB, "PSPB", SPR_NOACCESS, SPR_NOACCESS, @@ -8321,7 +7347,7 @@ static void gen_spr_power8_pspb(CPUPPCState *env) KVM_REG_PPC_PSPB, 0); } -static void gen_spr_power8_dpdes(CPUPPCState *env) +static void register_power8_dpdes_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* Directed Privileged Door-bell Exception State, used for IPI */ @@ -8333,7 +7359,7 @@ static void gen_spr_power8_dpdes(CPUPPCState *env) #endif } -static void gen_spr_power8_ic(CPUPPCState *env) +static void register_power8_ic_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) spr_register_hv(env, SPR_IC, "IC", @@ -8344,7 +7370,7 @@ static void gen_spr_power8_ic(CPUPPCState *env) #endif } -static void gen_spr_power8_book4(CPUPPCState *env) +static void register_power8_book4_sprs(CPUPPCState *env) { /* Add a number of P8 book4 registers */ #if !defined(CONFIG_USER_ONLY) @@ -8363,7 +7389,7 @@ static void gen_spr_power8_book4(CPUPPCState *env) #endif } -static void gen_spr_power7_book4(CPUPPCState *env) +static void register_power7_book4_sprs(CPUPPCState *env) { /* Add a number of P7 book4 registers */ #if !defined(CONFIG_USER_ONLY) @@ -8378,7 +7404,7 @@ static void gen_spr_power7_book4(CPUPPCState *env) #endif } -static void gen_spr_power8_rpr(CPUPPCState *env) +static void register_power8_rpr_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) spr_register_hv(env, SPR_RPR, "RPR", @@ -8389,7 +7415,7 @@ static void gen_spr_power8_rpr(CPUPPCState *env) #endif } -static void gen_spr_power9_mmu(CPUPPCState *env) +static void register_power9_mmu_sprs(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* Partition Table Control */ @@ -8409,13 +7435,13 @@ static void gen_spr_power9_mmu(CPUPPCState *env) static void init_proc_book3s_common(CPUPPCState *env) { - gen_spr_ne_601(env); - gen_tbl(env); - gen_spr_usprg3(env); - gen_spr_book3s_altivec(env); - gen_spr_book3s_pmu_sup(env); - gen_spr_book3s_pmu_user(env); - gen_spr_book3s_ctrl(env); + register_ne_601_sprs(env); + register_tbl(env); + register_usprg3_sprs(env); + register_book3s_altivec_sprs(env); + register_book3s_pmu_sup_sprs(env); + register_book3s_pmu_user_sprs(env); + register_book3s_ctrl_sprs(env); /* * Can't find information on what this should be on reset. This * value is the one used by 74xx processors. @@ -8427,17 +7453,17 @@ static void init_proc_970(CPUPPCState *env) { /* Common Registers */ init_proc_book3s_common(env); - gen_spr_sdr1(env); - gen_spr_book3s_dbg(env); + register_sdr1_sprs(env); + register_book3s_dbg_sprs(env); /* 970 Specific Registers */ - gen_spr_970_hid(env); - gen_spr_970_hior(env); - gen_low_BATs(env); - gen_spr_970_pmu_sup(env); - gen_spr_970_pmu_user(env); - gen_spr_970_lpar(env); - gen_spr_970_dbg(env); + register_970_hid_sprs(env); + register_970_hior_sprs(env); + register_low_BATs(env); + register_970_pmu_sup_sprs(env); + register_970_pmu_user_sprs(env); + register_970_lpar_sprs(env); + register_970_dbg_sprs(env); /* env variables */ env->dcache_line_size = 128; @@ -8500,19 +7526,19 @@ static void init_proc_power5plus(CPUPPCState *env) { /* Common Registers */ init_proc_book3s_common(env); - gen_spr_sdr1(env); - gen_spr_book3s_dbg(env); + register_sdr1_sprs(env); + register_book3s_dbg_sprs(env); /* POWER5+ Specific Registers */ - gen_spr_970_hid(env); - gen_spr_970_hior(env); - gen_low_BATs(env); - gen_spr_970_pmu_sup(env); - gen_spr_970_pmu_user(env); - gen_spr_power5p_common(env); - gen_spr_power5p_lpar(env); - gen_spr_power5p_ear(env); - gen_spr_power5p_tb(env); + register_970_hid_sprs(env); + register_970_hior_sprs(env); + register_low_BATs(env); + register_970_pmu_sup_sprs(env); + register_970_pmu_user_sprs(env); + register_power5p_common_sprs(env); + register_power5p_lpar_sprs(env); + register_power5p_ear_sprs(env); + register_power5p_tb_sprs(env); /* env variables */ env->dcache_line_size = 128; @@ -8579,21 +7605,21 @@ static void init_proc_POWER7(CPUPPCState *env) { /* Common Registers */ init_proc_book3s_common(env); - gen_spr_sdr1(env); - gen_spr_book3s_dbg(env); + register_sdr1_sprs(env); + register_book3s_dbg_sprs(env); /* POWER7 Specific Registers */ - gen_spr_book3s_ids(env); - gen_spr_rmor(env); - gen_spr_amr(env); - gen_spr_book3s_purr(env); - gen_spr_power5p_common(env); - gen_spr_power5p_lpar(env); - gen_spr_power5p_ear(env); - gen_spr_power5p_tb(env); - gen_spr_power6_common(env); - gen_spr_power6_dbg(env); - gen_spr_power7_book4(env); + register_book3s_ids_sprs(env); + register_rmor_sprs(env); + register_amr_sprs(env); + register_book3s_purr_sprs(env); + register_power5p_common_sprs(env); + register_power5p_lpar_sprs(env); + register_power5p_ear_sprs(env); + register_power5p_tb_sprs(env); + register_power6_common_sprs(env); + register_power6_dbg_sprs(env); + register_power7_book4_sprs(env); /* env variables */ env->dcache_line_size = 128; @@ -8725,34 +7751,34 @@ static void init_proc_POWER8(CPUPPCState *env) { /* Common Registers */ init_proc_book3s_common(env); - gen_spr_sdr1(env); - gen_spr_book3s_207_dbg(env); + register_sdr1_sprs(env); + register_book3s_207_dbg_sprs(env); /* POWER8 Specific Registers */ - gen_spr_book3s_ids(env); - gen_spr_rmor(env); - gen_spr_amr(env); - gen_spr_iamr(env); - gen_spr_book3s_purr(env); - gen_spr_power5p_common(env); - gen_spr_power5p_lpar(env); - gen_spr_power5p_ear(env); - gen_spr_power5p_tb(env); - gen_spr_power6_common(env); - gen_spr_power6_dbg(env); - gen_spr_power8_tce_address_control(env); - gen_spr_power8_ids(env); - gen_spr_power8_ebb(env); - gen_spr_power8_fscr(env); - gen_spr_power8_pmu_sup(env); - gen_spr_power8_pmu_user(env); - gen_spr_power8_tm(env); - gen_spr_power8_pspb(env); - gen_spr_power8_dpdes(env); - gen_spr_vtb(env); - gen_spr_power8_ic(env); - gen_spr_power8_book4(env); - gen_spr_power8_rpr(env); + register_book3s_ids_sprs(env); + register_rmor_sprs(env); + register_amr_sprs(env); + register_iamr_sprs(env); + register_book3s_purr_sprs(env); + register_power5p_common_sprs(env); + register_power5p_lpar_sprs(env); + register_power5p_ear_sprs(env); + register_power5p_tb_sprs(env); + register_power6_common_sprs(env); + register_power6_dbg_sprs(env); + register_power8_tce_address_control_sprs(env); + register_power8_ids_sprs(env); + register_power8_ebb_sprs(env); + register_power8_fscr_sprs(env); + register_power8_pmu_sup_sprs(env); + register_power8_pmu_user_sprs(env); + register_power8_tm_sprs(env); + register_power8_pspb_sprs(env); + register_power8_dpdes_sprs(env); + register_vtb_sprs(env); + register_power8_ic_sprs(env); + register_power8_book4_sprs(env); + register_power8_rpr_sprs(env); /* env variables */ env->dcache_line_size = 128; @@ -8922,33 +7948,33 @@ static void init_proc_POWER9(CPUPPCState *env) { /* Common Registers */ init_proc_book3s_common(env); - gen_spr_book3s_207_dbg(env); + register_book3s_207_dbg_sprs(env); /* POWER8 Specific Registers */ - gen_spr_book3s_ids(env); - gen_spr_amr(env); - gen_spr_iamr(env); - gen_spr_book3s_purr(env); - gen_spr_power5p_common(env); - gen_spr_power5p_lpar(env); - gen_spr_power5p_ear(env); - gen_spr_power5p_tb(env); - gen_spr_power6_common(env); - gen_spr_power6_dbg(env); - gen_spr_power8_tce_address_control(env); - gen_spr_power8_ids(env); - gen_spr_power8_ebb(env); - gen_spr_power8_fscr(env); - gen_spr_power8_pmu_sup(env); - gen_spr_power8_pmu_user(env); - gen_spr_power8_tm(env); - gen_spr_power8_pspb(env); - gen_spr_power8_dpdes(env); - gen_spr_vtb(env); - gen_spr_power8_ic(env); - gen_spr_power8_book4(env); - gen_spr_power8_rpr(env); - gen_spr_power9_mmu(env); + register_book3s_ids_sprs(env); + register_amr_sprs(env); + register_iamr_sprs(env); + register_book3s_purr_sprs(env); + register_power5p_common_sprs(env); + register_power5p_lpar_sprs(env); + register_power5p_ear_sprs(env); + register_power5p_tb_sprs(env); + register_power6_common_sprs(env); + register_power6_dbg_sprs(env); + register_power8_tce_address_control_sprs(env); + register_power8_ids_sprs(env); + register_power8_ebb_sprs(env); + register_power8_fscr_sprs(env); + register_power8_pmu_sup_sprs(env); + register_power8_pmu_user_sprs(env); + register_power8_tm_sprs(env); + register_power8_pspb_sprs(env); + register_power8_dpdes_sprs(env); + register_vtb_sprs(env); + register_power8_ic_sprs(env); + register_power8_book4_sprs(env); + register_power8_rpr_sprs(env); + register_power9_mmu_sprs(env); /* POWER9 Specific registers */ spr_register_kvm(env, SPR_TIDR, "TIDR", NULL, NULL, @@ -9140,31 +8166,31 @@ static void init_proc_POWER10(CPUPPCState *env) { /* Common Registers */ init_proc_book3s_common(env); - gen_spr_book3s_207_dbg(env); + register_book3s_207_dbg_sprs(env); /* POWER8 Specific Registers */ - gen_spr_book3s_ids(env); - gen_spr_amr(env); - gen_spr_iamr(env); - gen_spr_book3s_purr(env); - gen_spr_power5p_common(env); - gen_spr_power5p_lpar(env); - gen_spr_power5p_ear(env); - gen_spr_power6_common(env); - gen_spr_power6_dbg(env); - gen_spr_power8_tce_address_control(env); - gen_spr_power8_ids(env); - gen_spr_power8_ebb(env); - gen_spr_power8_fscr(env); - gen_spr_power8_pmu_sup(env); - gen_spr_power8_pmu_user(env); - gen_spr_power8_tm(env); - gen_spr_power8_pspb(env); - gen_spr_vtb(env); - gen_spr_power8_ic(env); - gen_spr_power8_book4(env); - gen_spr_power8_rpr(env); - gen_spr_power9_mmu(env); + register_book3s_ids_sprs(env); + register_amr_sprs(env); + register_iamr_sprs(env); + register_book3s_purr_sprs(env); + register_power5p_common_sprs(env); + register_power5p_lpar_sprs(env); + register_power5p_ear_sprs(env); + register_power6_common_sprs(env); + register_power6_dbg_sprs(env); + register_power8_tce_address_control_sprs(env); + register_power8_ids_sprs(env); + register_power8_ebb_sprs(env); + register_power8_fscr_sprs(env); + register_power8_pmu_sup_sprs(env); + register_power8_pmu_user_sprs(env); + register_power8_tm_sprs(env); + register_power8_pspb_sprs(env); + register_vtb_sprs(env); + register_power8_ic_sprs(env); + register_power8_book4_sprs(env); + register_power8_rpr_sprs(env); + register_power9_mmu_sprs(env); /* FIXME: Filter fields properly based on privilege level */ spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL, @@ -9369,7 +8395,7 @@ static void init_ppc_proc(PowerPCCPU *cpu) env->tlb_type = TLB_NONE; #endif /* Register SPR common to all PowerPC implementations */ - gen_spr_generic(env); + register_generic_sprs(env); spr_register(env, SPR_PVR, "PVR", /* Linux permits userspace to read PVR */ #if defined(CONFIG_LINUX_USER) @@ -10342,4 +9368,186 @@ static void ppc_cpu_register_types(void) #endif } +void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) +{ +#define RGPL 4 +#define RFPL 4 + + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + int i; + + qemu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " + TARGET_FMT_lx " XER " TARGET_FMT_lx " CPU#%d\n", + env->nip, env->lr, env->ctr, cpu_read_xer(env), + cs->cpu_index); + qemu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " + "%08x iidx %d didx %d\n", + env->msr, env->spr[SPR_HID0], env->hflags, + cpu_mmu_index(env, true), cpu_mmu_index(env, false)); +#if !defined(NO_TIMER_DUMP) + qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 +#if !defined(CONFIG_USER_ONLY) + " DECR " TARGET_FMT_lu +#endif + "\n", + cpu_ppc_load_tbu(env), cpu_ppc_load_tbl(env) +#if !defined(CONFIG_USER_ONLY) + , cpu_ppc_load_decr(env) +#endif + ); +#endif + for (i = 0; i < 32; i++) { + if ((i & (RGPL - 1)) == 0) { + qemu_fprintf(f, "GPR%02d", i); + } + qemu_fprintf(f, " %016" PRIx64, ppc_dump_gpr(env, i)); + if ((i & (RGPL - 1)) == (RGPL - 1)) { + qemu_fprintf(f, "\n"); + } + } + qemu_fprintf(f, "CR "); + for (i = 0; i < 8; i++) + qemu_fprintf(f, "%01x", env->crf[i]); + qemu_fprintf(f, " ["); + for (i = 0; i < 8; i++) { + char a = '-'; + if (env->crf[i] & 0x08) { + a = 'L'; + } else if (env->crf[i] & 0x04) { + a = 'G'; + } else if (env->crf[i] & 0x02) { + a = 'E'; + } + qemu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' '); + } + qemu_fprintf(f, " ] RES " TARGET_FMT_lx "\n", + env->reserve_addr); + + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < 32; i++) { + if ((i & (RFPL - 1)) == 0) { + qemu_fprintf(f, "FPR%02d", i); + } + qemu_fprintf(f, " %016" PRIx64, *cpu_fpr_ptr(env, i)); + if ((i & (RFPL - 1)) == (RFPL - 1)) { + qemu_fprintf(f, "\n"); + } + } + qemu_fprintf(f, "FPSCR " TARGET_FMT_lx "\n", env->fpscr); + } + +#if !defined(CONFIG_USER_ONLY) + qemu_fprintf(f, " SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx + " PVR " TARGET_FMT_lx " VRSAVE " TARGET_FMT_lx "\n", + env->spr[SPR_SRR0], env->spr[SPR_SRR1], + env->spr[SPR_PVR], env->spr[SPR_VRSAVE]); + + qemu_fprintf(f, "SPRG0 " TARGET_FMT_lx " SPRG1 " TARGET_FMT_lx + " SPRG2 " TARGET_FMT_lx " SPRG3 " TARGET_FMT_lx "\n", + env->spr[SPR_SPRG0], env->spr[SPR_SPRG1], + env->spr[SPR_SPRG2], env->spr[SPR_SPRG3]); + + qemu_fprintf(f, "SPRG4 " TARGET_FMT_lx " SPRG5 " TARGET_FMT_lx + " SPRG6 " TARGET_FMT_lx " SPRG7 " TARGET_FMT_lx "\n", + env->spr[SPR_SPRG4], env->spr[SPR_SPRG5], + env->spr[SPR_SPRG6], env->spr[SPR_SPRG7]); + +#if defined(TARGET_PPC64) + if (env->excp_model == POWERPC_EXCP_POWER7 || + env->excp_model == POWERPC_EXCP_POWER8 || + env->excp_model == POWERPC_EXCP_POWER9 || + env->excp_model == POWERPC_EXCP_POWER10) { + qemu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n", + env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); + } +#endif + if (env->excp_model == POWERPC_EXCP_BOOKE) { + qemu_fprintf(f, "CSRR0 " TARGET_FMT_lx " CSRR1 " TARGET_FMT_lx + " MCSRR0 " TARGET_FMT_lx " MCSRR1 " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1], + env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); + + qemu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx + " ESR " TARGET_FMT_lx " DEAR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_TCR], env->spr[SPR_BOOKE_TSR], + env->spr[SPR_BOOKE_ESR], env->spr[SPR_BOOKE_DEAR]); + + qemu_fprintf(f, " PIR " TARGET_FMT_lx " DECAR " TARGET_FMT_lx + " IVPR " TARGET_FMT_lx " EPCR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_PIR], env->spr[SPR_BOOKE_DECAR], + env->spr[SPR_BOOKE_IVPR], env->spr[SPR_BOOKE_EPCR]); + + qemu_fprintf(f, " MCSR " TARGET_FMT_lx " SPRG8 " TARGET_FMT_lx + " EPR " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MCSR], env->spr[SPR_BOOKE_SPRG8], + env->spr[SPR_BOOKE_EPR]); + + /* FSL-specific */ + qemu_fprintf(f, " MCAR " TARGET_FMT_lx " PID1 " TARGET_FMT_lx + " PID2 " TARGET_FMT_lx " SVR " TARGET_FMT_lx "\n", + env->spr[SPR_Exxx_MCAR], env->spr[SPR_BOOKE_PID1], + env->spr[SPR_BOOKE_PID2], env->spr[SPR_E500_SVR]); + + /* + * IVORs are left out as they are large and do not change often -- + * they can be read with "p $ivor0", "p $ivor1", etc. + */ + } + +#if defined(TARGET_PPC64) + if (env->flags & POWERPC_FLAG_CFAR) { + qemu_fprintf(f, " CFAR " TARGET_FMT_lx"\n", env->cfar); + } +#endif + + if (env->spr_cb[SPR_LPCR].name) { + qemu_fprintf(f, " LPCR " TARGET_FMT_lx "\n", env->spr[SPR_LPCR]); + } + + switch (env->mmu_model) { + case POWERPC_MMU_32B: + case POWERPC_MMU_601: + case POWERPC_MMU_SOFT_6xx: + case POWERPC_MMU_SOFT_74xx: +#if defined(TARGET_PPC64) + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: + case POWERPC_MMU_3_00: +#endif + if (env->spr_cb[SPR_SDR1].name) { /* SDR1 Exists */ + qemu_fprintf(f, " SDR1 " TARGET_FMT_lx " ", env->spr[SPR_SDR1]); + } + if (env->spr_cb[SPR_PTCR].name) { /* PTCR Exists */ + qemu_fprintf(f, " PTCR " TARGET_FMT_lx " ", env->spr[SPR_PTCR]); + } + qemu_fprintf(f, " DAR " TARGET_FMT_lx " DSISR " TARGET_FMT_lx "\n", + env->spr[SPR_DAR], env->spr[SPR_DSISR]); + break; + case POWERPC_MMU_BOOKE206: + qemu_fprintf(f, " MAS0 " TARGET_FMT_lx " MAS1 " TARGET_FMT_lx + " MAS2 " TARGET_FMT_lx " MAS3 " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MAS0], env->spr[SPR_BOOKE_MAS1], + env->spr[SPR_BOOKE_MAS2], env->spr[SPR_BOOKE_MAS3]); + + qemu_fprintf(f, " MAS4 " TARGET_FMT_lx " MAS6 " TARGET_FMT_lx + " MAS7 " TARGET_FMT_lx " PID " TARGET_FMT_lx "\n", + env->spr[SPR_BOOKE_MAS4], env->spr[SPR_BOOKE_MAS6], + env->spr[SPR_BOOKE_MAS7], env->spr[SPR_BOOKE_PID]); + + qemu_fprintf(f, "MMUCFG " TARGET_FMT_lx " TLB0CFG " TARGET_FMT_lx + " TLB1CFG " TARGET_FMT_lx "\n", + env->spr[SPR_MMUCFG], env->spr[SPR_BOOKE_TLB0CFG], + env->spr[SPR_BOOKE_TLB1CFG]); + break; + default: + break; + } +#endif + +#undef RGPL +#undef RFPL +} type_init(ppc_cpu_register_types) diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 94a7273ee0..9339e7eafe 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -498,7 +498,7 @@ static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n) return 16; } if (n == 32) { - gdb_get_reg32(buf, helper_mfvscr(env)); + gdb_get_reg32(buf, ppc_get_vscr(env)); mem_buf = gdb_get_reg_ptr(buf, 4); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; @@ -529,7 +529,7 @@ static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) } if (n == 32) { ppc_maybe_bswap_register(env, mem_buf, 4); - helper_mtvscr(env, ldl_p(mem_buf)); + ppc_store_vscr(env, ldl_p(mem_buf)); return 4; } if (n == 33) { diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 513066d54d..ea9f2a236c 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -1,5 +1,5 @@ -DEF_HELPER_FLAGS_3(raise_exception_err, TCG_CALL_NO_WG, void, env, i32, i32) -DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_3(raise_exception_err, TCG_CALL_NO_WG, noreturn, env, i32, i32) +DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) DEF_HELPER_FLAGS_4(tw, TCG_CALL_NO_WG, void, env, tl, tl, i32) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_4(td, TCG_CALL_NO_WG, void, env, tl, tl, i32) diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index a44c2d90ea..41f8477d4b 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -462,17 +462,12 @@ SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX) void helper_mtvscr(CPUPPCState *env, uint32_t vscr) { - env->vscr = vscr & ~(1u << VSCR_SAT); - /* Which bit we set is completely arbitrary, but clear the rest. */ - env->vscr_sat.u64[0] = vscr & (1u << VSCR_SAT); - env->vscr_sat.u64[1] = 0; - set_flush_to_zero((vscr >> VSCR_NJ) & 1, &env->vec_status); + ppc_store_vscr(env, vscr); } uint32_t helper_mfvscr(CPUPPCState *env) { - uint32_t sat = (env->vscr_sat.u64[0] | env->vscr_sat.u64[1]) != 0; - return env->vscr | (sat << VSCR_SAT); + return ppc_get_vscr(env); } static inline void set_vscr_sat(CPUPPCState *env) diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 184ba6d6b3..2b4b06eb76 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -228,4 +228,23 @@ void destroy_ppc_opcodes(PowerPCCPU *cpu); void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *ppc); gchar *ppc_gdb_arch_name(CPUState *cs); +/** + * prot_for_access_type: + * @access_type: Access type + * + * Return the protection bit required for the given access type. + */ +static inline int prot_for_access_type(MMUAccessType access_type) +{ + switch (access_type) { + case MMU_INST_FETCH: + return PAGE_EXEC; + case MMU_DATA_LOAD: + return PAGE_READ; + case MMU_DATA_STORE: + return PAGE_WRITE; + } + g_assert_not_reached(); +} + #endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/machine.c b/target/ppc/machine.c index e5bffbe365..93972df58e 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -8,7 +8,6 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "kvm_ppc.h" -#include "exec/helper-proto.h" static void post_load_update_msr(CPUPPCState *env) { @@ -107,7 +106,7 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) ppc_store_sdr1(env, sdr1); } qemu_get_be32s(f, &vscr); - helper_mtvscr(env, vscr); + ppc_store_vscr(env, vscr); qemu_get_be64s(f, &env->spe_acc); qemu_get_be32s(f, &env->spe_fscr); qemu_get_betls(f, &env->msr_mask); @@ -456,7 +455,7 @@ static int get_vscr(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { PowerPCCPU *cpu = opaque; - helper_mtvscr(&cpu->env, qemu_get_be32(f)); + ppc_store_vscr(&cpu->env, qemu_get_be32(f)); return 0; } @@ -464,7 +463,7 @@ static int put_vscr(QEMUFile *f, void *opaque, size_t size, const VMStateField *field, JSONWriter *vmdesc) { PowerPCCPU *cpu = opaque; - qemu_put_be32(f, helper_mfvscr(&cpu->env)); + qemu_put_be32(f, ppc_get_vscr(&cpu->env)); return 0; } diff --git a/target/ppc/meson.build b/target/ppc/meson.build index 4079d01ee3..d1aa7d5d39 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -2,6 +2,7 @@ ppc_ss = ss.source_set() ppc_ss.add(files( 'cpu-models.c', 'cpu.c', + 'cpu_init.c', 'dfp_helper.c', 'excp_helper.c', 'fpu_helper.c', diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 002958be26..08a31da289 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -261,6 +261,16 @@ void ppc_store_msr(CPUPPCState *env, target_ulong value) hreg_store_msr(env, value, 0); } +void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; + + env->spr[SPR_LPCR] = val & pcc->lpcr_mask; + /* The gtse bit affects hflags */ + hreg_compute_hflags(env); +} + /* * This code is lifted from MacOnLinux. It is called whenever THRM1,2 * or 3 is read an fixes up the values in such a way that will make diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 178cf090b7..744a763f44 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -24,6 +24,7 @@ #include "exec/helper-proto.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" +#include "internal.h" #include "mmu-hash32.h" #include "exec/log.h" @@ -152,16 +153,17 @@ static int hash32_bat_601_prot(PowerPCCPU *cpu, return ppc_hash32_pp_prot(key, pp, 0); } -static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, int rwx, - int *prot) +static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, + MMUAccessType access_type, int *prot) { CPUPPCState *env = &cpu->env; target_ulong *BATlt, *BATut; + bool ifetch = access_type == MMU_INST_FETCH; int i; LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - rwx == 2 ? 'I' : 'D', ea); - if (rwx == 2) { + ifetch ? 'I' : 'D', ea); + if (ifetch) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; } else { @@ -180,7 +182,7 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, int rwx, } LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', i, ea, batu, batl); + ifetch ? 'I' : 'D', i, ea, batu, batl); if (mask && ((ea & mask) == (batu & BATU32_BEPI))) { hwaddr raddr = (batl & mask) | (ea & ~mask); @@ -208,7 +210,7 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, int rwx, LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " TARGET_FMT_lx "\n", - __func__, type == ACCESS_CODE ? 'I' : 'D', i, ea, + __func__, ifetch ? 'I' : 'D', i, ea, *BATu, *BATl, BEPIu, BEPIl, bl); } } @@ -218,7 +220,8 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, int rwx, } static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, - target_ulong eaddr, int rwx, + target_ulong eaddr, + MMUAccessType access_type, hwaddr *raddr, int *prot) { CPUState *cs = CPU(cpu); @@ -239,7 +242,7 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, return 0; } - if (rwx == 2) { + if (access_type == MMU_INST_FETCH) { /* No code fetch is allowed in direct-store areas */ cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x10000000; @@ -260,7 +263,7 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, /* lwarx, ldarx or srwcx. */ env->error_code = 0; env->spr[SPR_DAR] = eaddr; - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x06000000; } else { env->spr[SPR_DSISR] = 0x04000000; @@ -280,7 +283,7 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x06100000; } else { env->spr[SPR_DSISR] = 0x04100000; @@ -290,14 +293,15 @@ static int ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, cpu_abort(cs, "ERROR: instruction should not need " "address translation\n"); } - if ((rwx == 1 || key != 1) && (rwx == 0 || key != 0)) { + if ((access_type == MMU_DATA_STORE || key != 1) && + (access_type == MMU_DATA_LOAD || key != 0)) { *raddr = eaddr; return 0; } else { cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x0a000000; } else { env->spr[SPR_DSISR] = 0x08000000; @@ -421,13 +425,16 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, hwaddr pte_offset; ppc_hash_pte32_t pte; int prot; - const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; + int need_prot; + MMUAccessType access_type; hwaddr raddr; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + access_type = rwx; + need_prot = prot_for_access_type(access_type); /* 1. Handle real mode accesses */ - if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { + if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) { /* Translation is off */ raddr = eaddr; tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, @@ -438,17 +445,17 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { - raddr = ppc_hash32_bat_lookup(cpu, eaddr, rwx, &prot); + raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, &prot); if (raddr != -1) { - if (need_prot[rwx] & ~prot) { - if (rwx == 2) { + if (need_prot & ~prot) { + if (access_type == MMU_INST_FETCH) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x08000000; } else { cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x0a000000; } else { env->spr[SPR_DSISR] = 0x08000000; @@ -469,7 +476,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, /* 4. Handle direct store segments */ if (sr & SR32_T) { - if (ppc_hash32_direct_store(cpu, sr, eaddr, rwx, + if (ppc_hash32_direct_store(cpu, sr, eaddr, access_type, &raddr, &prot) == 0) { tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK, prot, mmu_idx, @@ -481,7 +488,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, } /* 5. Check for segment level no-execute violation */ - if ((rwx == 2) && (sr & SR32_NX)) { + if (access_type == MMU_INST_FETCH && (sr & SR32_NX)) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x10000000; return 1; @@ -490,14 +497,14 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, /* 6. Locate the PTE in the hash table */ pte_offset = ppc_hash32_htab_lookup(cpu, sr, eaddr, &pte); if (pte_offset == -1) { - if (rwx == 2) { + if (access_type == MMU_INST_FETCH) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x40000000; } else { cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x42000000; } else { env->spr[SPR_DSISR] = 0x40000000; @@ -513,17 +520,17 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, prot = ppc_hash32_pte_prot(cpu, sr, pte); - if (need_prot[rwx] & ~prot) { + if (need_prot & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - if (rwx == 2) { + if (access_type == MMU_INST_FETCH) { cs->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x08000000; } else { cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = eaddr; - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x0a000000; } else { env->spr[SPR_DSISR] = 0x08000000; @@ -540,7 +547,7 @@ int ppc_hash32_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, ppc_hash32_set_r(cpu, pte_offset, pte.pte1); } if (!(pte.pte1 & HPTE32_R_C)) { - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { ppc_hash32_set_c(cpu, pte_offset, pte.pte1); } else { /* diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index d517a99832..f48b625f48 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -29,6 +29,7 @@ #include "mmu-hash64.h" #include "exec/log.h" #include "hw/hw.h" +#include "internal.h" #include "mmu-book3s-v3.h" #include "helper_regs.h" @@ -876,10 +877,12 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, hwaddr ptex; ppc_hash_pte64_t pte; int exec_prot, pp_prot, amr_prot, prot; - const int need_prot[] = {PAGE_READ, PAGE_WRITE, PAGE_EXEC}; + MMUAccessType access_type; + int need_prot; hwaddr raddr; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + access_type = rwx; /* * Note on LPCR usage: 970 uses HID4, but our special variant of @@ -890,7 +893,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, */ /* 1. Handle real mode accesses */ - if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) { + if (access_type == MMU_INST_FETCH ? !msr_ir : !msr_dr) { /* * Translation is supposedly "off", but in real mode the top 4 * effective address bits are (mostly) ignored @@ -923,14 +926,19 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, /* Emulated old-style RMO mode, bounds check against RMLS */ if (raddr >= limit) { - if (rwx == 2) { + switch (access_type) { + case MMU_INST_FETCH: ppc_hash64_set_isi(cs, SRR1_PROTFAULT); - } else { - int dsisr = DSISR_PROTFAULT; - if (rwx == 1) { - dsisr |= DSISR_ISSTORE; - } - ppc_hash64_set_dsi(cs, eaddr, dsisr); + break; + case MMU_DATA_LOAD: + ppc_hash64_set_dsi(cs, eaddr, DSISR_PROTFAULT); + break; + case MMU_DATA_STORE: + ppc_hash64_set_dsi(cs, eaddr, + DSISR_PROTFAULT | DSISR_ISSTORE); + break; + default: + g_assert_not_reached(); } return 1; } @@ -953,13 +961,19 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, exit(1); } /* Segment still not found, generate the appropriate interrupt */ - if (rwx == 2) { + switch (access_type) { + case MMU_INST_FETCH: cs->exception_index = POWERPC_EXCP_ISEG; env->error_code = 0; - } else { + break; + case MMU_DATA_LOAD: + case MMU_DATA_STORE: cs->exception_index = POWERPC_EXCP_DSEG; env->error_code = 0; env->spr[SPR_DAR] = eaddr; + break; + default: + g_assert_not_reached(); } return 1; } @@ -967,7 +981,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, skip_slb_search: /* 3. Check for segment level no-execute violation */ - if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { + if (access_type == MMU_INST_FETCH && (slb->vsid & SLB_VSID_N)) { ppc_hash64_set_isi(cs, SRR1_NOEXEC_GUARD); return 1; } @@ -975,14 +989,18 @@ skip_slb_search: /* 4. Locate the PTE in the hash table */ ptex = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte, &apshift); if (ptex == -1) { - if (rwx == 2) { + switch (access_type) { + case MMU_INST_FETCH: ppc_hash64_set_isi(cs, SRR1_NOPTE); - } else { - int dsisr = DSISR_NOPTE; - if (rwx == 1) { - dsisr |= DSISR_ISSTORE; - } - ppc_hash64_set_dsi(cs, eaddr, dsisr); + break; + case MMU_DATA_LOAD: + ppc_hash64_set_dsi(cs, eaddr, DSISR_NOPTE); + break; + case MMU_DATA_STORE: + ppc_hash64_set_dsi(cs, eaddr, DSISR_NOPTE | DSISR_ISSTORE); + break; + default: + g_assert_not_reached(); } return 1; } @@ -996,10 +1014,11 @@ skip_slb_search: amr_prot = ppc_hash64_amr_prot(cpu, pte); prot = exec_prot & pp_prot & amr_prot; - if ((need_prot[rwx] & ~prot) != 0) { + need_prot = prot_for_access_type(access_type); + if (need_prot & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - if (rwx == 2) { + if (access_type == MMU_INST_FETCH) { int srr1 = 0; if (PAGE_EXEC & ~exec_prot) { srr1 |= SRR1_NOEXEC_GUARD; /* Access violates noexec or guard */ @@ -1012,13 +1031,13 @@ skip_slb_search: ppc_hash64_set_isi(cs, srr1); } else { int dsisr = 0; - if (need_prot[rwx] & ~pp_prot) { + if (need_prot & ~pp_prot) { dsisr |= DSISR_PROTFAULT; } - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { dsisr |= DSISR_ISSTORE; } - if (need_prot[rwx] & ~amr_prot) { + if (need_prot & ~amr_prot) { dsisr |= DSISR_AMR; } ppc_hash64_set_dsi(cs, eaddr, dsisr); @@ -1034,7 +1053,7 @@ skip_slb_search: ppc_hash64_set_r(cpu, ptex, pte.pte1); } if (!(pte.pte1 & HPTE64_R_C)) { - if (rwx == 1) { + if (access_type == MMU_DATA_STORE) { ppc_hash64_set_c(cpu, ptex, pte.pte1); } else { /* @@ -1120,16 +1139,6 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH; } -void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) -{ - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - CPUPPCState *env = &cpu->env; - - env->spr[SPR_LPCR] = val & pcc->lpcr_mask; - /* The gtse bit affects hflags */ - hreg_compute_hflags(env); -} - void helper_store_lpcr(CPUPPCState *env, target_ulong val) { PowerPCCPU *cpu = env_archcpu(env); @@ -1200,61 +1209,4 @@ const PPCHash64Options ppc_hash64_opts_POWER7 = { } }; -void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, - bool (*cb)(void *, uint32_t, uint32_t), - void *opaque) -{ - PPCHash64Options *opts = cpu->hash64_opts; - int i; - int n = 0; - bool ci_largepage = false; - - assert(opts); - - n = 0; - for (i = 0; i < ARRAY_SIZE(opts->sps); i++) { - PPCHash64SegmentPageSizes *sps = &opts->sps[i]; - int j; - int m = 0; - assert(n <= i); - - if (!sps->page_shift) { - break; - } - - for (j = 0; j < ARRAY_SIZE(sps->enc); j++) { - PPCHash64PageSize *ps = &sps->enc[j]; - - assert(m <= j); - if (!ps->page_shift) { - break; - } - - if (cb(opaque, sps->page_shift, ps->page_shift)) { - if (ps->page_shift >= 16) { - ci_largepage = true; - } - sps->enc[m++] = *ps; - } - } - - /* Clear rest of the row */ - for (j = m; j < ARRAY_SIZE(sps->enc); j++) { - memset(&sps->enc[j], 0, sizeof(sps->enc[j])); - } - - if (m) { - n++; - } - } - - /* Clear the rest of the table */ - for (i = n; i < ARRAY_SIZE(opts->sps); i++) { - memset(&opts->sps[i], 0, sizeof(opts->sps[i])); - } - - if (!ci_largepage) { - opts->flags &= ~PPC_HASH64_CI_LARGEPAGE; - } -} diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 87729d48b3..4b8b8e7950 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -15,12 +15,8 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong pte0, target_ulong pte1); unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, uint64_t pte0, uint64_t pte1); -void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val); void ppc_hash64_init(PowerPCCPU *cpu); void ppc_hash64_finalize(PowerPCCPU *cpu); -void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, - bool (*cb)(void *, uint32_t, uint32_t), - void *opaque); #endif /* diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 30fcfcf11f..7972153f23 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -25,6 +25,7 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "exec/log.h" +#include "internal.h" #include "mmu-radix64.h" #include "mmu-book3s-v3.h" @@ -74,71 +75,94 @@ static bool ppc_radix64_get_fully_qualified_addr(const CPUPPCState *env, return true; } -static void ppc_radix64_raise_segi(PowerPCCPU *cpu, int rwx, vaddr eaddr) +static void ppc_radix64_raise_segi(PowerPCCPU *cpu, MMUAccessType access_type, + vaddr eaddr) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - if (rwx == 2) { /* Instruction Segment Interrupt */ + switch (access_type) { + case MMU_INST_FETCH: + /* Instruction Segment Interrupt */ cs->exception_index = POWERPC_EXCP_ISEG; - } else { /* Data Segment Interrupt */ + break; + case MMU_DATA_STORE: + case MMU_DATA_LOAD: + /* Data Segment Interrupt */ cs->exception_index = POWERPC_EXCP_DSEG; env->spr[SPR_DAR] = eaddr; + break; + default: + g_assert_not_reached(); } env->error_code = 0; } -static void ppc_radix64_raise_si(PowerPCCPU *cpu, int rwx, vaddr eaddr, - uint32_t cause) +static void ppc_radix64_raise_si(PowerPCCPU *cpu, MMUAccessType access_type, + vaddr eaddr, uint32_t cause) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - if (rwx == 2) { /* Instruction Storage Interrupt */ + switch (access_type) { + case MMU_INST_FETCH: + /* Instruction Storage Interrupt */ cs->exception_index = POWERPC_EXCP_ISI; env->error_code = cause; - } else { /* Data Storage Interrupt */ + break; + case MMU_DATA_STORE: + cause |= DSISR_ISSTORE; + /* fall through */ + case MMU_DATA_LOAD: + /* Data Storage Interrupt */ cs->exception_index = POWERPC_EXCP_DSI; - if (rwx == 1) { /* Write -> Store */ - cause |= DSISR_ISSTORE; - } env->spr[SPR_DSISR] = cause; env->spr[SPR_DAR] = eaddr; env->error_code = 0; + break; + default: + g_assert_not_reached(); } } -static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, int rwx, vaddr eaddr, - hwaddr g_raddr, uint32_t cause) +static void ppc_radix64_raise_hsi(PowerPCCPU *cpu, MMUAccessType access_type, + vaddr eaddr, hwaddr g_raddr, uint32_t cause) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - if (rwx == 2) { /* H Instruction Storage Interrupt */ + switch (access_type) { + case MMU_INST_FETCH: + /* H Instruction Storage Interrupt */ cs->exception_index = POWERPC_EXCP_HISI; env->spr[SPR_ASDR] = g_raddr; env->error_code = cause; - } else { /* H Data Storage Interrupt */ + break; + case MMU_DATA_STORE: + cause |= DSISR_ISSTORE; + /* fall through */ + case MMU_DATA_LOAD: + /* H Data Storage Interrupt */ cs->exception_index = POWERPC_EXCP_HDSI; - if (rwx == 1) { /* Write -> Store */ - cause |= DSISR_ISSTORE; - } env->spr[SPR_HDSISR] = cause; env->spr[SPR_HDAR] = eaddr; env->spr[SPR_ASDR] = g_raddr; env->error_code = 0; + break; + default: + g_assert_not_reached(); } } -static bool ppc_radix64_check_prot(PowerPCCPU *cpu, int rwx, uint64_t pte, - int *fault_cause, int *prot, +static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, + uint64_t pte, int *fault_cause, int *prot, bool partition_scoped) { CPUPPCState *env = &cpu->env; - const int need_prot[] = { PAGE_READ, PAGE_WRITE, PAGE_EXEC }; + int need_prot; /* Check Page Attributes (pte58:59) */ - if (((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO) && (rwx == 2)) { + if ((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO && access_type == MMU_INST_FETCH) { /* * Radix PTE entries with the non-idempotent I/O attribute are treated * as guarded storage @@ -158,7 +182,8 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, int rwx, uint64_t pte, } /* Check if requested access type is allowed */ - if (need_prot[rwx] & ~(*prot)) { /* Page Protected for that Access */ + need_prot = prot_for_access_type(access_type); + if (need_prot & ~*prot) { /* Page Protected for that Access */ *fault_cause |= DSISR_PROTFAULT; return true; } @@ -166,15 +191,15 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, int rwx, uint64_t pte, return false; } -static void ppc_radix64_set_rc(PowerPCCPU *cpu, int rwx, uint64_t pte, - hwaddr pte_addr, int *prot) +static void ppc_radix64_set_rc(PowerPCCPU *cpu, MMUAccessType access_type, + uint64_t pte, hwaddr pte_addr, int *prot) { CPUState *cs = CPU(cpu); uint64_t npte; npte = pte | R_PTE_R; /* Always set reference bit */ - if (rwx == 1) { /* Store/Write */ + if (access_type == MMU_DATA_STORE) { /* Store/Write */ npte |= R_PTE_C; /* Set change bit */ } else { /* @@ -269,7 +294,8 @@ static bool validate_pate(PowerPCCPU *cpu, uint64_t lpid, ppc_v3_pate_t *pate) return true; } -static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, int rwx, +static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, + MMUAccessType access_type, vaddr eaddr, hwaddr g_raddr, ppc_v3_pate_t pate, hwaddr *h_raddr, int *h_prot, @@ -285,24 +311,25 @@ static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, int rwx, if (ppc_radix64_walk_tree(CPU(cpu)->as, g_raddr, pate.dw0 & PRTBE_R_RPDB, pate.dw0 & PRTBE_R_RPDS, h_raddr, h_page_size, &pte, &fault_cause, &pte_addr) || - ppc_radix64_check_prot(cpu, rwx, pte, &fault_cause, h_prot, true)) { + ppc_radix64_check_prot(cpu, access_type, pte, &fault_cause, h_prot, true)) { if (pde_addr) { /* address being translated was that of a guest pde */ fault_cause |= DSISR_PRTABLE_FAULT; } if (guest_visible) { - ppc_radix64_raise_hsi(cpu, rwx, eaddr, g_raddr, fault_cause); + ppc_radix64_raise_hsi(cpu, access_type, eaddr, g_raddr, fault_cause); } return 1; } if (guest_visible) { - ppc_radix64_set_rc(cpu, rwx, pte, pte_addr, h_prot); + ppc_radix64_set_rc(cpu, access_type, pte, pte_addr, h_prot); } return 0; } -static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, int rwx, +static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, + MMUAccessType access_type, vaddr eaddr, uint64_t pid, ppc_v3_pate_t pate, hwaddr *g_raddr, int *g_prot, int *g_page_size, @@ -321,7 +348,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, int rwx, if (offset >= size) { /* offset exceeds size of the process table */ if (guest_visible) { - ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE); + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_NOPTE); } return 1; } @@ -362,7 +389,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, int rwx, if (ret) { /* No valid PTE */ if (guest_visible) { - ppc_radix64_raise_si(cpu, rwx, eaddr, fault_cause); + ppc_radix64_raise_si(cpu, access_type, eaddr, fault_cause); } return ret; } @@ -391,7 +418,7 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, int rwx, if (ret) { /* No valid pte */ if (guest_visible) { - ppc_radix64_raise_si(cpu, rwx, eaddr, fault_cause); + ppc_radix64_raise_si(cpu, access_type, eaddr, fault_cause); } return ret; } @@ -405,16 +432,16 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, int rwx, *g_raddr = (rpn & ~mask) | (eaddr & mask); } - if (ppc_radix64_check_prot(cpu, rwx, pte, &fault_cause, g_prot, false)) { + if (ppc_radix64_check_prot(cpu, access_type, pte, &fault_cause, g_prot, false)) { /* Access denied due to protection */ if (guest_visible) { - ppc_radix64_raise_si(cpu, rwx, eaddr, fault_cause); + ppc_radix64_raise_si(cpu, access_type, eaddr, fault_cause); } return 1; } if (guest_visible) { - ppc_radix64_set_rc(cpu, rwx, pte, pte_addr, g_prot); + ppc_radix64_set_rc(cpu, access_type, pte, pte_addr, g_prot); } return 0; @@ -437,7 +464,8 @@ static int ppc_radix64_process_scoped_xlate(PowerPCCPU *cpu, int rwx, * | = On | Process Scoped | Scoped | * +-------------+----------------+---------------+ */ -static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, int rwx, +static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, bool relocation, hwaddr *raddr, int *psizep, int *protp, bool guest_visible) @@ -451,7 +479,7 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, int rwx, /* Virtual Mode Access - get the fully qualified address */ if (!ppc_radix64_get_fully_qualified_addr(&cpu->env, eaddr, &lpid, &pid)) { if (guest_visible) { - ppc_radix64_raise_segi(cpu, rwx, eaddr); + ppc_radix64_raise_segi(cpu, access_type, eaddr); } return 1; } @@ -464,13 +492,13 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, int rwx, } else { if (!ppc64_v3_get_pate(cpu, lpid, &pate)) { if (guest_visible) { - ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE); + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_NOPTE); } return 1; } if (!validate_pate(cpu, lpid, &pate)) { if (guest_visible) { - ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_R_BADCONFIG); + ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_R_BADCONFIG); } return 1; } @@ -488,7 +516,7 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, int rwx, * - Translates an effective address to a guest real address. */ if (relocation) { - int ret = ppc_radix64_process_scoped_xlate(cpu, rwx, eaddr, pid, + int ret = ppc_radix64_process_scoped_xlate(cpu, access_type, eaddr, pid, pate, &g_raddr, &prot, &psize, guest_visible); if (ret) { @@ -511,9 +539,10 @@ static int ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, int rwx, if (lpid || !msr_hv) { int ret; - ret = ppc_radix64_partition_scoped_xlate(cpu, rwx, eaddr, g_raddr, - pate, raddr, &prot, &psize, - false, guest_visible); + ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr, + g_raddr, pate, raddr, + &prot, &psize, false, + guest_visible); if (ret) { return ret; } @@ -534,12 +563,14 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, CPUPPCState *env = &cpu->env; int page_size, prot; bool relocation; + MMUAccessType access_type; hwaddr raddr; assert(!(msr_hv && cpu->vhyp)); assert((rwx == 0) || (rwx == 1) || (rwx == 2)); + access_type = rwx; - relocation = ((rwx == 2) && (msr_ir == 1)) || ((rwx != 2) && (msr_dr == 1)); + relocation = (access_type == MMU_INST_FETCH ? msr_ir : msr_dr); /* HV or virtual hypervisor Real Mode Access */ if (!relocation && (msr_hv || cpu->vhyp)) { /* In real mode top 4 effective addr bits (mostly) ignored */ @@ -568,7 +599,7 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, } /* Translate eaddr to raddr (where raddr is addr qemu needs for access) */ - if (ppc_radix64_xlate(cpu, eaddr, rwx, relocation, &raddr, + if (ppc_radix64_xlate(cpu, eaddr, access_type, relocation, &raddr, &page_size, &prot, true)) { return 1; } diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index ca88658cba..37986c59ba 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -32,6 +32,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/qemu-print.h" +#include "internal.h" #include "mmu-book3s-v3.h" #include "mmu-radix64.h" @@ -126,36 +127,14 @@ static int pp_check(int key, int pp, int nx) return access; } -static int check_prot(int prot, int rw, int access_type) +static int check_prot(int prot, MMUAccessType access_type) { - int ret; - - if (access_type == ACCESS_CODE) { - if (prot & PAGE_EXEC) { - ret = 0; - } else { - ret = -2; - } - } else if (rw) { - if (prot & PAGE_WRITE) { - ret = 0; - } else { - ret = -2; - } - } else { - if (prot & PAGE_READ) { - ret = 0; - } else { - ret = -2; - } - } - - return ret; + return prot & prot_for_access_type(access_type) ? 0 : -2; } -static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, - int rw, int type) +static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, + target_ulong pte1, int h, + MMUAccessType access_type) { target_ulong ptem, mmask; int access, ret, pteh, ptev, pp; @@ -182,7 +161,7 @@ static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, /* Keep the matching PTE information */ ctx->raddr = pte1; ctx->prot = access; - ret = check_prot(ctx->prot, rw, type); + ret = check_prot(ctx->prot, access_type); if (ret == 0) { /* Access granted */ qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); @@ -197,7 +176,7 @@ static inline int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, } static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, int rw) + int ret, MMUAccessType access_type) { int store = 0; @@ -208,7 +187,7 @@ static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, store = 1; } if (!(*pte1p & 0x00000080)) { - if (rw == 1 && ret == 0) { + if (access_type == MMU_DATA_STORE && ret == 0) { /* Update changed flag */ *pte1p |= 0x00000080; store = 1; @@ -308,8 +287,8 @@ static void ppc6xx_tlb_store(CPUPPCState *env, target_ulong EPN, int way, env->last_way = way; } -static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type) +static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, MMUAccessType access_type) { ppc6xx_tlb_t *tlb; int nr, best, way; @@ -318,8 +297,7 @@ static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, best = -1; ret = -1; /* No TLB found */ for (way = 0; way < env->nb_ways; way++) { - nr = ppc6xx_tlb_getnum(env, eaddr, way, - access_type == ACCESS_CODE ? 1 : 0); + nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == MMU_INST_FETCH); tlb = &env->tlb.tlb6[nr]; /* This test "emulates" the PTE index match for hardware TLBs */ if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { @@ -333,9 +311,10 @@ static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, TARGET_FMT_lx " %c %c\n", nr, env->nb_tlb, pte_is_valid(tlb->pte0) ? "valid" : "inval", tlb->EPN, eaddr, tlb->pte1, - rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D'); + access_type == MMU_DATA_STORE ? 'S' : 'L', + access_type == MMU_INST_FETCH ? 'I' : 'D'); switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, - 0, rw, access_type)) { + 0, access_type)) { case -3: /* TLB inconsistency */ return -1; @@ -366,7 +345,7 @@ static inline int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n", ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret); /* Update page flags */ - pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, rw); + pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type); } return ret; @@ -400,24 +379,22 @@ static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, } static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong virtual, int rw, int type) + target_ulong virtual, MMUAccessType access_type) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong BEPIl, BEPIu, bl; int i, valid, prot; int ret = -1; + bool ifetch = access_type == MMU_INST_FETCH; LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', virtual); - switch (type) { - case ACCESS_CODE: + ifetch ? 'I' : 'D', virtual); + if (ifetch) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; - break; - default: + } else { BATlt = env->DBAT[1]; BATut = env->DBAT[0]; - break; } for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; @@ -427,7 +404,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl); + ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); if ((virtual & 0xF0000000) == BEPIu && ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { /* BAT matches */ @@ -438,7 +415,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, (virtual & 0x0001F000); /* Compute access rights */ ctx->prot = prot; - ret = check_prot(ctx->prot, rw, type); + ret = check_prot(ctx->prot, access_type); if (ret == 0) { LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n", i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-', @@ -461,7 +438,7 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " TARGET_FMT_lx "\n", - __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, + __func__, ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl, BEPIu, BEPIl, bl); } } @@ -472,8 +449,9 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, } /* Perform segment based translation */ -static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int type) +static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, MMUAccessType access_type, + int type) { PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; @@ -497,7 +475,7 @@ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx " ir=%d dr=%d pr=%d %d t=%d\n", eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir, - (int)msr_dr, pr != 0 ? 1 : 0, rw, type); + (int)msr_dr, pr != 0 ? 1 : 0, access_type == MMU_DATA_STORE, type); pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; hash = vsid ^ pgidx; ctx->ptem = (vsid << 7) | (pgidx >> 10); @@ -520,7 +498,7 @@ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, /* Initialize real address with an invalid value */ ctx->raddr = (hwaddr)-1ULL; /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); + ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); #if defined(DUMP_PAGE_TABLES) if (qemu_loglevel_mask(CPU_LOG_MMU)) { CPUState *cs = env_cpu(env); @@ -603,7 +581,8 @@ static inline int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, "address translation\n"); return -4; } - if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) { + if ((access_type == MMU_DATA_STORE || ctx->key != 1) && + (access_type == MMU_DATA_LOAD || ctx->key != 0)) { ctx->raddr = eaddr; ret = 2; } else { @@ -682,8 +661,8 @@ static inline void ppc4xx_tlb_invalidate_all(CPUPPCState *env) } static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, int rw, - int access_type) + target_ulong address, + MMUAccessType access_type) { ppcemb_tlb_t *tlb; hwaddr raddr; @@ -700,8 +679,8 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, } zsel = (tlb->attr >> 4) & 0xF; zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; - LOG_SWTLB("%s: TLB %d zsel %d zpr %d rw %d attr %08x\n", - __func__, i, zsel, zpr, rw, tlb->attr); + LOG_SWTLB("%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", + __func__, i, zsel, zpr, access_type, tlb->attr); /* Check execute enable bit */ switch (zpr) { case 0x2: @@ -727,7 +706,7 @@ static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, check_perms: /* Check from TLB entry */ ctx->prot = tlb->prot; - ret = check_prot(ctx->prot, rw, access_type); + ret = check_prot(ctx->prot, access_type); if (ret == -2) { env->spr[SPR_40x_ESR] = 0; } @@ -757,12 +736,11 @@ void store_40x_sler(CPUPPCState *env, uint32_t val) env->spr[SPR_405_SLER] = val; } -static inline int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, - hwaddr *raddr, int *prot, - target_ulong address, int rw, - int access_type, int i) +static int mmubooke_check_tlb(CPUPPCState *env, ppcemb_tlb_t *tlb, + hwaddr *raddr, int *prot, target_ulong address, + MMUAccessType access_type, int i) { - int ret, prot2; + int prot2; if (ppcemb_tlb_check(env, tlb, raddr, address, env->spr[SPR_BOOKE_PID], @@ -794,42 +772,24 @@ found_tlb: } /* Check the address space */ - if (access_type == ACCESS_CODE) { - if (msr_ir != (tlb->attr & 1)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = prot2; - if (prot2 & PAGE_EXEC) { - LOG_SWTLB("%s: good TLB!\n", __func__); - return 0; - } - - LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, prot2); - ret = -3; - } else { - if (msr_dr != (tlb->attr & 1)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = prot2; - if ((!rw && prot2 & PAGE_READ) || (rw && (prot2 & PAGE_WRITE))) { - LOG_SWTLB("%s: found TLB!\n", __func__); - return 0; - } + if ((access_type == MMU_INST_FETCH ? msr_ir : msr_dr) != (tlb->attr & 1)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } - LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, prot2); - ret = -2; + *prot = prot2; + if (prot2 & prot_for_access_type(access_type)) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; } - return ret; + LOG_SWTLB("%s: no prot match: %x\n", __func__, prot2); + return access_type == MMU_INST_FETCH ? -3 : -2; } static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, int rw, - int access_type) + target_ulong address, + MMUAccessType access_type) { ppcemb_tlb_t *tlb; hwaddr raddr; @@ -839,7 +799,7 @@ static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, raddr = (hwaddr)-1ULL; for (i = 0; i < env->nb_tlb; i++) { tlb = &env->tlb.tlbe[i]; - ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, rw, + ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, access_type, i); if (ret != -1) { break; @@ -938,10 +898,10 @@ static bool is_epid_mmu(int mmu_idx) return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; } -static uint32_t mmubooke206_esr(int mmu_idx, bool rw) +static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) { uint32_t esr = 0; - if (rw) { + if (access_type == MMU_DATA_STORE) { esr |= ESR_ST; } if (is_epid_mmu(mmu_idx)) { @@ -983,10 +943,9 @@ static bool mmubooke206_get_as(CPUPPCState *env, /* Check if the tlb found by hashing really matches */ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddr, int *prot, - target_ulong address, int rw, - int access_type, int mmu_idx) + target_ulong address, + MMUAccessType access_type, int mmu_idx) { - int ret; int prot2 = 0; uint32_t epid; bool as, pr; @@ -1043,44 +1002,31 @@ found_tlb: } /* Check the address space and permissions */ - if (access_type == ACCESS_CODE) { + if (access_type == MMU_INST_FETCH) { /* There is no way to fetch code using epid load */ assert(!use_epid); - if (msr_ir != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = prot2; - if (prot2 & PAGE_EXEC) { - LOG_SWTLB("%s: good TLB!\n", __func__); - return 0; - } - - LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, prot2); - ret = -3; - } else { - if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - LOG_SWTLB("%s: AS doesn't match\n", __func__); - return -1; - } + as = msr_ir; + } - *prot = prot2; - if ((!rw && prot2 & PAGE_READ) || (rw && (prot2 & PAGE_WRITE))) { - LOG_SWTLB("%s: found TLB!\n", __func__); - return 0; - } + if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + LOG_SWTLB("%s: AS doesn't match\n", __func__); + return -1; + } - LOG_SWTLB("%s: PAGE_READ/WRITE doesn't match: %x\n", __func__, prot2); - ret = -2; + *prot = prot2; + if (prot2 & prot_for_access_type(access_type)) { + LOG_SWTLB("%s: good TLB!\n", __func__); + return 0; } - return ret; + LOG_SWTLB("%s: no prot match: %x\n", __func__, prot2); + return access_type == MMU_INST_FETCH ? -3 : -2; } static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, int rw, - int access_type, int mmu_idx) + target_ulong address, + MMUAccessType access_type, + int mmu_idx) { ppcmas_tlb_t *tlb; hwaddr raddr; @@ -1098,7 +1044,7 @@ static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, continue; } ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, - rw, access_type, mmu_idx); + access_type, mmu_idx); if (ret != -1) { goto found_tlb; } @@ -1361,8 +1307,8 @@ void dump_mmu(CPUPPCState *env) } } -static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw) +static int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, + MMUAccessType access_type) { int in_plb, ret; @@ -1393,7 +1339,7 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0; if (in_plb ^ msr_px) { /* Access in protected area */ - if (rw == 1) { + if (access_type == MMU_DATA_STORE) { /* Access is not allowed */ ret = -2; } @@ -1413,28 +1359,28 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -static int get_physical_address_wtlb( - CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type, - int mmu_idx) +static int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, + MMUAccessType access_type, int type, + int mmu_idx) { int ret = -1; - bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) - || (access_type != ACCESS_CODE && msr_dr == 0); + bool real_mode = (type == ACCESS_CODE && msr_ir == 0) + || (type != ACCESS_CODE && msr_dr == 0); switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: if (real_mode) { - ret = check_physical(env, ctx, eaddr, rw); + ret = check_physical(env, ctx, eaddr, access_type); } else { /* Try to find a BAT */ if (env->nb_BATs != 0) { - ret = get_bat_6xx_tlb(env, ctx, eaddr, rw, access_type); + ret = get_bat_6xx_tlb(env, ctx, eaddr, access_type); } if (ret < 0) { /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment_6xx_tlb(env, ctx, eaddr, rw, access_type); + ret = get_segment_6xx_tlb(env, ctx, eaddr, access_type, type); } } break; @@ -1442,19 +1388,17 @@ static int get_physical_address_wtlb( case POWERPC_MMU_SOFT_4xx: case POWERPC_MMU_SOFT_4xx_Z: if (real_mode) { - ret = check_physical(env, ctx, eaddr, rw); + ret = check_physical(env, ctx, eaddr, access_type); } else { - ret = mmu40x_get_physical_address(env, ctx, eaddr, - rw, access_type); + ret = mmu40x_get_physical_address(env, ctx, eaddr, access_type); } break; case POWERPC_MMU_BOOKE: - ret = mmubooke_get_physical_address(env, ctx, eaddr, - rw, access_type); + ret = mmubooke_get_physical_address(env, ctx, eaddr, access_type); break; case POWERPC_MMU_BOOKE206: - ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, - access_type, mmu_idx); + ret = mmubooke206_get_physical_address(env, ctx, eaddr, access_type, + mmu_idx); break; case POWERPC_MMU_MPC8xx: /* XXX: TODO */ @@ -1462,7 +1406,7 @@ static int get_physical_address_wtlb( break; case POWERPC_MMU_REAL: if (real_mode) { - ret = check_physical(env, ctx, eaddr, rw); + ret = check_physical(env, ctx, eaddr, access_type); } else { cpu_abort(env_cpu(env), "PowerPC in real mode do not do any translation\n"); @@ -1476,11 +1420,11 @@ static int get_physical_address_wtlb( return ret; } -static int get_physical_address( - CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type) +static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, MMUAccessType access_type, + int type) { - return get_physical_address_wtlb(env, ctx, eaddr, rw, access_type, 0); + return get_physical_address_wtlb(env, ctx, eaddr, access_type, type, 0); } hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) @@ -1508,14 +1452,15 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) ; } - if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0)) { + if (unlikely(get_physical_address(env, &ctx, addr, MMU_DATA_LOAD, + ACCESS_INT) != 0)) { /* * Some MMUs have separate TLBs for code and data. If we only * try an ACCESS_INT, we may not be able to read instructions * mapped by code TLBs, so we also try a ACCESS_CODE. */ - if (unlikely(get_physical_address(env, &ctx, addr, 0, + if (unlikely(get_physical_address(env, &ctx, addr, MMU_INST_FETCH, ACCESS_CODE) != 0)) { return -1; } @@ -1525,13 +1470,14 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, - int rw, int mmu_idx) + MMUAccessType access_type, int mmu_idx) { uint32_t epid; bool as, pr; uint32_t missed_tid = 0; bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - if (rw == 2) { + + if (access_type == MMU_INST_FETCH) { as = msr_ir; } env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; @@ -1579,24 +1525,23 @@ static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, /* Perform address translation */ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, - int rw, int mmu_idx) + MMUAccessType access_type, int mmu_idx) { CPUState *cs = env_cpu(env); PowerPCCPU *cpu = POWERPC_CPU(cs); mmu_ctx_t ctx; - int access_type; + int type; int ret = 0; - if (rw == 2) { + if (access_type == MMU_INST_FETCH) { /* code access */ - rw = 0; - access_type = ACCESS_CODE; + type = ACCESS_CODE; } else { /* data access */ - access_type = env->access_type; + type = env->access_type; } - ret = get_physical_address_wtlb(env, &ctx, address, rw, - access_type, mmu_idx); + ret = get_physical_address_wtlb(env, &ctx, address, access_type, + type, mmu_idx); if (ret == 0) { tlb_set_page(cs, address & TARGET_PAGE_MASK, ctx.raddr & TARGET_PAGE_MASK, ctx.prot, @@ -1604,7 +1549,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, ret = 0; } else if (ret < 0) { LOG_MMU_STATE(cs); - if (access_type == ACCESS_CODE) { + if (type == ACCESS_CODE) { switch (ret) { case -1: /* No matches in page tables or TLB */ @@ -1632,7 +1577,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, cs->exception_index = POWERPC_EXCP_ITLB; env->error_code = 0; env->spr[SPR_BOOKE_DEAR] = address; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, 0); + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); return -1; case POWERPC_MMU_MPC8xx: /* XXX: TODO */ @@ -1674,7 +1619,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, /* No matches in page tables or TLB */ switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: - if (rw == 1) { + if (access_type == MMU_DATA_STORE) { cs->exception_index = POWERPC_EXCP_DSTLB; env->error_code = 1 << 16; } else { @@ -1691,7 +1636,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, get_pteg_offset32(cpu, ctx.hash[1]); break; case POWERPC_MMU_SOFT_74xx: - if (rw == 1) { + if (access_type == MMU_DATA_STORE) { cs->exception_index = POWERPC_EXCP_DSTLB; } else { cs->exception_index = POWERPC_EXCP_DLTLB; @@ -1708,7 +1653,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, cs->exception_index = POWERPC_EXCP_DTLB; env->error_code = 0; env->spr[SPR_40x_DEAR] = address; - if (rw) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_40x_ESR] = 0x00800000; } else { env->spr[SPR_40x_ESR] = 0x00000000; @@ -1719,13 +1664,13 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); break; case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, address, rw, mmu_idx); + booke206_update_mas_tlb_miss(env, address, access_type, mmu_idx); /* fall through */ case POWERPC_MMU_BOOKE: cs->exception_index = POWERPC_EXCP_DTLB; env->error_code = 0; env->spr[SPR_BOOKE_DEAR] = address; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, rw); + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); return -1; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " @@ -1743,16 +1688,16 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, if (env->mmu_model == POWERPC_MMU_SOFT_4xx || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) { env->spr[SPR_40x_DEAR] = address; - if (rw) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_40x_ESR] |= 0x00800000; } } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || (env->mmu_model == POWERPC_MMU_BOOKE206)) { env->spr[SPR_BOOKE_DEAR] = address; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, rw); + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); } else { env->spr[SPR_DAR] = address; - if (rw == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x0A000000; } else { env->spr[SPR_DSISR] = 0x08000000; @@ -1761,7 +1706,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, break; case -4: /* Direct store exception */ - switch (access_type) { + switch (type) { case ACCESS_FLOAT: /* Floating point load/store */ cs->exception_index = POWERPC_EXCP_ALIGN; @@ -1773,7 +1718,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = address; - if (rw == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x06000000; } else { env->spr[SPR_DSISR] = 0x04000000; @@ -1784,7 +1729,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; env->spr[SPR_DAR] = address; - if (rw == 1) { + if (access_type == MMU_DATA_STORE) { env->spr[SPR_DSISR] = 0x06100000; } else { env->spr[SPR_DSISR] = 0x04100000; @@ -2085,32 +2030,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) /*****************************************************************************/ /* Special registers manipulation */ -void ppc_store_sdr1(CPUPPCState *env, target_ulong value) -{ - PowerPCCPU *cpu = env_archcpu(env); - qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); - assert(!cpu->vhyp); -#if defined(TARGET_PPC64) - if (mmu_is_64bit(env->mmu_model)) { - target_ulong sdr_mask = SDR_64_HTABORG | SDR_64_HTABSIZE; - target_ulong htabsize = value & SDR_64_HTABSIZE; - - if (value & ~sdr_mask) { - error_report("Invalid bits 0x"TARGET_FMT_lx" set in SDR1", - value & ~sdr_mask); - value &= sdr_mask; - } - if (htabsize > 28) { - error_report("Invalid HTABSIZE 0x" TARGET_FMT_lx" stored in SDR1", - htabsize); - return; - } - } -#endif /* defined(TARGET_PPC64) */ - /* FIXME: Should check for valid HTABMASK values in 32-bit case */ - env->spr[SPR_SDR1] = value; -} - #if defined(TARGET_PPC64) void ppc_store_ptcr(CPUPPCState *env, target_ulong value) { diff --git a/target/ppc/spr_tcg.h b/target/ppc/spr_tcg.h new file mode 100644 index 0000000000..0be5f347d5 --- /dev/null +++ b/target/ppc/spr_tcg.h @@ -0,0 +1,136 @@ +/* + * PowerPC emulation for qemu: read/write callbacks for SPRs + * + * Copyright (C) 2021 Instituto de Pesquisas Eldorado + * + * 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.1 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/>. + */ +#ifndef SPR_TCG_H +#define SPR_TCG_H + +#define SPR_NOACCESS (&spr_noaccess) + +/* prototypes for readers and writers for SPRs */ +void spr_noaccess(DisasContext *ctx, int gprn, int sprn); +void spr_read_generic(DisasContext *ctx, int gprn, int sprn); +void spr_write_generic(DisasContext *ctx, int sprn, int gprn); +void spr_read_xer(DisasContext *ctx, int gprn, int sprn); +void spr_write_xer(DisasContext *ctx, int sprn, int gprn); +void spr_read_lr(DisasContext *ctx, int gprn, int sprn); +void spr_write_lr(DisasContext *ctx, int sprn, int gprn); +void spr_read_ctr(DisasContext *ctx, int gprn, int sprn); +void spr_write_ctr(DisasContext *ctx, int sprn, int gprn); +void spr_read_ureg(DisasContext *ctx, int gprn, int sprn); +void spr_read_tbl(DisasContext *ctx, int gprn, int sprn); +void spr_read_tbu(DisasContext *ctx, int gprn, int sprn); +void spr_read_atbl(DisasContext *ctx, int gprn, int sprn); +void spr_read_atbu(DisasContext *ctx, int gprn, int sprn); +void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn); +void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn); +void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn); +void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn); + +#ifndef CONFIG_USER_ONLY +void spr_write_generic32(DisasContext *ctx, int sprn, int gprn); +void spr_write_clear(DisasContext *ctx, int sprn, int gprn); +void spr_access_nop(DisasContext *ctx, int sprn, int gprn); +void spr_read_decr(DisasContext *ctx, int gprn, int sprn); +void spr_write_decr(DisasContext *ctx, int sprn, int gprn); +void spr_write_tbl(DisasContext *ctx, int sprn, int gprn); +void spr_write_tbu(DisasContext *ctx, int sprn, int gprn); +void spr_write_atbl(DisasContext *ctx, int sprn, int gprn); +void spr_write_atbu(DisasContext *ctx, int sprn, int gprn); +void spr_read_ibat(DisasContext *ctx, int gprn, int sprn); +void spr_read_ibat_h(DisasContext *ctx, int gprn, int sprn); +void spr_write_ibatu(DisasContext *ctx, int sprn, int gprn); +void spr_write_ibatu_h(DisasContext *ctx, int sprn, int gprn); +void spr_write_ibatl(DisasContext *ctx, int sprn, int gprn); +void spr_write_ibatl_h(DisasContext *ctx, int sprn, int gprn); +void spr_read_dbat(DisasContext *ctx, int gprn, int sprn); +void spr_read_dbat_h(DisasContext *ctx, int gprn, int sprn); +void spr_write_dbatu(DisasContext *ctx, int sprn, int gprn); +void spr_write_dbatu_h(DisasContext *ctx, int sprn, int gprn); +void spr_write_dbatl(DisasContext *ctx, int sprn, int gprn); +void spr_write_dbatl_h(DisasContext *ctx, int sprn, int gprn); +void spr_write_sdr1(DisasContext *ctx, int sprn, int gprn); +void spr_write_601_rtcu(DisasContext *ctx, int sprn, int gprn); +void spr_write_601_rtcl(DisasContext *ctx, int sprn, int gprn); +void spr_write_hid0_601(DisasContext *ctx, int sprn, int gprn); +void spr_read_601_ubat(DisasContext *ctx, int gprn, int sprn); +void spr_write_601_ubatu(DisasContext *ctx, int sprn, int gprn); +void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn); +void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn); +void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn); +void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn); +void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn); +void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn); +void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn); +void spr_read_403_pbr(DisasContext *ctx, int gprn, int sprn); +void spr_write_403_pbr(DisasContext *ctx, int sprn, int gprn); +void spr_write_pir(DisasContext *ctx, int sprn, int gprn); +void spr_write_excp_prefix(DisasContext *ctx, int sprn, int gprn); +void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn); +void spr_read_thrm(DisasContext *ctx, int gprn, int sprn); +void spr_write_e500_l1csr0(DisasContext *ctx, int sprn, int gprn); +void spr_write_e500_l1csr1(DisasContext *ctx, int sprn, int gprn); +void spr_write_e500_l2csr0(DisasContext *ctx, int sprn, int gprn); +void spr_write_booke206_mmucsr0(DisasContext *ctx, int sprn, int gprn); +void spr_write_booke_pid(DisasContext *ctx, int sprn, int gprn); +void spr_write_eplc(DisasContext *ctx, int sprn, int gprn); +void spr_write_epsc(DisasContext *ctx, int sprn, int gprn); +void spr_write_mas73(DisasContext *ctx, int sprn, int gprn); +void spr_read_mas73(DisasContext *ctx, int gprn, int sprn); +#ifdef TARGET_PPC64 +void spr_read_cfar(DisasContext *ctx, int gprn, int sprn); +void spr_write_cfar(DisasContext *ctx, int sprn, int gprn); +void spr_write_ureg(DisasContext *ctx, int sprn, int gprn); +void spr_read_purr(DisasContext *ctx, int gprn, int sprn); +void spr_write_purr(DisasContext *ctx, int sprn, int gprn); +void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn); +void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn); +void spr_read_vtb(DisasContext *ctx, int gprn, int sprn); +void spr_write_vtb(DisasContext *ctx, int sprn, int gprn); +void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn); +void spr_write_pidr(DisasContext *ctx, int sprn, int gprn); +void spr_write_lpidr(DisasContext *ctx, int sprn, int gprn); +void spr_read_hior(DisasContext *ctx, int gprn, int sprn); +void spr_write_hior(DisasContext *ctx, int sprn, int gprn); +void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn); +void spr_write_pcr(DisasContext *ctx, int sprn, int gprn); +void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn); +void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn); +void spr_write_amr(DisasContext *ctx, int sprn, int gprn); +void spr_write_uamor(DisasContext *ctx, int sprn, int gprn); +void spr_write_iamr(DisasContext *ctx, int sprn, int gprn); +#endif +#endif + +#ifdef TARGET_PPC64 +void spr_read_prev_upper32(DisasContext *ctx, int gprn, int sprn); +void spr_write_prev_upper32(DisasContext *ctx, int sprn, int gprn); +void spr_read_tar(DisasContext *ctx, int gprn, int sprn); +void spr_write_tar(DisasContext *ctx, int sprn, int gprn); +void spr_read_tm(DisasContext *ctx, int gprn, int sprn); +void spr_write_tm(DisasContext *ctx, int sprn, int gprn); +void spr_read_tm_upper32(DisasContext *ctx, int gprn, int sprn); +void spr_write_tm_upper32(DisasContext *ctx, int sprn, int gprn); +void spr_read_ebb(DisasContext *ctx, int gprn, int sprn); +void spr_write_ebb(DisasContext *ctx, int sprn, int gprn); +void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn); +void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn); +void spr_write_hmer(DisasContext *ctx, int sprn, int gprn); +void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn); +#endif + +#endif diff --git a/target/ppc/translate.c b/target/ppc/translate.c index a6381208a5..ea200f9637 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -36,7 +36,10 @@ #include "exec/translator.h" #include "exec/log.h" #include "qemu/atomic128.h" +#include "spr_tcg.h" +#include "qemu/qemu-print.h" +#include "qapi/error.h" #define CPU_SINGLE_STEP 0x1 #define CPU_BRANCH_STEP 0x2 @@ -154,8 +157,8 @@ void ppc_translate_init(void) /* internal defines */ struct DisasContext { DisasContextBase base; + target_ulong cia; /* current instruction address */ uint32_t opcode; - uint32_t exception; /* Routine used to access memory */ bool pr, hv, dr, le_mode; bool lazy_tlb_flush; @@ -181,6 +184,11 @@ struct DisasContext { uint64_t insns_flags2; }; +#define DISAS_EXIT DISAS_TARGET_0 /* exit to main loop, pc updated */ +#define DISAS_EXIT_UPDATE DISAS_TARGET_1 /* exit to main loop, pc stale */ +#define DISAS_CHAIN DISAS_TARGET_2 /* lookup next tb, pc updated */ +#define DISAS_CHAIN_UPDATE DISAS_TARGET_3 /* lookup next tb, pc stale */ + /* Return true iff byteswap is needed in a scalar memop */ static inline bool need_byteswap(const DisasContext *ctx) { @@ -252,15 +260,13 @@ static void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error) * These are all synchronous exceptions, we set the PC back to the * faulting instruction */ - if (ctx->exception == POWERPC_EXCP_NONE) { - gen_update_nip(ctx, ctx->base.pc_next - 4); - } + gen_update_nip(ctx, ctx->cia); t0 = tcg_const_i32(excp); t1 = tcg_const_i32(error); gen_helper_raise_exception_err(cpu_env, t0, t1); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); - ctx->exception = (excp); + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_exception(DisasContext *ctx, uint32_t excp) @@ -271,13 +277,11 @@ static void gen_exception(DisasContext *ctx, uint32_t excp) * These are all synchronous exceptions, we set the PC back to the * faulting instruction */ - if (ctx->exception == POWERPC_EXCP_NONE) { - gen_update_nip(ctx, ctx->base.pc_next - 4); - } + gen_update_nip(ctx, ctx->cia); t0 = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, t0); tcg_temp_free_i32(t0); - ctx->exception = (excp); + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_exception_nip(DisasContext *ctx, uint32_t excp, @@ -289,7 +293,21 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp, t0 = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, t0); tcg_temp_free_i32(t0); - ctx->exception = (excp); + ctx->base.is_jmp = DISAS_NORETURN; +} + +static void gen_icount_io_start(DisasContext *ctx) +{ + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + /* + * An I/O instruction must be last in the TB. + * Chain to the next TB, and let the code from gen_tb_start + * decide if we need to return to the main loop. + * Doing this first also allows this value to be overridden. + */ + ctx->base.is_jmp = DISAS_TOO_MANY; + } } /* @@ -322,19 +340,8 @@ static uint32_t gen_prep_dbgex(DisasContext *ctx) static void gen_debug_exception(DisasContext *ctx) { - TCGv_i32 t0; - - /* - * These are all synchronous exceptions, we set the PC back to the - * faulting instruction - */ - if ((ctx->exception != POWERPC_EXCP_BRANCH) && - (ctx->exception != POWERPC_EXCP_SYNC)) { - gen_update_nip(ctx, ctx->base.pc_next); - } - t0 = tcg_const_i32(EXCP_DEBUG); - gen_helper_raise_exception(cpu_env, t0); - tcg_temp_free_i32(t0); + gen_helper_raise_exception(cpu_env, tcg_constant_i32(EXCP_DEBUG)); + ctx->base.is_jmp = DISAS_NORETURN; } static inline void gen_inval_exception(DisasContext *ctx, uint32_t error) @@ -354,18 +361,924 @@ static inline void gen_hvpriv_exception(DisasContext *ctx, uint32_t error) gen_exception_err(ctx, POWERPC_EXCP_HV_EMU, POWERPC_EXCP_PRIV | error); } -/* Stop translation */ -static inline void gen_stop_exception(DisasContext *ctx) +/*****************************************************************************/ +/* SPR READ/WRITE CALLBACKS */ + +void spr_noaccess(DisasContext *ctx, int gprn, int sprn) { - gen_update_nip(ctx, ctx->base.pc_next); - ctx->exception = POWERPC_EXCP_STOP; +#if 0 + sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); + printf("ERROR: try to access SPR %d !\n", sprn); +#endif +} + +/* #define PPC_DUMP_SPR_ACCESSES */ + +/* + * Generic callbacks: + * do nothing but store/retrieve spr value + */ +static void spr_load_dump_spr(int sprn) +{ +#ifdef PPC_DUMP_SPR_ACCESSES + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_load_dump_spr(cpu_env, t0); + tcg_temp_free_i32(t0); +#endif +} + +void spr_read_generic(DisasContext *ctx, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], sprn); + spr_load_dump_spr(sprn); +} + +static void spr_store_dump_spr(int sprn) +{ +#ifdef PPC_DUMP_SPR_ACCESSES + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_store_dump_spr(cpu_env, t0); + tcg_temp_free_i32(t0); +#endif +} + +void spr_write_generic(DisasContext *ctx, int sprn, int gprn) +{ + gen_store_spr(sprn, cpu_gpr[gprn]); + spr_store_dump_spr(sprn); +} + +#if !defined(CONFIG_USER_ONLY) +void spr_write_generic32(DisasContext *ctx, int sprn, int gprn) +{ +#ifdef TARGET_PPC64 + TCGv t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); + spr_store_dump_spr(sprn); +#else + spr_write_generic(ctx, sprn, gprn); +#endif +} + +void spr_write_clear(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + gen_load_spr(t0, sprn); + tcg_gen_neg_tl(t1, cpu_gpr[gprn]); + tcg_gen_and_tl(t0, t0, t1); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +void spr_access_nop(DisasContext *ctx, int sprn, int gprn) +{ +} + +#endif + +/* SPR common to all PowerPC */ +/* XER */ +void spr_read_xer(DisasContext *ctx, int gprn, int sprn) +{ + TCGv dst = cpu_gpr[gprn]; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + tcg_gen_mov_tl(dst, cpu_xer); + tcg_gen_shli_tl(t0, cpu_so, XER_SO); + tcg_gen_shli_tl(t1, cpu_ov, XER_OV); + tcg_gen_shli_tl(t2, cpu_ca, XER_CA); + tcg_gen_or_tl(t0, t0, t1); + tcg_gen_or_tl(dst, dst, t2); + tcg_gen_or_tl(dst, dst, t0); + if (is_isa300(ctx)) { + tcg_gen_shli_tl(t0, cpu_ov32, XER_OV32); + tcg_gen_or_tl(dst, dst, t0); + tcg_gen_shli_tl(t0, cpu_ca32, XER_CA32); + tcg_gen_or_tl(dst, dst, t0); + } + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} + +void spr_write_xer(DisasContext *ctx, int sprn, int gprn) +{ + TCGv src = cpu_gpr[gprn]; + /* Write all flags, while reading back check for isa300 */ + tcg_gen_andi_tl(cpu_xer, src, + ~((1u << XER_SO) | + (1u << XER_OV) | (1u << XER_OV32) | + (1u << XER_CA) | (1u << XER_CA32))); + tcg_gen_extract_tl(cpu_ov32, src, XER_OV32, 1); + tcg_gen_extract_tl(cpu_ca32, src, XER_CA32, 1); + tcg_gen_extract_tl(cpu_so, src, XER_SO, 1); + tcg_gen_extract_tl(cpu_ov, src, XER_OV, 1); + tcg_gen_extract_tl(cpu_ca, src, XER_CA, 1); +} + +/* LR */ +void spr_read_lr(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_mov_tl(cpu_gpr[gprn], cpu_lr); +} + +void spr_write_lr(DisasContext *ctx, int sprn, int gprn) +{ + tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]); +} + +/* CFAR */ +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +void spr_read_cfar(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar); +} + +void spr_write_cfar(DisasContext *ctx, int sprn, int gprn) +{ + tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]); +} +#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */ + +/* CTR */ +void spr_read_ctr(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_mov_tl(cpu_gpr[gprn], cpu_ctr); +} + +void spr_write_ctr(DisasContext *ctx, int sprn, int gprn) +{ + tcg_gen_mov_tl(cpu_ctr, cpu_gpr[gprn]); +} + +/* User read access to SPR */ +/* USPRx */ +/* UMMCRx */ +/* UPMCx */ +/* USIA */ +/* UDECR */ +void spr_read_ureg(DisasContext *ctx, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], sprn + 0x10); +} + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +void spr_write_ureg(DisasContext *ctx, int sprn, int gprn) +{ + gen_store_spr(sprn + 0x10, cpu_gpr[gprn]); +} +#endif + +/* SPR common to all non-embedded PowerPC */ +/* DECR */ +#if !defined(CONFIG_USER_ONLY) +void spr_read_decr(DisasContext *ctx, int gprn, int sprn) +{ + gen_icount_io_start(ctx); + gen_helper_load_decr(cpu_gpr[gprn], cpu_env); +} + +void spr_write_decr(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_decr(cpu_env, cpu_gpr[gprn]); +} +#endif + +/* SPR common to all non-embedded PowerPC, except 601 */ +/* Time base */ +void spr_read_tbl(DisasContext *ctx, int gprn, int sprn) +{ + gen_icount_io_start(ctx); + gen_helper_load_tbl(cpu_gpr[gprn], cpu_env); +} + +void spr_read_tbu(DisasContext *ctx, int gprn, int sprn) +{ + gen_icount_io_start(ctx); + gen_helper_load_tbu(cpu_gpr[gprn], cpu_env); +} + +void spr_read_atbl(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_atbl(cpu_gpr[gprn], cpu_env); +} + +void spr_read_atbu(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_atbu(cpu_gpr[gprn], cpu_env); +} + +#if !defined(CONFIG_USER_ONLY) +void spr_write_tbl(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_tbu(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_atbl(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_atbl(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_atbu(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_atbu(cpu_env, cpu_gpr[gprn]); +} + +#if defined(TARGET_PPC64) +void spr_read_purr(DisasContext *ctx, int gprn, int sprn) +{ + gen_icount_io_start(ctx); + gen_helper_load_purr(cpu_gpr[gprn], cpu_env); +} + +void spr_write_purr(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_purr(cpu_env, cpu_gpr[gprn]); +} + +/* HDECR */ +void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn) +{ + gen_icount_io_start(ctx); + gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env); +} + +void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]); +} + +void spr_read_vtb(DisasContext *ctx, int gprn, int sprn) +{ + gen_icount_io_start(ctx); + gen_helper_load_vtb(cpu_gpr[gprn], cpu_env); +} + +void spr_write_vtb(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_vtb(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_tbu40(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_tbu40(cpu_env, cpu_gpr[gprn]); +} + +#endif +#endif + +#if !defined(CONFIG_USER_ONLY) +/* IBAT0U...IBAT0U */ +/* IBAT0L...IBAT7L */ +void spr_read_ibat(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + offsetof(CPUPPCState, + IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); +} + +void spr_read_ibat_h(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + offsetof(CPUPPCState, + IBAT[sprn & 1][((sprn - SPR_IBAT4U) / 2) + 4])); +} + +void spr_write_ibatu(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); + gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +void spr_write_ibatu_h(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4); + gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +void spr_write_ibatl(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2); + gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +void spr_write_ibatl_h(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4); + gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +/* DBAT0U...DBAT7U */ +/* DBAT0L...DBAT7L */ +void spr_read_dbat(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + offsetof(CPUPPCState, + DBAT[sprn & 1][(sprn - SPR_DBAT0U) / 2])); +} + +void spr_read_dbat_h(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + offsetof(CPUPPCState, + DBAT[sprn & 1][((sprn - SPR_DBAT4U) / 2) + 4])); +} + +void spr_write_dbatu(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2); + gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +void spr_write_dbatu_h(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4); + gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +void spr_write_dbatl(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2); + gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +void spr_write_dbatl_h(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4); + gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +/* SDR1 */ +void spr_write_sdr1(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_sdr1(cpu_env, cpu_gpr[gprn]); +} + +#if defined(TARGET_PPC64) +/* 64 bits PowerPC specific SPRs */ +/* PIDR */ +void spr_write_pidr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_pidr(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_lpidr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_lpidr(cpu_env, cpu_gpr[gprn]); +} + +void spr_read_hior(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix)); +} + +void spr_write_hior(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0x3FFFFF00000ULL); + tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); + tcg_temp_free(t0); +} +void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_ptcr(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_pcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_pcr(cpu_env, cpu_gpr[gprn]); +} + +/* DPDES */ +void spr_read_dpdes(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_dpdes(cpu_gpr[gprn], cpu_env); +} + +void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_dpdes(cpu_env, cpu_gpr[gprn]); +} +#endif +#endif + +/* PowerPC 601 specific registers */ +/* RTC */ +void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_601_rtcl(cpu_gpr[gprn], cpu_env); +} + +void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_601_rtcu(cpu_gpr[gprn], cpu_env); +} + +#if !defined(CONFIG_USER_ONLY) +void spr_write_601_rtcu(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_601_rtcu(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_601_rtcl(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_601_rtcl(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_hid0_601(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_hid0_601(cpu_env, cpu_gpr[gprn]); + /* Must stop the translation as endianness may have changed */ + ctx->base.is_jmp = DISAS_EXIT_UPDATE; +} +#endif + +/* Unified bats */ +#if !defined(CONFIG_USER_ONLY) +void spr_read_601_ubat(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + offsetof(CPUPPCState, + IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); +} + +void spr_write_601_ubatu(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); + gen_helper_store_601_batl(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); + gen_helper_store_601_batu(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} +#endif + +/* PowerPC 40x specific registers */ +#if !defined(CONFIG_USER_ONLY) +void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) +{ + gen_icount_io_start(ctx); + gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env); +} + +void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_store_spr(sprn, cpu_gpr[gprn]); + gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); + /* We must stop translation as we may have rebooted */ + ctx->base.is_jmp = DISAS_EXIT_UPDATE; +} + +void spr_write_40x_sler(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_booke_tcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_booke_tsr(DisasContext *ctx, int sprn, int gprn) +{ + gen_icount_io_start(ctx); + gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]); +} +#endif + +/* PowerPC 403 specific registers */ +/* PBL1 / PBU1 / PBL2 / PBU2 */ +#if !defined(CONFIG_USER_ONLY) +void spr_read_403_pbr(DisasContext *ctx, int gprn, int sprn) +{ + tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, + offsetof(CPUPPCState, pb[sprn - SPR_403_PBL1])); +} + +void spr_write_403_pbr(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1); + gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} + +void spr_write_pir(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xF); + gen_store_spr(SPR_PIR, t0); + tcg_temp_free(t0); +} +#endif + +/* SPE specific registers */ +void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_ld_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); + tcg_gen_extu_i32_tl(cpu_gpr[gprn], t0); + tcg_temp_free_i32(t0); +} + +void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[gprn]); + tcg_gen_st_i32(t0, cpu_env, offsetof(CPUPPCState, spe_fscr)); + tcg_temp_free_i32(t0); +} + +#if !defined(CONFIG_USER_ONLY) +/* Callback used to write the exception vector base */ +void spr_write_excp_prefix(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivpr_mask)); + tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); + tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} + +void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn) +{ + int sprn_offs; + + if (sprn >= SPR_BOOKE_IVOR0 && sprn <= SPR_BOOKE_IVOR15) { + sprn_offs = sprn - SPR_BOOKE_IVOR0; + } else if (sprn >= SPR_BOOKE_IVOR32 && sprn <= SPR_BOOKE_IVOR37) { + sprn_offs = sprn - SPR_BOOKE_IVOR32 + 32; + } else if (sprn >= SPR_BOOKE_IVOR38 && sprn <= SPR_BOOKE_IVOR42) { + sprn_offs = sprn - SPR_BOOKE_IVOR38 + 38; + } else { + printf("Trying to write an unknown exception vector %d %03x\n", + sprn, sprn); + gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); + return; + } + + TCGv t0 = tcg_temp_new(); + tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUPPCState, ivor_mask)); + tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]); + tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_vectors[sprn_offs])); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} +#endif + +#ifdef TARGET_PPC64 +#ifndef CONFIG_USER_ONLY +void spr_write_amr(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* + * Note, the HV=1 PR=0 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + if (ctx->pr) { + gen_load_spr(t1, SPR_UAMOR); + } else { + gen_load_spr(t1, SPR_AMOR); + } + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_AMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_AMR, t0); + spr_store_dump_spr(SPR_AMR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); } +void spr_write_uamor(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* + * Note, the HV=1 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + gen_load_spr(t1, SPR_AMOR); + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_UAMOR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_UAMOR, t0); + spr_store_dump_spr(SPR_UAMOR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} + +void spr_write_iamr(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + /* + * Note, the HV=1 case is handled earlier by simply using + * spr_write_generic for HV mode in the SPR table + */ + + /* Build insertion mask into t1 based on context */ + gen_load_spr(t1, SPR_AMOR); + + /* Mask new bits into t2 */ + tcg_gen_and_tl(t2, t1, cpu_gpr[gprn]); + + /* Load AMR and clear new bits in t0 */ + gen_load_spr(t0, SPR_IAMR); + tcg_gen_andc_tl(t0, t0, t1); + + /* Or'in new bits and write it out */ + tcg_gen_or_tl(t0, t0, t2); + gen_store_spr(SPR_IAMR, t0); + spr_store_dump_spr(SPR_IAMR); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); +} +#endif +#endif + #ifndef CONFIG_USER_ONLY -/* No need to update nip here, as execution flow will change */ -static inline void gen_sync_exception(DisasContext *ctx) +void spr_read_thrm(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_fixup_thrm(cpu_env); + gen_load_spr(cpu_gpr[gprn], sprn); + spr_load_dump_spr(sprn); +} +#endif /* !CONFIG_USER_ONLY */ + +#if !defined(CONFIG_USER_ONLY) +void spr_write_e500_l1csr0(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, cpu_gpr[gprn], L1CSR0_DCE | L1CSR0_CPE); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} + +void spr_write_e500_l1csr1(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, cpu_gpr[gprn], L1CSR1_ICE | L1CSR1_CPE); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} + +void spr_write_e500_l2csr0(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, cpu_gpr[gprn], + ~(E500_L2CSR0_L2FI | E500_L2CSR0_L2FL | E500_L2CSR0_L2LFC)); + gen_store_spr(sprn, t0); + tcg_temp_free(t0); +} + +void spr_write_booke206_mmucsr0(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_booke206_tlbflush(cpu_env, cpu_gpr[gprn]); +} + +void spr_write_booke_pid(DisasContext *ctx, int sprn, int gprn) +{ + TCGv_i32 t0 = tcg_const_i32(sprn); + gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); + tcg_temp_free_i32(t0); +} +void spr_write_eplc(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_booke_set_eplc(cpu_env, cpu_gpr[gprn]); +} +void spr_write_epsc(DisasContext *ctx, int sprn, int gprn) { - ctx->exception = POWERPC_EXCP_SYNC; + gen_helper_booke_set_epsc(cpu_env, cpu_gpr[gprn]); +} + +#endif + +#if !defined(CONFIG_USER_ONLY) +void spr_write_mas73(DisasContext *ctx, int sprn, int gprn) +{ + TCGv val = tcg_temp_new(); + tcg_gen_ext32u_tl(val, cpu_gpr[gprn]); + gen_store_spr(SPR_BOOKE_MAS3, val); + tcg_gen_shri_tl(val, cpu_gpr[gprn], 32); + gen_store_spr(SPR_BOOKE_MAS7, val); + tcg_temp_free(val); +} + +void spr_read_mas73(DisasContext *ctx, int gprn, int sprn) +{ + TCGv mas7 = tcg_temp_new(); + TCGv mas3 = tcg_temp_new(); + gen_load_spr(mas7, SPR_BOOKE_MAS7); + tcg_gen_shli_tl(mas7, mas7, 32); + gen_load_spr(mas3, SPR_BOOKE_MAS3); + tcg_gen_or_tl(cpu_gpr[gprn], mas3, mas7); + tcg_temp_free(mas3); + tcg_temp_free(mas7); +} + +#endif + +#ifdef TARGET_PPC64 +static void gen_fscr_facility_check(DisasContext *ctx, int facility_sprn, + int bit, int sprn, int cause) +{ + TCGv_i32 t1 = tcg_const_i32(bit); + TCGv_i32 t2 = tcg_const_i32(sprn); + TCGv_i32 t3 = tcg_const_i32(cause); + + gen_helper_fscr_facility_check(cpu_env, t1, t2, t3); + + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +static void gen_msr_facility_check(DisasContext *ctx, int facility_sprn, + int bit, int sprn, int cause) +{ + TCGv_i32 t1 = tcg_const_i32(bit); + TCGv_i32 t2 = tcg_const_i32(sprn); + TCGv_i32 t3 = tcg_const_i32(cause); + + gen_helper_msr_facility_check(cpu_env, t1, t2, t3); + + tcg_temp_free_i32(t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t1); +} + +void spr_read_prev_upper32(DisasContext *ctx, int gprn, int sprn) +{ + TCGv spr_up = tcg_temp_new(); + TCGv spr = tcg_temp_new(); + + gen_load_spr(spr, sprn - 1); + tcg_gen_shri_tl(spr_up, spr, 32); + tcg_gen_ext32u_tl(cpu_gpr[gprn], spr_up); + + tcg_temp_free(spr); + tcg_temp_free(spr_up); +} + +void spr_write_prev_upper32(DisasContext *ctx, int sprn, int gprn) +{ + TCGv spr = tcg_temp_new(); + + gen_load_spr(spr, sprn - 1); + tcg_gen_deposit_tl(spr, spr, cpu_gpr[gprn], 32, 32); + gen_store_spr(sprn - 1, spr); + + tcg_temp_free(spr); +} + +#if !defined(CONFIG_USER_ONLY) +void spr_write_hmer(DisasContext *ctx, int sprn, int gprn) +{ + TCGv hmer = tcg_temp_new(); + + gen_load_spr(hmer, sprn); + tcg_gen_and_tl(hmer, cpu_gpr[gprn], hmer); + gen_store_spr(sprn, hmer); + spr_store_dump_spr(sprn); + tcg_temp_free(hmer); +} + +void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_lpcr(cpu_env, cpu_gpr[gprn]); +} +#endif /* !defined(CONFIG_USER_ONLY) */ + +void spr_read_tar(DisasContext *ctx, int gprn, int sprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); + spr_read_generic(ctx, gprn, sprn); +} + +void spr_write_tar(DisasContext *ctx, int sprn, int gprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_TAR, sprn, FSCR_IC_TAR); + spr_write_generic(ctx, sprn, gprn); +} + +void spr_read_tm(DisasContext *ctx, int gprn, int sprn) +{ + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_read_generic(ctx, gprn, sprn); +} + +void spr_write_tm(DisasContext *ctx, int sprn, int gprn) +{ + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_write_generic(ctx, sprn, gprn); +} + +void spr_read_tm_upper32(DisasContext *ctx, int gprn, int sprn) +{ + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_read_prev_upper32(ctx, gprn, sprn); +} + +void spr_write_tm_upper32(DisasContext *ctx, int sprn, int gprn) +{ + gen_msr_facility_check(ctx, SPR_FSCR, MSR_TM, sprn, FSCR_IC_TM); + spr_write_prev_upper32(ctx, sprn, gprn); +} + +void spr_read_ebb(DisasContext *ctx, int gprn, int sprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_read_generic(ctx, gprn, sprn); +} + +void spr_write_ebb(DisasContext *ctx, int sprn, int gprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_write_generic(ctx, sprn, gprn); +} + +void spr_read_ebb_upper32(DisasContext *ctx, int gprn, int sprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_read_prev_upper32(ctx, gprn, sprn); +} + +void spr_write_ebb_upper32(DisasContext *ctx, int sprn, int gprn) +{ + gen_fscr_facility_check(ctx, SPR_FSCR, FSCR_EBB, sprn, FSCR_IC_EBB); + spr_write_prev_upper32(ctx, sprn, gprn); } #endif @@ -1851,18 +2764,13 @@ static void gen_darn(DisasContext *ctx) if (l > 2) { tcg_gen_movi_i64(cpu_gpr[rD(ctx->opcode)], -1); } else { - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + gen_icount_io_start(ctx); if (l == 0) { gen_helper_darn32(cpu_gpr[rD(ctx->opcode)]); } else { /* Return 64-bit random for both CRN and RRN */ gen_helper_darn64(cpu_gpr[rD(ctx->opcode)]); } - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_stop_exception(ctx); - } } } #endif @@ -3112,7 +4020,7 @@ static void gen_eieio(DisasContext *ctx) */ if (!(ctx->insns_flags2 & PPC2_ISA300)) { qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" - TARGET_FMT_lx "\n", ctx->base.pc_next - 4); + TARGET_FMT_lx "\n", ctx->cia); } else { bar = TCG_MO_ST_LD; } @@ -3157,7 +4065,7 @@ static void gen_isync(DisasContext *ctx) gen_check_tlb_flush(ctx, false); } tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - gen_stop_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT_UPDATE; } #define MEMOP_GET_SIZE(x) (1 << ((x) & MO_SIZE)) @@ -3740,8 +4648,9 @@ static void gen_lookup_and_goto_ptr(DisasContext *ctx) } else if (sse & (CPU_SINGLE_STEP | CPU_BRANCH_STEP)) { uint32_t excp = gen_prep_dbgex(ctx); gen_exception(ctx, excp); + } else { + tcg_gen_exit_tb(NULL, 0); } - tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -3776,20 +4685,20 @@ static void gen_b(DisasContext *ctx) { target_ulong li, target; - ctx->exception = POWERPC_EXCP_BRANCH; /* sign extend LI */ li = LI(ctx->opcode); li = (li ^ 0x02000000) - 0x02000000; if (likely(AA(ctx->opcode) == 0)) { - target = ctx->base.pc_next + li - 4; + target = ctx->cia + li; } else { target = li; } if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->base.pc_next); } - gen_update_cfar(ctx, ctx->base.pc_next - 4); + gen_update_cfar(ctx, ctx->cia); gen_goto_tb(ctx, 0, target); + ctx->base.is_jmp = DISAS_NORETURN; } #define BCOND_IM 0 @@ -3802,7 +4711,6 @@ static void gen_bcond(DisasContext *ctx, int type) uint32_t bo = BO(ctx->opcode); TCGLabel *l1; TCGv target; - ctx->exception = POWERPC_EXCP_BRANCH; if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { target = tcg_temp_local_new(); @@ -3887,11 +4795,11 @@ static void gen_bcond(DisasContext *ctx, int type) } tcg_temp_free_i32(temp); } - gen_update_cfar(ctx, ctx->base.pc_next - 4); + gen_update_cfar(ctx, ctx->cia); if (type == BCOND_IM) { target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); if (likely(AA(ctx->opcode) == 0)) { - gen_goto_tb(ctx, 0, ctx->base.pc_next + li - 4); + gen_goto_tb(ctx, 0, ctx->cia + li); } else { gen_goto_tb(ctx, 0, li); } @@ -3909,6 +4817,7 @@ static void gen_bcond(DisasContext *ctx, int type) gen_set_label(l1); gen_goto_tb(ctx, 1, ctx->base.pc_next); } + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_bc(DisasContext *ctx) @@ -4004,12 +4913,10 @@ static void gen_rfi(DisasContext *ctx) } /* Restore CPU state */ CHK_SV; - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_update_cfar(ctx, ctx->base.pc_next - 4); + gen_icount_io_start(ctx); + gen_update_cfar(ctx, ctx->cia); gen_helper_rfi(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4021,12 +4928,10 @@ static void gen_rfid(DisasContext *ctx) #else /* Restore CPU state */ CHK_SV; - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_update_cfar(ctx, ctx->base.pc_next - 4); + gen_icount_io_start(ctx); + gen_update_cfar(ctx, ctx->cia); gen_helper_rfid(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4038,12 +4943,10 @@ static void gen_rfscv(DisasContext *ctx) #else /* Restore CPU state */ CHK_SV; - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_update_cfar(ctx, ctx->base.pc_next - 4); + gen_icount_io_start(ctx); + gen_update_cfar(ctx, ctx->cia); gen_helper_rfscv(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif } #endif @@ -4056,7 +4959,7 @@ static void gen_hrfid(DisasContext *ctx) /* Restore CPU state */ CHK_HV; gen_helper_hrfid(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif } #endif @@ -4083,13 +4986,10 @@ static void gen_scv(DisasContext *ctx) uint32_t lev = (ctx->opcode >> 5) & 0x7F; /* Set the PC back to the faulting instruction. */ - if (ctx->exception == POWERPC_EXCP_NONE) { - gen_update_nip(ctx, ctx->base.pc_next - 4); - } + gen_update_nip(ctx, ctx->cia); gen_helper_scv(cpu_env, tcg_constant_i32(lev)); - /* This need not be exact, just not POWERPC_EXCP_NONE */ - ctx->exception = POWERPC_SYSCALL_VECTORED; + ctx->base.is_jmp = DISAS_NORETURN; } #endif #endif @@ -4175,43 +5075,6 @@ static void gen_tdi(DisasContext *ctx) /*** Processor control ***/ -static void gen_read_xer(DisasContext *ctx, TCGv dst) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - tcg_gen_mov_tl(dst, cpu_xer); - tcg_gen_shli_tl(t0, cpu_so, XER_SO); - tcg_gen_shli_tl(t1, cpu_ov, XER_OV); - tcg_gen_shli_tl(t2, cpu_ca, XER_CA); - tcg_gen_or_tl(t0, t0, t1); - tcg_gen_or_tl(dst, dst, t2); - tcg_gen_or_tl(dst, dst, t0); - if (is_isa300(ctx)) { - tcg_gen_shli_tl(t0, cpu_ov32, XER_OV32); - tcg_gen_or_tl(dst, dst, t0); - tcg_gen_shli_tl(t0, cpu_ca32, XER_CA32); - tcg_gen_or_tl(dst, dst, t0); - } - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); -} - -static void gen_write_xer(TCGv src) -{ - /* Write all flags, while reading back check for isa300 */ - tcg_gen_andi_tl(cpu_xer, src, - ~((1u << XER_SO) | - (1u << XER_OV) | (1u << XER_OV32) | - (1u << XER_CA) | (1u << XER_CA32))); - tcg_gen_extract_tl(cpu_ov32, src, XER_OV32, 1); - tcg_gen_extract_tl(cpu_ca32, src, XER_CA32, 1); - tcg_gen_extract_tl(cpu_so, src, XER_SO, 1); - tcg_gen_extract_tl(cpu_ov, src, XER_OV, 1); - tcg_gen_extract_tl(cpu_ca, src, XER_CA, 1); -} - /* mcrxr */ static void gen_mcrxr(DisasContext *ctx) { @@ -4299,15 +5162,6 @@ static void gen_mfmsr(DisasContext *ctx) tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_msr); } -static void spr_noaccess(DisasContext *ctx, int gprn, int sprn) -{ -#if 0 - sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); - printf("ERROR: try to access SPR %d !\n", sprn); -#endif -} -#define SPR_NOACCESS (&spr_noaccess) - /* mfspr */ static inline void gen_op_mfspr(DisasContext *ctx) { @@ -4338,7 +5192,7 @@ static inline void gen_op_mfspr(DisasContext *ctx) if (sprn != SPR_PVR) { qemu_log_mask(LOG_GUEST_ERROR, "Trying to read privileged spr " "%d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, - ctx->base.pc_next - 4); + ctx->cia); } gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } @@ -4352,7 +5206,7 @@ static inline void gen_op_mfspr(DisasContext *ctx) /* Not defined */ qemu_log_mask(LOG_GUEST_ERROR, "Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); + TARGET_FMT_lx "\n", sprn, sprn, ctx->cia); /* * The behaviour depends on MSR:PR and SPR# bit 0x10, it can @@ -4416,9 +5270,7 @@ static void gen_mtmsrd(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + gen_icount_io_start(ctx); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ TCGv t0 = tcg_temp_new(); @@ -4443,7 +5295,7 @@ static void gen_mtmsrd(DisasContext *ctx) gen_helper_store_msr(cpu_env, cpu_gpr[rS(ctx->opcode)]); } /* Must stop the translation as machine state (may have) changed */ - gen_stop_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT_UPDATE; #endif /* !defined(CONFIG_USER_ONLY) */ } #endif /* defined(TARGET_PPC64) */ @@ -4453,9 +5305,7 @@ static void gen_mtmsr(DisasContext *ctx) CHK_SV; #if !defined(CONFIG_USER_ONLY) - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } + gen_icount_io_start(ctx); if (ctx->opcode & 0x00010000) { /* L=1 form only updates EE and RI */ TCGv t0 = tcg_temp_new(); @@ -4488,7 +5338,7 @@ static void gen_mtmsr(DisasContext *ctx) tcg_temp_free(msr); } /* Must stop the translation as machine state (may have) changed */ - gen_stop_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT_UPDATE; #endif } @@ -4516,7 +5366,7 @@ static void gen_mtspr(DisasContext *ctx) /* Privilege exception */ qemu_log_mask(LOG_GUEST_ERROR, "Trying to write privileged spr " "%d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, - ctx->base.pc_next - 4); + ctx->cia); gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { @@ -4530,7 +5380,7 @@ static void gen_mtspr(DisasContext *ctx) /* Not defined */ qemu_log_mask(LOG_GUEST_ERROR, "Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); + TARGET_FMT_lx "\n", sprn, sprn, ctx->cia); /* @@ -5943,7 +6793,7 @@ static void gen_rfsvc(DisasContext *ctx) CHK_SV; gen_helper_rfsvc(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6323,7 +7173,7 @@ static void gen_rfci_40x(DisasContext *ctx) CHK_SV; /* Restore CPU state */ gen_helper_40x_rfci(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6335,7 +7185,7 @@ static void gen_rfci(DisasContext *ctx) CHK_SV; /* Restore CPU state */ gen_helper_rfci(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6350,7 +7200,7 @@ static void gen_rfdi(DisasContext *ctx) CHK_SV; /* Restore CPU state */ gen_helper_rfdi(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6363,7 +7213,7 @@ static void gen_rfmci(DisasContext *ctx) CHK_SV; /* Restore CPU state */ gen_helper_rfmci(cpu_env); - gen_sync_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6625,7 +7475,7 @@ static void gen_wrtee(DisasContext *ctx) * Stop translation to have a chance to raise an exception if we * just set msr_ee to 1 */ - gen_stop_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT_UPDATE; #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6639,7 +7489,7 @@ static void gen_wrteei(DisasContext *ctx) if (ctx->opcode & 0x00008000) { tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE)); /* Stop translation to have a chance to raise an exception */ - gen_stop_exception(ctx); + ctx->base.is_jmp = DISAS_EXIT_UPDATE; } else { tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); } @@ -7638,194 +8488,6 @@ GEN_HANDLER2_E(trechkpt, "trechkpt", 0x1F, 0x0E, 0x1F, 0x03FFF800, \ #include "translate/spe-ops.c.inc" }; -#include "helper_regs.h" -#include "translate_init.c.inc" - -/*****************************************************************************/ -/* Misc PowerPC helpers */ -void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) -{ -#define RGPL 4 -#define RFPL 4 - - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - int i; - - qemu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " - TARGET_FMT_lx " XER " TARGET_FMT_lx " CPU#%d\n", - env->nip, env->lr, env->ctr, cpu_read_xer(env), - cs->cpu_index); - qemu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " - "%08x iidx %d didx %d\n", - env->msr, env->spr[SPR_HID0], env->hflags, - cpu_mmu_index(env, true), cpu_mmu_index(env, false)); -#if !defined(NO_TIMER_DUMP) - qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 -#if !defined(CONFIG_USER_ONLY) - " DECR " TARGET_FMT_lu -#endif - "\n", - cpu_ppc_load_tbu(env), cpu_ppc_load_tbl(env) -#if !defined(CONFIG_USER_ONLY) - , cpu_ppc_load_decr(env) -#endif - ); -#endif - for (i = 0; i < 32; i++) { - if ((i & (RGPL - 1)) == 0) { - qemu_fprintf(f, "GPR%02d", i); - } - qemu_fprintf(f, " %016" PRIx64, ppc_dump_gpr(env, i)); - if ((i & (RGPL - 1)) == (RGPL - 1)) { - qemu_fprintf(f, "\n"); - } - } - qemu_fprintf(f, "CR "); - for (i = 0; i < 8; i++) - qemu_fprintf(f, "%01x", env->crf[i]); - qemu_fprintf(f, " ["); - for (i = 0; i < 8; i++) { - char a = '-'; - if (env->crf[i] & 0x08) { - a = 'L'; - } else if (env->crf[i] & 0x04) { - a = 'G'; - } else if (env->crf[i] & 0x02) { - a = 'E'; - } - qemu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' '); - } - qemu_fprintf(f, " ] RES " TARGET_FMT_lx "\n", - env->reserve_addr); - - if (flags & CPU_DUMP_FPU) { - for (i = 0; i < 32; i++) { - if ((i & (RFPL - 1)) == 0) { - qemu_fprintf(f, "FPR%02d", i); - } - qemu_fprintf(f, " %016" PRIx64, *cpu_fpr_ptr(env, i)); - if ((i & (RFPL - 1)) == (RFPL - 1)) { - qemu_fprintf(f, "\n"); - } - } - qemu_fprintf(f, "FPSCR " TARGET_FMT_lx "\n", env->fpscr); - } - -#if !defined(CONFIG_USER_ONLY) - qemu_fprintf(f, " SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx - " PVR " TARGET_FMT_lx " VRSAVE " TARGET_FMT_lx "\n", - env->spr[SPR_SRR0], env->spr[SPR_SRR1], - env->spr[SPR_PVR], env->spr[SPR_VRSAVE]); - - qemu_fprintf(f, "SPRG0 " TARGET_FMT_lx " SPRG1 " TARGET_FMT_lx - " SPRG2 " TARGET_FMT_lx " SPRG3 " TARGET_FMT_lx "\n", - env->spr[SPR_SPRG0], env->spr[SPR_SPRG1], - env->spr[SPR_SPRG2], env->spr[SPR_SPRG3]); - - qemu_fprintf(f, "SPRG4 " TARGET_FMT_lx " SPRG5 " TARGET_FMT_lx - " SPRG6 " TARGET_FMT_lx " SPRG7 " TARGET_FMT_lx "\n", - env->spr[SPR_SPRG4], env->spr[SPR_SPRG5], - env->spr[SPR_SPRG6], env->spr[SPR_SPRG7]); - -#if defined(TARGET_PPC64) - if (env->excp_model == POWERPC_EXCP_POWER7 || - env->excp_model == POWERPC_EXCP_POWER8 || - env->excp_model == POWERPC_EXCP_POWER9 || - env->excp_model == POWERPC_EXCP_POWER10) { - qemu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n", - env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); - } -#endif - if (env->excp_model == POWERPC_EXCP_BOOKE) { - qemu_fprintf(f, "CSRR0 " TARGET_FMT_lx " CSRR1 " TARGET_FMT_lx - " MCSRR0 " TARGET_FMT_lx " MCSRR1 " TARGET_FMT_lx "\n", - env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1], - env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); - - qemu_fprintf(f, " TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx - " ESR " TARGET_FMT_lx " DEAR " TARGET_FMT_lx "\n", - env->spr[SPR_BOOKE_TCR], env->spr[SPR_BOOKE_TSR], - env->spr[SPR_BOOKE_ESR], env->spr[SPR_BOOKE_DEAR]); - - qemu_fprintf(f, " PIR " TARGET_FMT_lx " DECAR " TARGET_FMT_lx - " IVPR " TARGET_FMT_lx " EPCR " TARGET_FMT_lx "\n", - env->spr[SPR_BOOKE_PIR], env->spr[SPR_BOOKE_DECAR], - env->spr[SPR_BOOKE_IVPR], env->spr[SPR_BOOKE_EPCR]); - - qemu_fprintf(f, " MCSR " TARGET_FMT_lx " SPRG8 " TARGET_FMT_lx - " EPR " TARGET_FMT_lx "\n", - env->spr[SPR_BOOKE_MCSR], env->spr[SPR_BOOKE_SPRG8], - env->spr[SPR_BOOKE_EPR]); - - /* FSL-specific */ - qemu_fprintf(f, " MCAR " TARGET_FMT_lx " PID1 " TARGET_FMT_lx - " PID2 " TARGET_FMT_lx " SVR " TARGET_FMT_lx "\n", - env->spr[SPR_Exxx_MCAR], env->spr[SPR_BOOKE_PID1], - env->spr[SPR_BOOKE_PID2], env->spr[SPR_E500_SVR]); - - /* - * IVORs are left out as they are large and do not change often -- - * they can be read with "p $ivor0", "p $ivor1", etc. - */ - } - -#if defined(TARGET_PPC64) - if (env->flags & POWERPC_FLAG_CFAR) { - qemu_fprintf(f, " CFAR " TARGET_FMT_lx"\n", env->cfar); - } -#endif - - if (env->spr_cb[SPR_LPCR].name) { - qemu_fprintf(f, " LPCR " TARGET_FMT_lx "\n", env->spr[SPR_LPCR]); - } - - switch (env->mmu_model) { - case POWERPC_MMU_32B: - case POWERPC_MMU_601: - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_74xx: -#if defined(TARGET_PPC64) - case POWERPC_MMU_64B: - case POWERPC_MMU_2_03: - case POWERPC_MMU_2_06: - case POWERPC_MMU_2_07: - case POWERPC_MMU_3_00: -#endif - if (env->spr_cb[SPR_SDR1].name) { /* SDR1 Exists */ - qemu_fprintf(f, " SDR1 " TARGET_FMT_lx " ", env->spr[SPR_SDR1]); - } - if (env->spr_cb[SPR_PTCR].name) { /* PTCR Exists */ - qemu_fprintf(f, " PTCR " TARGET_FMT_lx " ", env->spr[SPR_PTCR]); - } - qemu_fprintf(f, " DAR " TARGET_FMT_lx " DSISR " TARGET_FMT_lx "\n", - env->spr[SPR_DAR], env->spr[SPR_DSISR]); - break; - case POWERPC_MMU_BOOKE206: - qemu_fprintf(f, " MAS0 " TARGET_FMT_lx " MAS1 " TARGET_FMT_lx - " MAS2 " TARGET_FMT_lx " MAS3 " TARGET_FMT_lx "\n", - env->spr[SPR_BOOKE_MAS0], env->spr[SPR_BOOKE_MAS1], - env->spr[SPR_BOOKE_MAS2], env->spr[SPR_BOOKE_MAS3]); - - qemu_fprintf(f, " MAS4 " TARGET_FMT_lx " MAS6 " TARGET_FMT_lx - " MAS7 " TARGET_FMT_lx " PID " TARGET_FMT_lx "\n", - env->spr[SPR_BOOKE_MAS4], env->spr[SPR_BOOKE_MAS6], - env->spr[SPR_BOOKE_MAS7], env->spr[SPR_BOOKE_PID]); - - qemu_fprintf(f, "MMUCFG " TARGET_FMT_lx " TLB0CFG " TARGET_FMT_lx - " TLB1CFG " TARGET_FMT_lx "\n", - env->spr[SPR_MMUCFG], env->spr[SPR_BOOKE_TLB0CFG], - env->spr[SPR_BOOKE_TLB1CFG]); - break; - default: - break; - } -#endif - -#undef RGPL -#undef RFPL -} - /*****************************************************************************/ /* Opcode types */ enum { @@ -8270,14 +8932,68 @@ void ppc_cpu_dump_statistics(CPUState *cs, int flags) #endif } +static bool decode_legacy(PowerPCCPU *cpu, DisasContext *ctx, uint32_t insn) +{ + opc_handler_t **table, *handler; + uint32_t inval; + + ctx->opcode = insn; + + LOG_DISAS("translate opcode %08x (%02x %02x %02x %02x) (%s)\n", + insn, opc1(insn), opc2(insn), opc3(insn), opc4(insn), + ctx->le_mode ? "little" : "big"); + + table = cpu->opcodes; + handler = table[opc1(insn)]; + if (is_indirect_opcode(handler)) { + table = ind_table(handler); + handler = table[opc2(insn)]; + if (is_indirect_opcode(handler)) { + table = ind_table(handler); + handler = table[opc3(insn)]; + if (is_indirect_opcode(handler)) { + table = ind_table(handler); + handler = table[opc4(insn)]; + } + } + } + + /* Is opcode *REALLY* valid ? */ + if (unlikely(handler->handler == &gen_invalid)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid/unsupported opcode: " + "%02x - %02x - %02x - %02x (%08x) " + TARGET_FMT_lx "\n", + opc1(insn), opc2(insn), opc3(insn), opc4(insn), + insn, ctx->cia); + return false; + } + + if (unlikely(handler->type & (PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE) + && Rc(insn))) { + inval = handler->inval2; + } else { + inval = handler->inval1; + } + + if (unlikely((insn & inval) != 0)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid bits: %08x for opcode: " + "%02x - %02x - %02x - %02x (%08x) " + TARGET_FMT_lx "\n", insn & inval, + opc1(insn), opc2(insn), opc3(insn), opc4(insn), + insn, ctx->cia); + return false; + } + + handler->handler(ctx); + return true; +} + static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUPPCState *env = cs->env_ptr; uint32_t hflags = ctx->base.tb->flags; - int bound; - ctx->exception = POWERPC_EXCP_NONE; ctx->spr_cb = env->spr_cb; ctx->pr = (hflags >> HFLAGS_PR) & 1; ctx->mem_idx = (hflags >> HFLAGS_DMMU_IDX) & 7; @@ -8316,8 +9032,12 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP; } - bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; - ctx->base.max_insns = MIN(ctx->base.max_insns, bound); + if (ctx->singlestep_enabled & (CPU_SINGLE_STEP | GDBSTUB_SINGLE_STEP)) { + ctx->base.max_insns = 1; + } else { + int bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); + } } static void ppc_tr_tb_start(DisasContextBase *db, CPUState *cs) @@ -8334,8 +9054,8 @@ static bool ppc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, { DisasContext *ctx = container_of(dcbase, DisasContext, base); + gen_update_nip(ctx, ctx->base.pc_next); gen_debug_exception(ctx); - dcbase->is_jmp = DISAS_NORETURN; /* * The address covered by the breakpoint must be included in * [tb->pc, tb->pc + tb->size) in order to for it to be properly @@ -8351,100 +9071,93 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) DisasContext *ctx = container_of(dcbase, DisasContext, base); PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = cs->env_ptr; - opc_handler_t **table, *handler; + uint32_t insn; + bool ok; LOG_DISAS("----------------\n"); LOG_DISAS("nip=" TARGET_FMT_lx " super=%d ir=%d\n", ctx->base.pc_next, ctx->mem_idx, (int)msr_ir); - ctx->opcode = translator_ldl_swap(env, ctx->base.pc_next, - need_byteswap(ctx)); - - LOG_DISAS("translate opcode %08x (%02x %02x %02x %02x) (%s)\n", - ctx->opcode, opc1(ctx->opcode), opc2(ctx->opcode), - opc3(ctx->opcode), opc4(ctx->opcode), - ctx->le_mode ? "little" : "big"); + ctx->cia = ctx->base.pc_next; + insn = translator_ldl_swap(env, ctx->base.pc_next, need_byteswap(ctx)); ctx->base.pc_next += 4; - table = cpu->opcodes; - handler = table[opc1(ctx->opcode)]; - if (is_indirect_opcode(handler)) { - table = ind_table(handler); - handler = table[opc2(ctx->opcode)]; - if (is_indirect_opcode(handler)) { - table = ind_table(handler); - handler = table[opc3(ctx->opcode)]; - if (is_indirect_opcode(handler)) { - table = ind_table(handler); - handler = table[opc4(ctx->opcode)]; - } - } - } - /* Is opcode *REALLY* valid ? */ - if (unlikely(handler->handler == &gen_invalid)) { - qemu_log_mask(LOG_GUEST_ERROR, "invalid/unsupported opcode: " - "%02x - %02x - %02x - %02x (%08x) " - TARGET_FMT_lx " %d\n", - opc1(ctx->opcode), opc2(ctx->opcode), - opc3(ctx->opcode), opc4(ctx->opcode), - ctx->opcode, ctx->base.pc_next - 4, (int)msr_ir); - } else { - uint32_t inval; - if (unlikely(handler->type & (PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE) - && Rc(ctx->opcode))) { - inval = handler->inval2; - } else { - inval = handler->inval1; - } - - if (unlikely((ctx->opcode & inval) != 0)) { - qemu_log_mask(LOG_GUEST_ERROR, "invalid bits: %08x for opcode: " - "%02x - %02x - %02x - %02x (%08x) " - TARGET_FMT_lx "\n", ctx->opcode & inval, - opc1(ctx->opcode), opc2(ctx->opcode), - opc3(ctx->opcode), opc4(ctx->opcode), - ctx->opcode, ctx->base.pc_next - 4); - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - ctx->base.is_jmp = DISAS_NORETURN; - return; - } + ok = decode_legacy(cpu, ctx, insn); + if (!ok) { + gen_invalid(ctx); } - (*(handler->handler))(ctx); + #if defined(DO_PPC_STATISTICS) handler->count++; #endif - /* Check trace mode exceptions */ - if (unlikely(ctx->singlestep_enabled & CPU_SINGLE_STEP && - (ctx->base.pc_next <= 0x100 || ctx->base.pc_next > 0xF00) && - ctx->exception != POWERPC_SYSCALL && - ctx->exception != POWERPC_EXCP_TRAP && - ctx->exception != POWERPC_EXCP_BRANCH)) { - uint32_t excp = gen_prep_dbgex(ctx); - gen_exception_nip(ctx, excp, ctx->base.pc_next); - } - - if (tcg_check_temp_count()) { - qemu_log("Opcode %02x %02x %02x %02x (%08x) leaked " - "temporaries\n", opc1(ctx->opcode), opc2(ctx->opcode), - opc3(ctx->opcode), opc4(ctx->opcode), ctx->opcode); - } - ctx->base.is_jmp = ctx->exception == POWERPC_EXCP_NONE ? - DISAS_NEXT : DISAS_NORETURN; + translator_loop_temp_check(&ctx->base); } static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + DisasJumpType is_jmp = ctx->base.is_jmp; + target_ulong nip = ctx->base.pc_next; + int sse; + + if (is_jmp == DISAS_NORETURN) { + /* We have already exited the TB. */ + return; + } + + /* Honor single stepping. */ + sse = ctx->singlestep_enabled & (CPU_SINGLE_STEP | GDBSTUB_SINGLE_STEP); + if (unlikely(sse)) { + switch (is_jmp) { + case DISAS_TOO_MANY: + case DISAS_EXIT_UPDATE: + case DISAS_CHAIN_UPDATE: + gen_update_nip(ctx, nip); + break; + case DISAS_EXIT: + case DISAS_CHAIN: + break; + default: + g_assert_not_reached(); + } - if (ctx->exception == POWERPC_EXCP_NONE) { - gen_goto_tb(ctx, 0, ctx->base.pc_next); - } else if (ctx->exception != POWERPC_EXCP_BRANCH) { - if (unlikely(ctx->base.singlestep_enabled)) { + if (sse & GDBSTUB_SINGLE_STEP) { gen_debug_exception(ctx); + return; + } + /* else CPU_SINGLE_STEP... */ + if (nip <= 0x100 || nip > 0xf00) { + gen_exception(ctx, gen_prep_dbgex(ctx)); + return; } - /* Generate the return instruction */ + } + + switch (is_jmp) { + case DISAS_TOO_MANY: + if (use_goto_tb(ctx, nip)) { + tcg_gen_goto_tb(0); + gen_update_nip(ctx, nip); + tcg_gen_exit_tb(ctx->base.tb, 0); + break; + } + /* fall through */ + case DISAS_CHAIN_UPDATE: + gen_update_nip(ctx, nip); + /* fall through */ + case DISAS_CHAIN: + tcg_gen_lookup_and_goto_ptr(); + break; + + case DISAS_EXIT_UPDATE: + gen_update_nip(ctx, nip); + /* fall through */ + case DISAS_EXIT: tcg_gen_exit_tb(NULL, 0); + break; + + default: + g_assert_not_reached(); } } diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index b817d31260..57a7f73bba 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -139,7 +139,7 @@ static void gen_lxvwsx(DisasContext *ctx) gen_addr_reg_index(ctx, EA); data = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(data, EA, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_ld_i32(data, EA, ctx->mem_idx, DEF_MEMOP(MO_UL)); tcg_gen_gvec_dup_i32(MO_UL, vsr_full_offset(xT(ctx->opcode)), 16, 16, data); tcg_temp_free(EA); @@ -162,7 +162,7 @@ static void gen_lxvdsx(DisasContext *ctx) gen_addr_reg_index(ctx, EA); data = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(data, EA, ctx->mem_idx, MO_TEQ); + tcg_gen_qemu_ld_i64(data, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); tcg_gen_gvec_dup_i64(MO_Q, vsr_full_offset(xT(ctx->opcode)), 16, 16, data); tcg_temp_free(EA); diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 9f464cb92c..ff5d732889 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -140,25 +140,46 @@ docker-image-debian-mips-cross: docker-image-debian10 docker-image-debian-mips64-cross: docker-image-debian10 docker-image-debian-mips64el-cross: docker-image-debian10 docker-image-debian-mipsel-cross: docker-image-debian10 -docker-image-debian-powerpc-cross: docker-image-debian10 -docker-image-debian-ppc64-cross: docker-image-debian10 docker-image-debian-ppc64el-cross: docker-image-debian10 docker-image-debian-riscv64-cross: docker-image-debian10 docker-image-debian-s390x-cross: docker-image-debian10 docker-image-debian-sh4-cross: docker-image-debian10 docker-image-debian-sparc64-cross: docker-image-debian10 +# +# The build rule for hexagon-cross is special in so far for most of +# the time we don't want to build it. While dockers caching does avoid +# this most of the time sometimes we want to force the issue. +# +docker-image-debian-hexagon-cross: $(DOCKER_FILES_DIR)/debian-hexagon-cross.docker + $(if $(NOCACHE), \ + $(call quiet-command, \ + $(DOCKER_SCRIPT) build -t qemu/debian-hexagon-cross -f $< \ + $(if $V,,--quiet) --no-cache \ + --registry $(DOCKER_REGISTRY) --extra-files \ + $(DOCKER_FILES_DIR)/debian-hexagon-cross.docker.d/build-toolchain.sh, \ + "BUILD", "debian-hexagon-cross"), \ + $(call quiet-command, \ + $(DOCKER_SCRIPT) fetch $(if $V,,--quiet) \ + qemu/debian-hexagon-cross $(DOCKER_REGISTRY), \ + "FETCH", "debian-hexagon-cross") \ + $(call quiet-command, \ + $(DOCKER_SCRIPT) update $(if $V,,--quiet) \ + qemu/debian-hexagon-cross --add-current-user, \ + "PREPARE", "debian-hexagon-cross")) + # Specialist build images, sometimes very limited tools docker-image-debian-tricore-cross: docker-image-debian10 docker-image-debian-all-test-cross: docker-image-debian10 docker-image-debian-arm64-test-cross: docker-image-debian11 +docker-image-debian-powerpc-test-cross: docker-image-debian11 # These images may be good enough for building tests but not for test builds DOCKER_PARTIAL_IMAGES += debian-alpha-cross DOCKER_PARTIAL_IMAGES += debian-arm64-test-cross +DOCKER_PARTIAL_IMAGES += debian-powerpc-test-cross DOCKER_PARTIAL_IMAGES += debian-hppa-cross DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross -DOCKER_PARTIAL_IMAGES += debian-powerpc-cross debian-ppc64-cross DOCKER_PARTIAL_IMAGES += debian-riscv64-cross DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross DOCKER_PARTIAL_IMAGES += debian-tricore-cross @@ -248,7 +269,7 @@ docker-run: docker-qemu-src $(if $(EXECUTABLE), \ $(call quiet-command, \ $(DOCKER_SCRIPT) update \ - $(IMAGE) $(EXECUTABLE), \ + $(IMAGE) --executable $(EXECUTABLE), \ " COPYING $(EXECUTABLE) to $(IMAGE)")) $(call quiet-command, \ $(DOCKER_SCRIPT) run \ diff --git a/tests/docker/docker.py b/tests/docker/docker.py index d28df4c140..4d9bb7c7ed 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -515,16 +515,36 @@ class BuildCommand(SubCommand): return 0 +class FetchCommand(SubCommand): + """ Fetch a docker image from the registry. Args: <tag> <registry>""" + name = "fetch" + + def args(self, parser): + parser.add_argument("tag", + help="Local tag for image") + parser.add_argument("registry", + help="Docker registry") + + def run(self, args, argv): + dkr = Docker() + dkr.command(cmd="pull", quiet=args.quiet, + argv=["%s/%s" % (args.registry, args.tag)]) + dkr.command(cmd="tag", quiet=args.quiet, + argv=["%s/%s" % (args.registry, args.tag), args.tag]) + class UpdateCommand(SubCommand): - """ Update a docker image with new executables. Args: <tag> <executable>""" + """ Update a docker image. Args: <tag> <actions>""" name = "update" def args(self, parser): parser.add_argument("tag", help="Image Tag") - parser.add_argument("executable", + parser.add_argument("--executable", help="Executable to copy") + parser.add_argument("--add-current-user", "-u", dest="user", + action="store_true", + help="Add the current user to image's passwd") def run(self, args, argv): # Create a temporary tarball with our whole build context and @@ -532,28 +552,44 @@ class UpdateCommand(SubCommand): tmp = tempfile.NamedTemporaryFile(suffix="dckr.tar.gz") tmp_tar = TarFile(fileobj=tmp, mode='w') - # Add the executable to the tarball, using the current - # configured binfmt_misc path. If we don't get a path then we - # only need the support libraries copied - ff, enabled = _check_binfmt_misc(args.executable) - - if not enabled: - print("binfmt_misc not enabled, update disabled") - return 1 - - if ff: - tmp_tar.add(args.executable, arcname=ff) - - # Add any associated libraries - libs = _get_so_libs(args.executable) - if libs: - for l in libs: - tmp_tar.add(os.path.realpath(l), arcname=l) - # Create a Docker buildfile df = StringIO() df.write(u"FROM %s\n" % args.tag) - df.write(u"ADD . /\n") + + if args.executable: + # Add the executable to the tarball, using the current + # configured binfmt_misc path. If we don't get a path then we + # only need the support libraries copied + ff, enabled = _check_binfmt_misc(args.executable) + + if not enabled: + print("binfmt_misc not enabled, update disabled") + return 1 + + if ff: + tmp_tar.add(args.executable, arcname=ff) + + # Add any associated libraries + libs = _get_so_libs(args.executable) + if libs: + for l in libs: + so_path = os.path.dirname(l) + name = os.path.basename(l) + real_l = os.path.realpath(l) + try: + tmp_tar.add(real_l, arcname="%s/%s" % (so_path, name)) + except FileNotFoundError: + print("Couldn't add %s/%s to archive" % (so_path, name)) + pass + + df.write(u"ADD . /\n") + + if args.user: + uid = os.getuid() + uname = getpwuid(uid).pw_name + df.write("\n") + df.write("RUN id %s 2>/dev/null || useradd -u %d -U %s" % + (uname, uid, uname)) df_bytes = BytesIO(bytes(df.getvalue(), "UTF-8")) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 0ac46ddd91..7eeecacc46 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -20,6 +20,7 @@ ENV PACKAGES \ gnutls-dev \ gtk+3.0-dev \ libaio-dev \ + libbpf-dev \ libcap-ng-dev \ libjpeg-turbo-dev \ libnfs-dev \ diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index a8c6c528b0..efc1349cc8 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -14,6 +14,7 @@ ENV PACKAGES \ git \ glib2-devel \ libaio-devel \ + libbpf-devel \ libepoxy-devel \ libfdt-devel \ libgcrypt-devel \ diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker b/tests/docker/dockerfiles/debian-hexagon-cross.docker new file mode 100644 index 0000000000..d5dc299dc1 --- /dev/null +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker @@ -0,0 +1,45 @@ +# +# Docker Hexagon cross-compiler target +# +# This docker target is used for building hexagon tests. As it also +# needs to be able to build QEMU itself in CI we include it's +# build-deps. It is also a "stand-alone" image so as not to be +# triggered by re-builds on other base images given it takes a long +# time to build. +# +FROM qemu/debian10 + +# Install common build utilities +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + bison \ + cmake \ + flex \ + lld \ + rsync \ + wget + +ENV TOOLCHAIN_INSTALL /usr/local +ENV ROOTFS /usr/local + +ENV LLVM_URL https://github.com/llvm/llvm-project/archive/bfcd21876adc3498065e4da92799f613e730d475.tar.gz +ENV MUSL_URL https://github.com/quic/musl/archive/aff74b395fbf59cd7e93b3691905aa1af6c0778c.tar.gz +ENV LINUX_URL https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.6.18.tar.xz + +ADD build-toolchain.sh /root/hexagon-toolchain/build-toolchain.sh + +RUN cd /root/hexagon-toolchain && ./build-toolchain.sh + +FROM debian:buster-slim +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list +# Install QEMU build deps for use in CI +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ + DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy git ninja-build && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt build-dep -yy --arch-only qemu +COPY --from=0 /usr/local /usr/local +ENV PATH $PATH:/usr/local/bin/ diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh new file mode 100755 index 0000000000..19b1c9f83e --- /dev/null +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker.d/build-toolchain.sh @@ -0,0 +1,141 @@ +#!/bin/bash + +set -e + +BASE=$(readlink -f ${PWD}) + +TOOLCHAIN_INSTALL=$(readlink -f "$TOOLCHAIN_INSTALL") +ROOTFS=$(readlink -f "$ROOTFS") + +TOOLCHAIN_BIN=${TOOLCHAIN_INSTALL}/bin +HEX_SYSROOT=${TOOLCHAIN_INSTALL}/hexagon-unknown-linux-musl +HEX_TOOLS_TARGET_BASE=${HEX_SYSROOT}/usr + +function cdp() { + DIR="$1" + mkdir -p "$DIR" + cd "$DIR" +} + +function fetch() { + DIR="$1" + URL="$2" + TEMP="$(readlink -f "$PWD/tmp.tar.gz")" + wget --quiet "$URL" -O "$TEMP" + cdp "$DIR" + tar xaf "$TEMP" --strip-components=1 + rm "$TEMP" + cd - +} + +build_llvm_clang() { + fetch "$BASE/llvm-project" "$LLVM_URL" + cdp "$BASE/build-llvm" + + cmake -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=${TOOLCHAIN_INSTALL} \ + -DLLVM_ENABLE_LLD=ON \ + -DLLVM_TARGETS_TO_BUILD="Hexagon" \ + -DLLVM_ENABLE_PROJECTS="clang;lld" \ + "$BASE/llvm-project/llvm" + ninja all install + cd ${TOOLCHAIN_BIN} + ln -sf clang hexagon-unknown-linux-musl-clang + ln -sf clang++ hexagon-unknown-linux-musl-clang++ + ln -sf llvm-ar hexagon-unknown-linux-musl-ar + ln -sf llvm-objdump hexagon-unknown-linux-musl-objdump + ln -sf llvm-objcopy hexagon-unknown-linux-musl-objcopy + ln -sf llvm-readelf hexagon-unknown-linux-musl-readelf + ln -sf llvm-ranlib hexagon-unknown-linux-musl-ranlib + + # workaround for now: + cat <<EOF > hexagon-unknown-linux-musl.cfg +-G0 --sysroot=${HEX_SYSROOT} +EOF +} + +build_clang_rt() { + cdp "$BASE/build-clang_rt" + cmake -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_CONFIG_PATH="$BASE/build-llvm/bin/llvm-config" \ + -DCMAKE_ASM_FLAGS="-G0 -mlong-calls -fno-pic --target=hexagon-unknown-linux-musl " \ + -DCMAKE_SYSTEM_NAME=Linux \ + -DCMAKE_C_COMPILER="${TOOLCHAIN_BIN}/hexagon-unknown-linux-musl-clang" \ + -DCMAKE_ASM_COMPILER="${TOOLCHAIN_BIN}/hexagon-unknown-linux-musl-clang" \ + -DCMAKE_INSTALL_PREFIX=${HEX_TOOLS_TARGET_BASE} \ + -DCMAKE_CROSSCOMPILING=ON \ + -DCMAKE_C_COMPILER_FORCED=ON \ + -DCMAKE_CXX_COMPILER_FORCED=ON \ + -DCOMPILER_RT_BUILD_BUILTINS=ON \ + -DCOMPILER_RT_BUILTINS_ENABLE_PIC=OFF \ + -DCMAKE_SIZEOF_VOID_P=4 \ + -DCOMPILER_RT_OS_DIR= \ + -DCAN_TARGET_hexagon=1 \ + -DCAN_TARGET_x86_64=0 \ + -DCOMPILER_RT_SUPPORTED_ARCH=hexagon \ + -DLLVM_ENABLE_PROJECTS="compiler-rt" \ + "$BASE/llvm-project/compiler-rt" + ninja install-compiler-rt +} + +build_musl_headers() { + fetch "$BASE/musl" "$MUSL_URL" + cd "$BASE/musl" + make clean + CC=${TOOLCHAIN_BIN}/hexagon-unknown-linux-musl-clang \ + CROSS_COMPILE=hexagon-unknown-linux-musl \ + LIBCC=${HEX_TOOLS_TARGET_BASE}/lib/libclang_rt.builtins-hexagon.a \ + CROSS_CFLAGS="-G0 -O0 -mv65 -fno-builtin -fno-rounding-math --target=hexagon-unknown-linux-musl" \ + ./configure --target=hexagon --prefix=${HEX_TOOLS_TARGET_BASE} + PATH=${TOOLCHAIN_BIN}:$PATH make CROSS_COMPILE= install-headers + + cd ${HEX_SYSROOT}/.. + ln -sf hexagon-unknown-linux-musl hexagon +} + +build_kernel_headers() { + fetch "$BASE/linux" "$LINUX_URL" + mkdir -p "$BASE/build-linux" + cd "$BASE/linux" + make O=../build-linux ARCH=hexagon \ + KBUILD_CFLAGS_KERNEL="-mlong-calls" \ + CC=${TOOLCHAIN_BIN}/hexagon-unknown-linux-musl-clang \ + LD=${TOOLCHAIN_BIN}/ld.lld \ + KBUILD_VERBOSE=1 comet_defconfig + make mrproper + + cd "$BASE/build-linux" + make \ + ARCH=hexagon \ + CC=${TOOLCHAIN_BIN}/clang \ + INSTALL_HDR_PATH=${HEX_TOOLS_TARGET_BASE} \ + V=1 \ + headers_install +} + +build_musl() { + cd "$BASE/musl" + make clean + CROSS_COMPILE=hexagon-unknown-linux-musl- \ + AR=llvm-ar \ + RANLIB=llvm-ranlib \ + STRIP=llvm-strip \ + CC=clang \ + LIBCC=${HEX_TOOLS_TARGET_BASE}/lib/libclang_rt.builtins-hexagon.a \ + CFLAGS="-G0 -O0 -mv65 -fno-builtin -fno-rounding-math --target=hexagon-unknown-linux-musl" \ + ./configure --target=hexagon --prefix=${HEX_TOOLS_TARGET_BASE} + PATH=${TOOLCHAIN_BIN}/:$PATH make CROSS_COMPILE= install + cd ${HEX_TOOLS_TARGET_BASE}/lib + ln -sf libc.so ld-musl-hexagon.so + ln -sf ld-musl-hexagon.so ld-musl-hexagon.so.1 + cdp ${HEX_TOOLS_TARGET_BASE}/../lib + ln -sf ../usr/lib/ld-musl-hexagon.so.1 +} + +build_llvm_clang +build_kernel_headers +build_musl_headers +build_clang_rt +build_musl diff --git a/tests/docker/dockerfiles/debian-powerpc-cross.docker b/tests/docker/dockerfiles/debian-powerpc-cross.docker deleted file mode 100644 index 07e1789650..0000000000 --- a/tests/docker/dockerfiles/debian-powerpc-cross.docker +++ /dev/null @@ -1,12 +0,0 @@ -# -# Docker powerpc cross-compiler target -# -# This docker target builds on the debian Buster base image. -# -FROM qemu/debian10 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-powerpc-linux-gnu \ - libc6-dev-powerpc-cross diff --git a/tests/docker/dockerfiles/debian-powerpc-test-cross.docker b/tests/docker/dockerfiles/debian-powerpc-test-cross.docker new file mode 100644 index 0000000000..36b336f709 --- /dev/null +++ b/tests/docker/dockerfiles/debian-powerpc-test-cross.docker @@ -0,0 +1,17 @@ +# +# Docker powerpc/ppc64/ppc64le cross-compiler target +# +# This docker target builds on the debian Bullseye base image. +# +FROM qemu/debian11 + +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + gcc-powerpc-linux-gnu \ + libc6-dev-powerpc-cross \ + gcc-10-powerpc64-linux-gnu \ + libc6-dev-ppc64-cross \ + gcc-10-powerpc64le-linux-gnu \ + libc6-dev-ppc64el-cross + diff --git a/tests/docker/dockerfiles/debian-ppc64-cross.docker b/tests/docker/dockerfiles/debian-ppc64-cross.docker deleted file mode 100644 index 8efe68874e..0000000000 --- a/tests/docker/dockerfiles/debian-ppc64-cross.docker +++ /dev/null @@ -1,11 +0,0 @@ -# -# Docker ppc64 cross-compiler target -# -# This docker target builds on the debian Buster base image. -FROM qemu/debian10 - -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-powerpc64-linux-gnu \ - libc6-dev-ppc64-cross diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index d8fa16372d..0979c0e1f4 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -32,6 +32,7 @@ ENV PACKAGES \ libcurl-devel \ libepoxy-devel \ libfdt-devel \ + libbpf-devel \ libiscsi-devel \ libjpeg-devel \ libpmem-devel \ diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 1cddf5bdaa..1f5c8f1053 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -32,6 +32,7 @@ libqos_srcs = files('../libqtest.c', 'virtio-9p.c', 'virtio-balloon.c', 'virtio-blk.c', + 'vhost-user-blk.c', 'virtio-mmio.c', 'virtio-net.c', 'virtio-pci.c', diff --git a/tests/qtest/libqos/vhost-user-blk.c b/tests/qtest/libqos/vhost-user-blk.c new file mode 100644 index 0000000000..568c3426ed --- /dev/null +++ b/tests/qtest/libqos/vhost-user-blk.c @@ -0,0 +1,130 @@ +/* + * libqos driver framework + * + * Based on tests/qtest/libqos/virtio-blk.c + * + * Copyright (c) 2020 Coiby Xu <coiby.xu@gmail.com> + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * 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/> + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/module.h" +#include "standard-headers/linux/virtio_blk.h" +#include "vhost-user-blk.h" + +#define PCI_SLOT 0x04 +#define PCI_FN 0x00 + +/* virtio-blk-device */ +static void *qvhost_user_blk_get_driver(QVhostUserBlk *v_blk, + const char *interface) +{ + if (!g_strcmp0(interface, "vhost-user-blk")) { + return v_blk; + } + if (!g_strcmp0(interface, "virtio")) { + return v_blk->vdev; + } + + fprintf(stderr, "%s not present in vhost-user-blk-device\n", interface); + g_assert_not_reached(); +} + +static void *qvhost_user_blk_device_get_driver(void *object, + const char *interface) +{ + QVhostUserBlkDevice *v_blk = object; + return qvhost_user_blk_get_driver(&v_blk->blk, interface); +} + +static void *vhost_user_blk_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVhostUserBlkDevice *vhost_user_blk = g_new0(QVhostUserBlkDevice, 1); + QVhostUserBlk *interface = &vhost_user_blk->blk; + + interface->vdev = virtio_dev; + + vhost_user_blk->obj.get_driver = qvhost_user_blk_device_get_driver; + + return &vhost_user_blk->obj; +} + +/* virtio-blk-pci */ +static void *qvhost_user_blk_pci_get_driver(void *object, const char *interface) +{ + QVhostUserBlkPCI *v_blk = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_blk->pci_vdev.pdev; + } + return qvhost_user_blk_get_driver(&v_blk->blk, interface); +} + +static void *vhost_user_blk_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVhostUserBlkPCI *vhost_user_blk = g_new0(QVhostUserBlkPCI, 1); + QVhostUserBlk *interface = &vhost_user_blk->blk; + QOSGraphObject *obj = &vhost_user_blk->pci_vdev.obj; + + virtio_pci_init(&vhost_user_blk->pci_vdev, pci_bus, addr); + interface->vdev = &vhost_user_blk->pci_vdev.vdev; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_BLOCK); + + obj->get_driver = qvhost_user_blk_pci_get_driver; + + return obj; +} + +static void vhost_user_blk_register_nodes(void) +{ + /* + * FIXME: every test using these two nodes needs to setup a + * -drive,id=drive0 otherwise QEMU is not going to start. + * Therefore, we do not include "produces" edge for virtio + * and pci-device yet. + */ + + char *arg = g_strdup_printf("id=drv0,chardev=char1,addr=%x.%x", + PCI_SLOT, PCI_FN); + + QPCIAddress addr = { + .devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN), + }; + + QOSGraphEdgeOptions opts = { }; + + /* virtio-blk-device */ + /** opts.extra_device_opts = "drive=drive0"; */ + qos_node_create_driver("vhost-user-blk-device", + vhost_user_blk_device_create); + qos_node_consumes("vhost-user-blk-device", "virtio-bus", &opts); + qos_node_produces("vhost-user-blk-device", "vhost-user-blk"); + + /* virtio-blk-pci */ + opts.extra_device_opts = arg; + add_qpci_address(&opts, &addr); + qos_node_create_driver("vhost-user-blk-pci", vhost_user_blk_pci_create); + qos_node_consumes("vhost-user-blk-pci", "pci-bus", &opts); + qos_node_produces("vhost-user-blk-pci", "vhost-user-blk"); + + g_free(arg); +} + +libqos_init(vhost_user_blk_register_nodes); diff --git a/tests/qtest/libqos/vhost-user-blk.h b/tests/qtest/libqos/vhost-user-blk.h new file mode 100644 index 0000000000..2a03456a45 --- /dev/null +++ b/tests/qtest/libqos/vhost-user-blk.h @@ -0,0 +1,48 @@ +/* + * libqos driver framework + * + * Based on tests/qtest/libqos/virtio-blk.c + * + * Copyright (c) 2020 Coiby Xu <coiby.xu@gmail.com> + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * 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/> + */ + +#ifndef TESTS_LIBQOS_VHOST_USER_BLK_H +#define TESTS_LIBQOS_VHOST_USER_BLK_H + +#include "qgraph.h" +#include "virtio.h" +#include "virtio-pci.h" + +typedef struct QVhostUserBlk QVhostUserBlk; +typedef struct QVhostUserBlkPCI QVhostUserBlkPCI; +typedef struct QVhostUserBlkDevice QVhostUserBlkDevice; + +struct QVhostUserBlk { + QVirtioDevice *vdev; +}; + +struct QVhostUserBlkPCI { + QVirtioPCIDevice pci_vdev; + QVhostUserBlk blk; +}; + +struct QVhostUserBlkDevice { + QOSGraphObject obj; + QVhostUserBlk blk; +}; + +#endif diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 49de74ff59..c3a223a83d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -231,6 +231,9 @@ if have_virtfs qos_test_ss.add(files('virtio-9p-test.c')) endif qos_test_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user-test.c')) +if have_tools and have_vhost_user_blk_server + qos_test_ss.add(files('vhost-user-blk-test.c')) +endif tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] @@ -269,6 +272,7 @@ foreach dir : target_dirs endif qtest_env.set('G_TEST_DBUS_DAEMON', meson.source_root() / 'tests/dbus-vmstate-daemon.sh') qtest_env.set('QTEST_QEMU_BINARY', './qemu-system-' + target_base) + qtest_env.set('QTEST_QEMU_STORAGE_DAEMON_BINARY', './storage-daemon/qemu-storage-daemon') foreach test : target_qtests # Executables are shared across targets, declare them only the first time we diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c new file mode 100644 index 0000000000..8796c74ca4 --- /dev/null +++ b/tests/qtest/vhost-user-blk-test.c @@ -0,0 +1,989 @@ +/* + * QTest testcase for Vhost-user Block Device + * + * Based on tests/qtest//virtio-blk-test.c + + * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2014 Marc Marà + * Copyright (c) 2020 Coiby Xu + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qemu/bswap.h" +#include "qemu/module.h" +#include "standard-headers/linux/virtio_blk.h" +#include "standard-headers/linux/virtio_pci.h" +#include "libqos/qgraph.h" +#include "libqos/vhost-user-blk.h" +#include "libqos/libqos-pc.h" + +#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) +#define PCI_SLOT_HP 0x06 + +typedef struct { + pid_t pid; +} QemuStorageDaemonState; + +typedef struct QVirtioBlkReq { + uint32_t type; + uint32_t ioprio; + uint64_t sector; + char *data; + uint8_t status; +} QVirtioBlkReq; + +#ifdef HOST_WORDS_BIGENDIAN +static const bool host_is_big_endian = true; +#else +static const bool host_is_big_endian; /* false */ +#endif + +static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) +{ + if (qvirtio_is_big_endian(d) != host_is_big_endian) { + req->type = bswap32(req->type); + req->ioprio = bswap32(req->ioprio); + req->sector = bswap64(req->sector); + } +} + +static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d, + struct virtio_blk_discard_write_zeroes *dwz_hdr) +{ + if (qvirtio_is_big_endian(d) != host_is_big_endian) { + dwz_hdr->sector = bswap64(dwz_hdr->sector); + dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors); + dwz_hdr->flags = bswap32(dwz_hdr->flags); + } +} + +static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d, + QVirtioBlkReq *req, uint64_t data_size) +{ + uint64_t addr; + uint8_t status = 0xFF; + QTestState *qts = global_qtest; + + switch (req->type) { + case VIRTIO_BLK_T_IN: + case VIRTIO_BLK_T_OUT: + g_assert_cmpuint(data_size % 512, ==, 0); + break; + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: + g_assert_cmpuint(data_size % + sizeof(struct virtio_blk_discard_write_zeroes), ==, 0); + break; + default: + g_assert_cmpuint(data_size, ==, 0); + } + + addr = guest_alloc(alloc, sizeof(*req) + data_size); + + virtio_blk_fix_request(d, req); + + qtest_memwrite(qts, addr, req, 16); + qtest_memwrite(qts, addr + 16, req->data, data_size); + qtest_memwrite(qts, addr + 16 + data_size, &status, sizeof(status)); + + return addr; +} + +static void test_invalid_discard_write_zeroes(QVirtioDevice *dev, + QGuestAllocator *alloc, + QTestState *qts, + QVirtQueue *vq, + uint32_t type) +{ + QVirtioBlkReq req; + struct virtio_blk_discard_write_zeroes dwz_hdr; + struct virtio_blk_discard_write_zeroes dwz_hdr2[2]; + uint64_t req_addr; + uint32_t free_head; + uint8_t status; + + /* More than one dwz is not supported */ + req.type = type; + req.data = (char *) dwz_hdr2; + dwz_hdr2[0].sector = 0; + dwz_hdr2[0].num_sectors = 1; + dwz_hdr2[0].flags = 0; + dwz_hdr2[1].sector = 1; + dwz_hdr2[1].num_sectors = 1; + dwz_hdr2[1].flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr2[0]); + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr2[1]); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr2)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr2), false, true); + qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr2), 1, true, + false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr2)); + g_assert_cmpint(status, ==, VIRTIO_BLK_S_UNSUPP); + + guest_free(alloc, req_addr); + + /* num_sectors must be less than config->max_write_zeroes_sectors */ + req.type = type; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = 0; + dwz_hdr.num_sectors = 0xffffffff; + dwz_hdr.flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, + false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, VIRTIO_BLK_S_IOERR); + + guest_free(alloc, req_addr); + + /* sector must be less than the device capacity */ + req.type = type; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = TEST_IMAGE_SIZE / 512 + 1; + dwz_hdr.num_sectors = 1; + dwz_hdr.flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, + false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, VIRTIO_BLK_S_IOERR); + + guest_free(alloc, req_addr); + + /* reserved flag bits must be zero */ + req.type = type; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = 0; + dwz_hdr.num_sectors = 1; + dwz_hdr.flags = ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, + false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, VIRTIO_BLK_S_UNSUPP); + + guest_free(alloc, req_addr); +} + +/* Returns the request virtqueue so the caller can perform further tests */ +static QVirtQueue *test_basic(QVirtioDevice *dev, QGuestAllocator *alloc) +{ + QVirtioBlkReq req; + uint64_t req_addr; + uint64_t capacity; + uint64_t features; + uint32_t free_head; + uint8_t status; + char *data; + QTestState *qts = global_qtest; + QVirtQueue *vq; + + features = qvirtio_get_features(dev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_RING_F_EVENT_IDX) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + vq = qvirtqueue_setup(dev, alloc, 0); + + qvirtio_set_driver_ok(dev); + + /* Write and read with 3 descriptor layout */ + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + qtest_memread(qts, req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(alloc, req_addr); + + if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) { + struct virtio_blk_discard_write_zeroes dwz_hdr; + void *expected; + + /* + * WRITE_ZEROES request on the same sector of previous test where + * we wrote "TEST". + */ + req.type = VIRTIO_BLK_T_WRITE_ZEROES; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = 0; + dwz_hdr.num_sectors = 1; + dwz_hdr.flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, + false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request to check if the sector contains all zeroes */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc(512); + expected = g_malloc0(512); + qtest_memread(qts, req_addr + 16, data, 512); + g_assert_cmpmem(data, 512, expected, 512); + g_free(expected); + g_free(data); + + guest_free(alloc, req_addr); + + test_invalid_discard_write_zeroes(dev, alloc, qts, vq, + VIRTIO_BLK_T_WRITE_ZEROES); + } + + if (features & (1u << VIRTIO_BLK_F_DISCARD)) { + struct virtio_blk_discard_write_zeroes dwz_hdr; + + req.type = VIRTIO_BLK_T_DISCARD; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = 0; + dwz_hdr.num_sectors = 1; + dwz_hdr.flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(qts, vq, req_addr + 16 + sizeof(dwz_hdr), + 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + test_invalid_discard_write_zeroes(dev, alloc, qts, vq, + VIRTIO_BLK_T_DISCARD); + } + + if (features & (1u << VIRTIO_F_ANY_LAYOUT)) { + /* Write and read with 2 descriptor layout */ + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 528, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 513, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + qtest_memread(qts, req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(alloc, req_addr); + } + + return vq; +} + +static void basic(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVhostUserBlk *blk_if = obj; + QVirtQueue *vq; + + vq = test_basic(blk_if->vdev, t_alloc); + qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc); + +} + +static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) +{ + QVirtQueue *vq; + QVhostUserBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; + QVirtioBlkReq req; + QVRingIndirectDesc *indirect; + uint64_t req_addr; + uint64_t capacity; + uint64_t features; + uint32_t free_head; + uint8_t status; + char *data; + QTestState *qts = global_qtest; + + features = qvirtio_get_features(dev); + g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_EVENT_IDX) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtio_set_driver_ok(dev); + + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2); + qvring_indirect_desc_add(dev, qts, indirect, req_addr, 528, false); + qvring_indirect_desc_add(dev, qts, indirect, req_addr + 528, 1, true); + free_head = qvirtqueue_add_indirect(qts, vq, indirect); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + g_free(indirect); + guest_free(t_alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + indirect = qvring_indirect_desc_setup(qts, dev, t_alloc, 2); + qvring_indirect_desc_add(dev, qts, indirect, req_addr, 16, false); + qvring_indirect_desc_add(dev, qts, indirect, req_addr + 16, 513, true); + free_head = qvirtqueue_add_indirect(qts, vq, indirect); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + qtest_memread(qts, req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + g_free(indirect); + guest_free(t_alloc, req_addr); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); +} + +static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc) +{ + QVirtQueue *vq; + QVhostUserBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QVirtioDevice *dev = &pdev->vdev; + QVirtioBlkReq req; + uint64_t req_addr; + uint64_t capacity; + uint64_t features; + uint32_t free_head; + uint32_t write_head; + uint32_t desc_idx; + uint8_t status; + char *data; + QOSGraphObject *blk_object = obj; + QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); + QTestState *qts = global_qtest; + + if (qpci_check_buggy_msi(pci_dev)) { + return; + } + + qpci_msix_enable(pdev->pdev); + qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); + + features = qvirtio_get_features(dev); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev, features); + + capacity = qvirtio_config_readq(dev, 0); + g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); + + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); + + qvirtio_set_driver_ok(dev); + + /* + * libvhost-user signals the call fd in VHOST_USER_SET_VRING_CALL, make + * sure to wait for the isr here so we don't race and confuse it later on. + */ + qvirtio_wait_queue_isr(qts, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + + qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + + /* Write request */ + req.type = VIRTIO_BLK_T_OUT; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + strcpy(req.data, "TEST"); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + /* Notify after processing the third request */ + qvirtqueue_set_used_event(qts, vq, 2); + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, false, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(qts, dev, vq, free_head); + write_head = free_head; + + /* No notification expected */ + status = qvirtio_wait_status_byte_no_isr(qts, dev, + vq, req_addr + 528, + QVIRTIO_BLK_TIMEOUT_US); + g_assert_cmpint(status, ==, 0); + + guest_free(t_alloc, req_addr); + + /* Read request */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 1; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(qts, vq, req_addr, 16, false, true); + qvirtqueue_add(qts, vq, req_addr + 16, 512, true, true); + qvirtqueue_add(qts, vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(qts, dev, vq, free_head); + + /* We get just one notification for both requests */ + qvirtio_wait_used_elem(qts, dev, vq, write_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + g_assert(qvirtqueue_get_buf(qts, vq, &desc_idx, NULL)); + g_assert_cmpint(desc_idx, ==, free_head); + + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc0(512); + qtest_memread(qts, req_addr + 16, data, 512); + g_assert_cmpstr(data, ==, "TEST"); + g_free(data); + + guest_free(t_alloc, req_addr); + + /* End test */ + qpci_msix_disable(pdev->pdev); + + qvirtqueue_cleanup(dev->bus, vq, t_alloc); +} + +static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioPCIDevice *dev1 = obj; + QVirtioPCIDevice *dev; + QTestState *qts = dev1->pdev->bus->qts; + + /* plug secondary disk */ + qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1", + "{'addr': %s, 'chardev': 'char2'}", + stringify(PCI_SLOT_HP) ".0"); + + dev = virtio_pci_new(dev1->pdev->bus, + &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) + }); + g_assert_nonnull(dev); + g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); + qvirtio_pci_device_disable(dev); + qos_object_destroy((QOSGraphObject *)dev); + + /* unplug secondary disk */ + qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP); +} + +static void multiqueue(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioPCIDevice *pdev1 = obj; + QVirtioDevice *dev1 = &pdev1->vdev; + QVirtioPCIDevice *pdev8; + QVirtioDevice *dev8; + QTestState *qts = pdev1->pdev->bus->qts; + uint64_t features; + uint16_t num_queues; + + /* + * The primary device has 1 queue and VIRTIO_BLK_F_MQ is not enabled. The + * VIRTIO specification allows VIRTIO_BLK_F_MQ to be enabled when there is + * only 1 virtqueue, but --device vhost-user-blk-pci doesn't do this (which + * is also spec-compliant). + */ + features = qvirtio_get_features(dev1); + g_assert_cmpint(features & (1u << VIRTIO_BLK_F_MQ), ==, 0); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | + (1u << VIRTIO_BLK_F_SCSI)); + qvirtio_set_features(dev1, features); + + /* Hotplug a secondary device with 8 queues */ + qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1", + "{'addr': %s, 'chardev': 'char2', 'num-queues': 8}", + stringify(PCI_SLOT_HP) ".0"); + + pdev8 = virtio_pci_new(pdev1->pdev->bus, + &(QPCIAddress) { + .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) + }); + g_assert_nonnull(pdev8); + g_assert_cmpint(pdev8->vdev.device_type, ==, VIRTIO_ID_BLOCK); + + qos_object_start_hw(&pdev8->obj); + + dev8 = &pdev8->vdev; + features = qvirtio_get_features(dev8); + g_assert_cmpint(features & (1u << VIRTIO_BLK_F_MQ), + ==, + (1u << VIRTIO_BLK_F_MQ)); + features = features & ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | + (1u << VIRTIO_BLK_F_SCSI) | + (1u << VIRTIO_BLK_F_MQ)); + qvirtio_set_features(dev8, features); + + num_queues = qvirtio_config_readw(dev8, + offsetof(struct virtio_blk_config, num_queues)); + g_assert_cmpint(num_queues, ==, 8); + + qvirtio_pci_device_disable(pdev8); + qos_object_destroy(&pdev8->obj); + + /* unplug secondary disk */ + qpci_unplug_acpi_device_test(qts, "drv1", PCI_SLOT_HP); +} + +/* + * Check that setting the vring addr on a non-existent virtqueue does + * not crash. + */ +static void test_nonexistent_virtqueue(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVhostUserBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QPCIBar bar0; + QPCIDevice *dev; + + dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0)); + g_assert(dev != NULL); + qpci_device_enable(dev); + + bar0 = qpci_iomap(dev, 0, NULL); + + qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2); + qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1); + + g_free(dev); +} + +static const char *qtest_qemu_storage_daemon_binary(void) +{ + const char *qemu_storage_daemon_bin; + + qemu_storage_daemon_bin = getenv("QTEST_QEMU_STORAGE_DAEMON_BINARY"); + if (!qemu_storage_daemon_bin) { + fprintf(stderr, "Environment variable " + "QTEST_QEMU_STORAGE_DAEMON_BINARY required\n"); + exit(0); + } + + return qemu_storage_daemon_bin; +} + +/* g_test_queue_destroy() cleanup function for files */ +static void destroy_file(void *path) +{ + unlink(path); + g_free(path); + qos_invalidate_command_line(); +} + +static char *drive_create(void) +{ + int fd, ret; + /** vhost-user-blk won't recognize drive located in /tmp */ + char *t_path = g_strdup("qtest.XXXXXX"); + + /** Create a temporary raw image */ + fd = mkstemp(t_path); + g_assert_cmpint(fd, >=, 0); + ret = ftruncate(fd, TEST_IMAGE_SIZE); + g_assert_cmpint(ret, ==, 0); + close(fd); + + g_test_queue_destroy(destroy_file, t_path); + return t_path; +} + +static char *create_listen_socket(int *fd) +{ + int tmp_fd; + char *path; + + /* No race because our pid makes the path unique */ + path = g_strdup_printf("/tmp/qtest-%d-sock.XXXXXX", getpid()); + tmp_fd = mkstemp(path); + g_assert_cmpint(tmp_fd, >=, 0); + close(tmp_fd); + unlink(path); + + *fd = qtest_socket_server(path); + g_test_queue_destroy(destroy_file, path); + return path; +} + +/* + * g_test_queue_destroy() and qtest_add_abrt_handler() cleanup function for + * qemu-storage-daemon. + */ +static void quit_storage_daemon(void *data) +{ + QemuStorageDaemonState *qsd = data; + int wstatus; + pid_t pid; + + /* + * If we were invoked as a g_test_queue_destroy() cleanup function we need + * to remove the abrt handler to avoid being called again if the code below + * aborts. Also, we must not leave the abrt handler installed after + * cleanup. + */ + qtest_remove_abrt_handler(data); + + /* Before quitting storage-daemon, quit qemu to avoid dubious messages */ + qtest_kill_qemu(global_qtest); + + kill(qsd->pid, SIGTERM); + pid = waitpid(qsd->pid, &wstatus, 0); + g_assert_cmpint(pid, ==, qsd->pid); + if (!WIFEXITED(wstatus)) { + fprintf(stderr, "%s: expected qemu-storage-daemon to exit\n", + __func__); + abort(); + } + if (WEXITSTATUS(wstatus) != 0) { + fprintf(stderr, "%s: expected qemu-storage-daemon to exit " + "successfully, got %d\n", + __func__, WEXITSTATUS(wstatus)); + abort(); + } + + g_free(data); +} + +static void start_vhost_user_blk(GString *cmd_line, int vus_instances, + int num_queues) +{ + const char *vhost_user_blk_bin = qtest_qemu_storage_daemon_binary(); + int i; + gchar *img_path; + GString *storage_daemon_command = g_string_new(NULL); + QemuStorageDaemonState *qsd; + + g_string_append_printf(storage_daemon_command, + "exec %s ", + vhost_user_blk_bin); + + g_string_append_printf(cmd_line, + " -object memory-backend-memfd,id=mem,size=256M,share=on " + " -M memory-backend=mem -m 256M "); + + for (i = 0; i < vus_instances; i++) { + int fd; + char *sock_path = create_listen_socket(&fd); + + /* create image file */ + img_path = drive_create(); + g_string_append_printf(storage_daemon_command, + "--blockdev driver=file,node-name=disk%d,filename=%s " + "--export type=vhost-user-blk,id=disk%d,addr.type=unix,addr.path=%s," + "node-name=disk%i,writable=on,num-queues=%d ", + i, img_path, i, sock_path, i, num_queues); + + g_string_append_printf(cmd_line, "-chardev socket,id=char%d,path=%s ", + i + 1, sock_path); + } + + g_test_message("starting vhost-user backend: %s", + storage_daemon_command->str); + pid_t pid = fork(); + if (pid == 0) { + /* + * Close standard file descriptors so tap-driver.pl pipe detects when + * our parent terminates. + */ + close(0); + close(1); + open("/dev/null", O_RDONLY); + open("/dev/null", O_WRONLY); + + execlp("/bin/sh", "sh", "-c", storage_daemon_command->str, NULL); + exit(1); + } + g_string_free(storage_daemon_command, true); + + qsd = g_new(QemuStorageDaemonState, 1); + qsd->pid = pid; + + /* Make sure qemu-storage-daemon is stopped */ + qtest_add_abrt_handler(quit_storage_daemon, qsd); + g_test_queue_destroy(quit_storage_daemon, qsd); +} + +static void *vhost_user_blk_test_setup(GString *cmd_line, void *arg) +{ + start_vhost_user_blk(cmd_line, 1, 1); + return arg; +} + +/* + * Setup for hotplug. + * + * Since vhost-user server only serves one vhost-user client one time, + * another exprot + * + */ +static void *vhost_user_blk_hotplug_test_setup(GString *cmd_line, void *arg) +{ + /* "-chardev socket,id=char2" is used for pci_hotplug*/ + start_vhost_user_blk(cmd_line, 2, 1); + return arg; +} + +static void *vhost_user_blk_multiqueue_test_setup(GString *cmd_line, void *arg) +{ + start_vhost_user_blk(cmd_line, 2, 8); + return arg; +} + +static void register_vhost_user_blk_test(void) +{ + QOSGraphTestOptions opts = { + .before = vhost_user_blk_test_setup, + }; + + /* + * tests for vhost-user-blk and vhost-user-blk-pci + * The tests are borrowed from tests/virtio-blk-test.c. But some tests + * regarding block_resize don't work for vhost-user-blk. + * vhost-user-blk device doesn't have -drive, so tests containing + * block_resize are also abandoned, + * - config + * - resize + */ + qos_add_test("basic", "vhost-user-blk", basic, &opts); + qos_add_test("indirect", "vhost-user-blk", indirect, &opts); + qos_add_test("idx", "vhost-user-blk-pci", idx, &opts); + qos_add_test("nxvirtq", "vhost-user-blk-pci", + test_nonexistent_virtqueue, &opts); + + opts.before = vhost_user_blk_hotplug_test_setup; + qos_add_test("hotplug", "vhost-user-blk-pci", pci_hotplug, &opts); + + opts.before = vhost_user_blk_multiqueue_test_setup; + qos_add_test("multiqueue", "vhost-user-blk-pci", multiqueue, &opts); +} + +libqos_init(register_vhost_user_blk_test); diff --git a/tests/tcg/Makefile.qemu b/tests/tcg/Makefile.qemu index a56564660c..84c8543878 100644 --- a/tests/tcg/Makefile.qemu +++ b/tests/tcg/Makefile.qemu @@ -22,6 +22,8 @@ quiet-@ = $(if $(V),,@) quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3) CROSS_CC_GUEST:= +CROSS_AS_GUEST:= +CROSS_LD_GUEST:= DOCKER_IMAGE:= -include tests/tcg/config-$(TARGET).mak @@ -42,6 +44,8 @@ cross-build-guest-tests: $(call quiet-command, \ (mkdir -p tests/tcg/$(TARGET) && cd tests/tcg/$(TARGET) && \ $(MAKE) -f $(TCG_MAKE) TARGET="$(TARGET)" CC="$(CROSS_CC_GUEST)" \ + $(if $(CROSS_AS_GUEST),AS="$(CROSS_AS_GUEST)") \ + $(if $(CROSS_LD_GUEST),LD="$(CROSS_LD_GUEST)") \ SRC_PATH="$(SRC_PATH)" BUILD_STATIC=$(CROSS_CC_GUEST_STATIC) \ EXTRA_CFLAGS="$(CROSS_CC_GUEST_CFLAGS)"), \ "BUILD","$(TARGET) guest-tests with $(CROSS_CC_GUEST)") @@ -59,11 +63,24 @@ DOCKER_COMPILE_CMD="$(DOCKER_SCRIPT) cc \ -i qemu/$(DOCKER_IMAGE) \ -s $(SRC_PATH) -- " +DOCKER_AS_CMD=$(if $(DOCKER_CROSS_AS_GUEST),"$(DOCKER_SCRIPT) cc \ + --cc $(DOCKER_CROSS_AS_GUEST) \ + -i qemu/$(DOCKER_IMAGE) \ + -s $(SRC_PATH) -- ") + +DOCKER_LD_CMD=$(if $(DOCKER_CROSS_LD_GUEST),"$(DOCKER_SCRIPT) cc \ + --cc $(DOCKER_CROSS_LD_GUEST) \ + -i qemu/$(DOCKER_IMAGE) \ + -s $(SRC_PATH) -- ") + + .PHONY: docker-build-guest-tests docker-build-guest-tests: docker-image-$(DOCKER_IMAGE) $(call quiet-command, \ (mkdir -p tests/tcg/$(TARGET) && cd tests/tcg/$(TARGET) && \ $(MAKE) -f $(TCG_MAKE) TARGET="$(TARGET)" CC=$(DOCKER_COMPILE_CMD) \ + $(if $(DOCKER_AS_CMD),AS=$(DOCKER_AS_CMD)) \ + $(if $(DOCKER_LD_CMD),LD=$(DOCKER_LD_CMD)) \ SRC_PATH="$(SRC_PATH)" BUILD_STATIC=y \ EXTRA_CFLAGS="$(CROSS_CC_GUEST_CFLAGS)"), \ "BUILD","$(TARGET) guest-tests with docker qemu/$(DOCKER_IMAGE)") diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index cab8c6b3a2..b29fae4630 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -43,9 +43,10 @@ quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) # $1 = test name, $2 = cmd, $3 = desc ifdef CONFIG_USER_ONLY -run-test = $(call quiet-command, timeout $(TIMEOUT) $2 > $1.out,"TEST",$3) +run-test = $(call quiet-command, timeout --foreground $(TIMEOUT) $2 > $1.out, \ + "TEST",$3) else -run-test = $(call quiet-command, timeout $(TIMEOUT) $2,"TEST",$3) +run-test = $(call quiet-command, timeout --foreground $(TIMEOUT) $2,"TEST",$3) endif # $1 = test name, $2 = reference diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index 8f20ce065d..ed6492ce59 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -74,37 +74,17 @@ fi for target in $target_list; do arch=${target%%-*} - case $arch in - arm|armeb) - arches=arm - ;; - aarch64|aarch64_be) - arches="aarch64 arm" - ;; - mips*) - arches=mips - ;; - ppc*) - arches=ppc - ;; - sh4|sh4eb) - arches=sh4 - ;; - x86_64) - arches="x86_64 i386" - ;; - xtensa|xtensaeb) - arches=xtensa - ;; - alpha|cris|hexagon|hppa|i386|microblaze|microblazeel|m68k|openrisc|riscv64|s390x|sh4|sparc64) - arches=$target - ;; - *) - continue - ;; - esac + # reset all container fields container_image= + container_hosts= + container_cross_cc= + container_cross_as= + container_cross_ld= + + # suppress clang + supress_clang= + case $target in aarch64-*) # We don't have any bigendian build tools so we only use this for AArch64 @@ -128,6 +108,11 @@ for target in $target_list; do container_image=fedora-cris-cross container_cross_cc=cris-linux-gnu-gcc ;; + hexagon-*) + container_hosts=x86_64 + container_image=debian-hexagon-cross + container_cross_cc=hexagon-unknown-linux-musl-clang + ;; hppa-*) container_hosts=x86_64 container_image=debian-hppa-cross @@ -137,6 +122,7 @@ for target in $target_list; do container_hosts=x86_64 container_image=fedora-i386-cross container_cross_cc=gcc + supress_clang=yes ;; m68k-*) container_hosts=x86_64 @@ -165,18 +151,14 @@ for target in $target_list; do ;; ppc-*|ppc64abi32-*) container_hosts=x86_64 - container_image=debian-powerpc-cross - container_cross_cc=powerpc-linux-gnu-gcc - ;; - ppc64-*) - container_hosts=x86_64 - container_image=debian-ppc64-cross - container_cross_cc=powerpc64-linux-gnu-gcc + container_image=debian-powerpc-test-cross + container_cross_cc=powerpc-linux-gnu-gcc-10 ;; - ppc64le-*) + ppc64-*|ppc64le-*) container_hosts=x86_64 - container_image=debian-ppc64el-cross - container_cross_cc=powerpc64le-linux-gnu-gcc + container_image=debian-powerpc-test-cross + container_cross_cc=${target%%-*}-linux-gnu-gcc-10 + container_cross_cc=powerpc${container_cross_cc#ppc} ;; riscv64-*) container_hosts=x86_64 @@ -198,10 +180,17 @@ for target in $target_list; do container_image=debian-sparc64-cross container_cross_cc=sparc64-linux-gnu-gcc ;; + tricore-softmmu) + container_hosts=x86_64 + container_image=debian-tricore-cross + container_cross_as=tricore-as + container_cross_ld=tricore-ld + ;; x86_64-*) container_hosts="aarch64 ppc64el x86_64" container_image=debian-amd64-cross container_cross_cc=x86_64-linux-gnu-gcc + supress_clang=yes ;; xtensa*-softmmu) container_hosts=x86_64 @@ -216,6 +205,7 @@ for target in $target_list; do echo "# Automatically generated by configure - do not modify" > $config_target_mak echo "TARGET_NAME=$arch" >> $config_target_mak + echo "target=$target" >> $config_target_mak case $target in *-linux-user | *-bsd-user) echo "CONFIG_USER_ONLY=y" >> $config_target_mak @@ -231,74 +221,91 @@ for target in $target_list; do echo "CROSS_CC_GUEST_CFLAGS=$target_compiler_cflags" >> $config_target_mak got_cross_cc=no - for i in $arch $arches; do - if eval test "x\${cross_cc_$i+yes}" != xyes; then - continue - fi - eval "target_compiler=\${cross_cc_$i}" - if ! has $target_compiler; then - continue - fi - write_c_skeleton - if ! do_compiler "$target_compiler" $target_compiler_cflags -o $TMPE $TMPC -static ; then - # For host systems we might get away with building without -static - if ! do_compiler "$target_compiler" $target_compiler_cflags -o $TMPE $TMPC ; then - continue - fi - echo "CROSS_CC_GUEST_STATIC=y" >> $config_target_mak - else - echo "CROSS_CC_GUEST_STATIC=y" >> $config_target_mak - fi - echo "CROSS_CC_GUEST=$target_compiler" >> $config_target_mak + if eval test "x\${cross_cc_$arch}" != xyes; then + eval "target_compiler=\${cross_cc_$arch}" - # Test for compiler features for optional tests. We only do this - # for cross compilers because ensuring the docker containers based - # compilers is a requirememt for adding a new test that needs a - # compiler feature. - case $target in - aarch64-*) - if do_compiler "$target_compiler" $target_compiler_cflags \ - -march=armv8.1-a+sve -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_SVE=y" >> $config_target_mak - fi - if do_compiler "$target_compiler" $target_compiler_cflags \ - -march=armv8.3-a -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak - fi - if do_compiler "$target_compiler" $target_compiler_cflags \ - -mbranch-protection=standard -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_ARMV8_BTI=y" >> $config_target_mak - fi - if do_compiler "$target_compiler" $target_compiler_cflags \ - -march=armv8.5-a+memtag -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak - fi - ;; - ppc*) - if do_compiler "$target_compiler" $target_compiler_cflags \ - -mpower8-vector -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak - fi - ;; - i386-linux-user) - if do_compiler "$target_compiler" $target_compiler_cflags \ - -Werror -fno-pie -o $TMPE $TMPC; then - echo "CROSS_CC_HAS_I386_NOPIE=y" >> $config_target_mak - fi - ;; - esac + if has "$target_compiler"; then + if test "$supress_clang" = yes && + $target_compiler --version | grep -qi "clang"; then + got_cross_cc=no + else + write_c_skeleton + if ! do_compiler "$target_compiler" $target_compiler_cflags \ + -o $TMPE $TMPC -static ; then + # For host systems we might get away with building without -static + if do_compiler "$target_compiler" $target_compiler_cflags \ + -o $TMPE $TMPC ; then + got_cross_cc=yes + echo "CROSS_CC_GUEST_STATIC=y" >> $config_target_mak + echo "CROSS_CC_GUEST=$target_compiler" >> $config_target_mak + fi + else + got_cross_cc=yes + echo "CROSS_CC_GUEST_STATIC=y" >> $config_target_mak + echo "CROSS_CC_GUEST=$target_compiler" >> $config_target_mak + fi + fi + fi + fi - enabled_cross_compilers="$enabled_cross_compilers $target_compiler" - got_cross_cc=yes - break - done + if test $got_cross_cc = yes; then + # Test for compiler features for optional tests. We only do this + # for cross compilers because ensuring the docker containers based + # compilers is a requirememt for adding a new test that needs a + # compiler feature. - if test $got_cross_cc = no && test "$container" != no && test -n "$container_image"; then + case $target in + aarch64-*) + if do_compiler "$target_compiler" $target_compiler_cflags \ + -march=armv8.1-a+sve -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_SVE=y" >> $config_target_mak + fi + if do_compiler "$target_compiler" $target_compiler_cflags \ + -march=armv8.3-a -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_ARMV8_3=y" >> $config_target_mak + fi + if do_compiler "$target_compiler" $target_compiler_cflags \ + -mbranch-protection=standard -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_ARMV8_BTI=y" >> $config_target_mak + fi + if do_compiler "$target_compiler" $target_compiler_cflags \ + -march=armv8.5-a+memtag -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_ARMV8_MTE=y" >> $config_target_mak + fi + ;; + ppc*) + if do_compiler "$target_compiler" $target_compiler_cflags \ + -mpower8-vector -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_POWER8_VECTOR=y" >> $config_target_mak + fi + if do_compiler "$target_compiler" $target_compiler_cflags \ + -mpower10 -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_POWER10=y" >> $config_target_mak + fi + ;; + i386-linux-user) + if do_compiler "$target_compiler" $target_compiler_cflags \ + -Werror -fno-pie -o $TMPE $TMPC; then + echo "CROSS_CC_HAS_I386_NOPIE=y" >> $config_target_mak + fi + ;; + esac + elif test $got_cross_cc = no && test "$container" != no && \ + test -n "$container_image"; then for host in $container_hosts; do if test "$host" = "$ARCH"; then echo "DOCKER_IMAGE=$container_image" >> $config_target_mak - echo "DOCKER_CROSS_CC_GUEST=$container_cross_cc" >> $config_target_mak + echo "DOCKER_CROSS_CC_GUEST=$container_cross_cc" >> \ + $config_target_mak + if test -n "$container_cross_as"; then + echo "DOCKER_CROSS_AS_GUEST=$container_cross_as" >> \ + $config_target_mak + fi + if test -n "$container_cross_ld"; then + echo "DOCKER_CROSS_LD_GUEST=$container_cross_ld" >> \ + $config_target_mak + fi fi done fi diff --git a/tests/tcg/multiarch/system/memory.c b/tests/tcg/multiarch/system/memory.c index eb0ec6f8eb..41c7f66e2e 100644 --- a/tests/tcg/multiarch/system/memory.c +++ b/tests/tcg/multiarch/system/memory.c @@ -326,6 +326,7 @@ static bool do_unsigned_test(init_ufn fn) fn(i); ok = do_unsigned_reads(i); } + return ok; #else fn(0); return do_unsigned_reads(0); diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index 0c6a4585fc..a6a4ddaeca 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -10,4 +10,17 @@ PPC64_TESTS=bcdsub endif bcdsub: CFLAGS += -mpower8-vector +PPC64_TESTS += byte_reverse +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER10),) +run-byte_reverse: QEMU_OPTS+=-cpu POWER10 +run-plugin-byte_reverse-with-%: QEMU_OPTS+=-cpu POWER10 +else +byte_reverse: + $(call skip-test, "BUILD of $@", "missing compiler support") +run-byte_reverse: + $(call skip-test, "RUN of byte_reverse", "not built") +run-plugin-byte_reverse-with-%: + $(call skip-test, "RUN of byte_reverse ($*)", "not built") +endif + TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/ppc64le/Makefile.target b/tests/tcg/ppc64le/Makefile.target index 1acfcff94a..c0c14ffbad 100644 --- a/tests/tcg/ppc64le/Makefile.target +++ b/tests/tcg/ppc64le/Makefile.target @@ -9,4 +9,17 @@ PPC64LE_TESTS=bcdsub endif bcdsub: CFLAGS += -mpower8-vector +PPC64LE_TESTS += byte_reverse +ifneq ($(DOCKER_IMAGE)$(CROSS_CC_HAS_POWER10),) +run-byte_reverse: QEMU_OPTS+=-cpu POWER10 +run-plugin-byte_reverse-with-%: QEMU_OPTS+=-cpu POWER10 +else +byte_reverse: + $(call skip-test, "BUILD of $@", "missing compiler support") +run-byte_reverse: + $(call skip-test, "RUN of byte_reverse", "not built") +run-plugin-byte_reverse-with-%: + $(call skip-test, "RUN of byte_reverse ($*)", "not built") +endif + TESTS += $(PPC64LE_TESTS) diff --git a/tests/tcg/ppc64le/byte_reverse.c b/tests/tcg/ppc64le/byte_reverse.c new file mode 100644 index 0000000000..53b76fc2e2 --- /dev/null +++ b/tests/tcg/ppc64le/byte_reverse.c @@ -0,0 +1,21 @@ +#include <assert.h> + +int main(void) +{ + unsigned long var; + + var = 0xFEDCBA9876543210; + asm("brh %0, %0" : "+r"(var)); + assert(var == 0xDCFE98BA54761032); + + var = 0xFEDCBA9876543210; + asm("brw %0, %0" : "+r"(var)); + assert(var == 0x98BADCFE10325476); + + var = 0xFEDCBA9876543210; + asm("brd %0, %0" : "+r"(var)); + assert(var == 0x1032547698BADCFE); + + return 0; +} + diff --git a/tests/tcg/tricore/Makefile.softmmu-target b/tests/tcg/tricore/Makefile.softmmu-target new file mode 100644 index 0000000000..5007c60ce8 --- /dev/null +++ b/tests/tcg/tricore/Makefile.softmmu-target @@ -0,0 +1,26 @@ +TESTS_PATH = $(SRC_PATH)/tests/tcg/tricore + +LDFLAGS = -T$(TESTS_PATH)/link.ld +ASFLAGS = + +TESTS += test_abs.tst +TESTS += test_bmerge.tst +TESTS += test_clz.tst +TESTS += test_dvstep.tst +TESTS += test_fadd.tst +TESTS += test_fmul.tst +TESTS += test_ftoi.tst +TESTS += test_madd.tst +TESTS += test_msub.tst +TESTS += test_muls.tst + +QEMU_OPTS += -M tricore_testboard -nographic -kernel + +%.pS: $(TESTS_PATH)/%.S + $(HOST_CC) -E -o $@ $< + +%.o: %.pS + $(AS) $(ASFLAGS) -o $@ $< + +%.tst: %.o + $(LD) $(LDFLAGS) $< -o $@ diff --git a/tests/tcg/tricore/link.ld b/tests/tcg/tricore/link.ld new file mode 100644 index 0000000000..364bcdc00a --- /dev/null +++ b/tests/tcg/tricore/link.ld @@ -0,0 +1,60 @@ +/* Default linker script, for normal executables */ +OUTPUT_FORMAT("elf32-tricore") +OUTPUT_ARCH(tricore) +ENTRY(_start) + +/* the internal ram description */ +MEMORY +{ + text_ram (rx!p): org = 0x80000000, len = 15K + data_ram (w!xp): org = 0xd0000000, len = 130K +} +/* + * Define the sizes of the user and system stacks. + */ +__USTACK_SIZE = DEFINED (__USTACK_SIZE) ? __USTACK_SIZE : 1K ; +/* + * Define the start address and the size of the context save area. + */ +__CSA_BEGIN = 0xd0000000 ; +__CSA_SIZE = 8k ; +__CSA_END = __CSA_BEGIN + __CSA_SIZE ; + +SECTIONS +{ + .text : + { + *(.text) + . = ALIGN(8); + } > text_ram + + .rodata : + { + *(.rodata) + *(.rodata1) + } > data_ram + + .data : + { + . = ALIGN(8) ; + *(.data) + *(.data.*) + . = ALIGN(8) ; + __USTACK = . + __USTACK_SIZE -768; + + } > data_ram + /* + * Allocate space for BSS sections. + */ + .bss : + { + BSS_BASE = . ; + *(.bss) + *(COMMON) + . = ALIGN(8) ; + } > data_ram + /* Make sure CSA, stack and heap addresses are properly aligned. */ + _. = ASSERT ((__CSA_BEGIN & 0x3f) == 0 , "illegal CSA start address") ; + _. = ASSERT ((__CSA_SIZE & 0x3f) == 0 , "illegal CSA size") ; + +} diff --git a/tests/tcg/tricore/macros.h b/tests/tcg/tricore/macros.h new file mode 100644 index 0000000000..0d76fc403a --- /dev/null +++ b/tests/tcg/tricore/macros.h @@ -0,0 +1,129 @@ +/* Helpers */ +#define LI(reg, val) \ + mov.u reg, lo:val; \ + movh DREG_TEMP_LI, up:val; \ + or reg, reg, DREG_TEMP_LI; \ + +/* Address definitions */ +#define TESTDEV_ADDR 0xf0000000 +/* Register definitions */ +#define DREG_RS1 %d0 +#define DREG_RS2 %d1 +#define DREG_RS3 %d4 +#define DREG_CALC_RESULT %d1 +#define DREG_CALC_PSW %d2 +#define DREG_CORRECT_PSW %d3 +#define DREG_TEMP_LI %d10 +#define DREG_TEMP %d11 +#define DREG_TEST_NUM %d14 +#define DREG_CORRECT_RESULT %d15 + +#define DREG_DEV_ADDR %a15 + +#define EREG_RS1 %e6 +#define EREG_RS1_LO %d6 +#define EREG_RS1_HI %d7 +#define EREG_RS2 %e8 +#define EREG_RS2_LO %d8 +#define EREG_RS2_HI %d9 +#define EREG_CALC_RESULT %e8 +#define EREG_CALC_RESULT_HI %d9 +#define EREG_CALC_RESULT_LO %d8 +#define EREG_CORRECT_RESULT_LO %d0 +#define EREG_CORRECT_RESULT_HI %d1 + +/* Test case wrappers */ +#define TEST_CASE(num, testreg, correct, code...) \ +test_ ## num: \ + code; \ + LI(DREG_CORRECT_RESULT, correct) \ + mov DREG_TEST_NUM, num; \ + jne testreg, DREG_CORRECT_RESULT, fail \ + +#define TEST_CASE_E(num, correct_lo, correct_hi, code...) \ +test_ ## num: \ + code; \ + mov DREG_TEST_NUM, num; \ + LI(EREG_CORRECT_RESULT_LO, correct_lo) \ + jne EREG_CALC_RESULT_LO, EREG_CORRECT_RESULT_LO, fail; \ + LI(EREG_CORRECT_RESULT_HI, correct_hi) \ + jne EREG_CALC_RESULT_HI, EREG_CORRECT_RESULT_HI, fail; + +#define TEST_CASE_PSW(num, testreg, correct, correct_psw, code...) \ +test_ ## num: \ + code; \ + LI(DREG_CORRECT_RESULT, correct) \ + mov DREG_TEST_NUM, num; \ + jne testreg, DREG_CORRECT_RESULT, fail; \ + mfcr DREG_CALC_PSW, $psw; \ + LI(DREG_CORRECT_PSW, correct_psw) \ + mov DREG_TEST_NUM, num; \ + jne DREG_CALC_PSW, DREG_CORRECT_PSW, fail; + +/* Actual test case type + * e.g inst %dX, %dY -> TEST_D_D + * inst %dX, %dY, %dZ -> TEST_D_DD + * inst %eX, %dY, %dZ -> TEST_E_DD + */ +#define TEST_D_D(insn, num, result, rs1) \ + TEST_CASE(num, DREG_CALC_RESULT, result, \ + LI(DREG_RS1, rs1); \ + insn DREG_CALC_RESULT, DREG_RS1; \ + ) + +#define TEST_D_D_PSW(insn, num, result, psw, rs1) \ + TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ + LI(DREG_RS1, rs1); \ + rstv; \ + insn DREG_CORRECT_RESULT, DREG_RS1; \ + ) + +#define TEST_D_DD_PSW(insn, num, result, psw, rs1, rs2) \ + TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ + LI(DREG_RS1, rs1); \ + LI(DREG_RS2, rs2); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2; \ + ) + +#define TEST_D_DDD_PSW(insn, num, result, psw, rs1, rs2, rs3) \ + TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ + LI(DREG_RS1, rs1); \ + LI(DREG_RS2, rs2); \ + LI(DREG_RS3, rs3); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2, DREG_RS3; \ + ) + +#define TEST_D_DDI_PSW(insn, num, result, psw, rs1, rs2, imm) \ + TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ + LI(DREG_RS1, rs1); \ + LI(DREG_RS2, rs2); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2, imm; \ + ) + +#define TEST_E_ED(insn, num, res_hi, res_lo, rs1_hi, rs1_lo, rs2) \ + TEST_CASE_E(num, res_lo, res_hi, \ + LI(EREG_RS1_LO, rs1_lo); \ + LI(EREG_RS1_HI, rs1_hi); \ + LI(DREG_RS2, rs2); \ + insn EREG_CALC_RESULT, EREG_RS1, DREG_RS2; \ + ) + +/* Pass/Fail handling part */ +#define TEST_PASSFAIL \ + j pass; \ +fail: \ + LI(DREG_TEMP, TESTDEV_ADDR) \ + mov.a DREG_DEV_ADDR, DREG_TEMP; \ + st.w [DREG_DEV_ADDR], DREG_TEST_NUM;\ + debug; \ + j fail; \ +pass: \ + LI(DREG_TEMP, TESTDEV_ADDR) \ + mov.a DREG_DEV_ADDR, DREG_TEMP; \ + mov DREG_TEST_NUM, 0; \ + st.w [DREG_DEV_ADDR], DREG_TEST_NUM;\ + debug; \ + j pass; diff --git a/tests/tcg/tricore/test_abs.S b/tests/tcg/tricore/test_abs.S new file mode 100644 index 0000000000..e42240159a --- /dev/null +++ b/tests/tcg/tricore/test_abs.S @@ -0,0 +1,7 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_D(abs, 1, 0, 0) + + TEST_PASSFAIL diff --git a/tests/tcg/tricore/test_bmerge.S b/tests/tcg/tricore/test_bmerge.S new file mode 100644 index 0000000000..8a0fa6d3f6 --- /dev/null +++ b/tests/tcg/tricore/test_bmerge.S @@ -0,0 +1,8 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_DD_PSW(bmerge, 1, 0x555557f7, 0x00000b80, 0x0000001d, 0x0000ffff) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_clz.S b/tests/tcg/tricore/test_clz.S new file mode 100644 index 0000000000..e03835f123 --- /dev/null +++ b/tests/tcg/tricore/test_clz.S @@ -0,0 +1,9 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_D(cls.h, 1, 0x0, 0x6db17976) + TEST_D_D(cls.h, 2, 0x000f000f, 0x0) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_dvstep.S b/tests/tcg/tricore/test_dvstep.S new file mode 100644 index 0000000000..858dbc62dd --- /dev/null +++ b/tests/tcg/tricore/test_dvstep.S @@ -0,0 +1,15 @@ +#include "macros.h" +.text +.global _start +_start: + # Result RS1 RS2 + TEST_E_ED(dvstep, 1, 0x000001ff, 0xfffe5cff, 0x00000001, 0xfffffe5c, 0x0) + TEST_E_ED(dvstep, 2, 0x00000000, 0x000000ff, 0x00000000, 0x00000000, 0x0) + TEST_E_ED(dvstep, 3, 0x0000f000, 0x000000fd, 0x010000f0, 0x00000000, 0x0) + TEST_E_ED(dvstep, 4, 0xfffff000, 0x00000000, 0x7ffffff0, 0x00000000, 0x0) + TEST_E_ED(dvstep.u, 5, 0xffffff00, 0x100008ff, 0xffffffff, 0x00100008, 0x0) + TEST_E_ED(dvstep.u, 6, 0x00000100, 0x00000000, 0x08000001, 0x00000000, \ + 0xffffff2d) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_fadd.S b/tests/tcg/tricore/test_fadd.S new file mode 100644 index 0000000000..1a65054803 --- /dev/null +++ b/tests/tcg/tricore/test_fadd.S @@ -0,0 +1,16 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_DD_PSW(add.f, 1, 0x7fc00000, 0x00000b80, 0xffffff85, 0x00001234) + TEST_D_DD_PSW(add.f, 2, 0xf9c00000, 0x00000b80, 0xf9400000, 0xf9400000) + TEST_D_DD_PSW(add.f, 3, 0x8bb858ca, 0x00000b80, 0x8b3858ca, 0x8b3858ca) + TEST_D_DD_PSW(add.f, 4, 0x00000000, 0x00000b80, 0x000000ff, 0x00000000) + TEST_D_DD_PSW(add.f, 5, 0x7fc00000, 0x00000b80, 0xfffffe52, 0x0a4cf70c) + TEST_D_DD_PSW(add.f, 6, 0x9e6d5076, 0x84000b80, 0x9ded50ec, 0x9ded4fff) + TEST_D_DD_PSW(add.f, 7, 0x00000000, 0x04000b80, 0x0000e8bd, 0x00000000) + TEST_D_DD_PSW(add.f, 8, 0x7fc00000, 0xc4000b80, 0xffad546e, 0xffad546e) + TEST_D_DD_PSW(add.f, 9, 0x7fc00000, 0x04000b80, 0xfffe0000, 0x08130000) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_fmul.S b/tests/tcg/tricore/test_fmul.S new file mode 100644 index 0000000000..fb1f634b2d --- /dev/null +++ b/tests/tcg/tricore/test_fmul.S @@ -0,0 +1,8 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_DD_PSW(mul.f, 1, 0x974f4f0a, 0x84000b80, 0x1a0b1980, 0xbcbec42d) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_ftoi.S b/tests/tcg/tricore/test_ftoi.S new file mode 100644 index 0000000000..fb4af6b5aa --- /dev/null +++ b/tests/tcg/tricore/test_ftoi.S @@ -0,0 +1,10 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_D_PSW(ftoi, 1, 0x0, 0x84000b80, 0x05f6e605) + TEST_D_D_PSW(ftoi, 2, 0x0, 0x04000b80, 0x00012200) + TEST_D_D_PSW(ftoi, 3, 0x0, 0xc4000b80, 0xffffffff) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_madd.S b/tests/tcg/tricore/test_madd.S new file mode 100644 index 0000000000..5d839772bb --- /dev/null +++ b/tests/tcg/tricore/test_madd.S @@ -0,0 +1,11 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_DDI_PSW(madd, 1, 0x0000fffd, 0x60000b80, 0x0000ffff, 0x7fffffff,2) + TEST_D_DDI_PSW(madd, 2, 0xffff7fff, 0x60000b80, 0xffff8001, 0x7fffffff,2) + TEST_D_DDD_PSW(madds.u, 3, 0xffffffff, 0x60000b80, 0x00000000, 0x80000000, \ + 0x80000000) + + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_msub.S b/tests/tcg/tricore/test_msub.S new file mode 100644 index 0000000000..6dee87d99c --- /dev/null +++ b/tests/tcg/tricore/test_msub.S @@ -0,0 +1,9 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_DDI_PSW(msub, 1, 0xd2fbe5e0, 0x00000b80,0x64003300, 0xff5420d4, -216) + TEST_D_DDI_PSW(msub, 2, 0xfffffc10, 0x00000b80,0xfffffe68, 0xfffffffd, -200) + TEST_D_DDD_PSW(msubs.u, 3, 0x0, 0x60000b80, 0x1, 0xffffffff, 0xffffffdb) + TEST_PASSFAIL + diff --git a/tests/tcg/tricore/test_muls.S b/tests/tcg/tricore/test_muls.S new file mode 100644 index 0000000000..ca517556bc --- /dev/null +++ b/tests/tcg/tricore/test_muls.S @@ -0,0 +1,9 @@ +#include "macros.h" +.text +.global _start +_start: + TEST_D_DD_PSW(muls.u, 1, 0xffffffff, 0x78000b80, 0x80000001, 0xffffffff) + TEST_D_DD_PSW(muls.u, 2, 0xffffffff, 0x60000b80, 0xfffffffe, 0xffffffff) + + TEST_PASSFAIL + |