aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--block/backup.c150
-rw-r--r--block/io.c35
-rw-r--r--block/trace-events1
-rwxr-xr-xconfigure2
-rw-r--r--default-configs/ppc-softmmu.mak3
-rw-r--r--hw/display/cirrus_vga.c9
-rw-r--r--hw/display/qxl.c3
-rw-r--r--hw/display/vga-isa-mm.c3
-rw-r--r--hw/display/vga-isa.c3
-rw-r--r--hw/display/vga-pci.c5
-rw-r--r--hw/display/vga.c4
-rw-r--r--hw/display/vga_int.h3
-rw-r--r--hw/display/virtio-gpu.c64
-rw-r--r--hw/display/virtio-vga.c2
-rw-r--r--hw/display/vmware_vga.c4
-rw-r--r--hw/i2c/ppc4xx_i2c.c299
-rw-r--r--hw/intc/xics.c174
-rw-r--r--hw/intc/xics_kvm.c80
-rw-r--r--hw/intc/xics_pnv.c15
-rw-r--r--hw/misc/macio/mac_dbdma.c21
-rw-r--r--hw/ppc/Makefile.objs3
-rw-r--r--hw/ppc/mac_newworld.c4
-rw-r--r--hw/ppc/pnv_core.c1
-rw-r--r--hw/ppc/ppc440.h1
-rw-r--r--hw/ppc/ppc440_uc.c222
-rw-r--r--hw/ppc/sam460ex.c32
-rw-r--r--hw/ppc/spapr.c16
-rw-r--r--hw/ppc/spapr_caps.c13
-rw-r--r--hw/timer/Makefile.objs1
-rw-r--r--hw/timer/m41t80.c117
-rw-r--r--hw/usb/hcd-ehci.c3
-rw-r--r--hw/usb/hcd-xhci.c7
-rw-r--r--include/block/block.h5
-rw-r--r--include/hw/compat.h16
-rw-r--r--include/hw/i2c/ppc4xx_i2c.h3
-rw-r--r--include/hw/ppc/xics.h9
-rw-r--r--linux-user/openrisc/signal.c217
-rw-r--r--linux-user/openrisc/target_signal.h2
-rw-r--r--linux-user/openrisc/target_syscall.h28
-rw-r--r--linux-user/ppc/cpu_loop.c121
-rw-r--r--linux-user/signal.c2
-rwxr-xr-xscripts/qemu-binfmt-conf.sh10
-rw-r--r--target/openrisc/Makefile.objs5
-rw-r--r--target/openrisc/cpu.c17
-rw-r--r--target/openrisc/cpu.h61
-rw-r--r--target/openrisc/disas.c170
-rw-r--r--target/openrisc/helper.h4
-rw-r--r--target/openrisc/interrupt.c55
-rw-r--r--target/openrisc/interrupt_helper.c35
-rw-r--r--target/openrisc/machine.c44
-rw-r--r--target/openrisc/mmu.c279
-rw-r--r--target/openrisc/mmu_helper.c40
-rw-r--r--target/openrisc/sys_helper.c84
-rw-r--r--target/openrisc/translate.c303
-rw-r--r--target/ppc/cpu.h8
-rw-r--r--target/ppc/excp_helper.c18
-rw-r--r--target/ppc/fpu_helper.c8
-rw-r--r--target/ppc/helper.h11
-rw-r--r--target/ppc/internal.h5
-rw-r--r--target/ppc/kvm.c118
-rw-r--r--target/ppc/mem_helper.c72
-rw-r--r--target/ppc/translate.c641
-rw-r--r--target/ppc/translate_init.inc.c1
64 files changed, 2127 insertions, 1566 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 42a1892..6630d69 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -851,6 +851,7 @@ M: BALATON Zoltan <balaton@eik.bme.hu>
L: qemu-ppc@nongnu.org
S: Maintained
F: hw/ide/sii3112.c
+F: hw/timer/m41t80.c
SH4 Machines
------------
diff --git a/block/backup.c b/block/backup.c
index d18be40..81895dd 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -45,6 +45,8 @@ typedef struct BackupBlockJob {
QLIST_HEAD(, CowRequest) inflight_reqs;
HBitmap *copy_bitmap;
+ bool use_copy_range;
+ int64_t copy_range_size;
} BackupBlockJob;
static const BlockJobDriver backup_job_driver;
@@ -86,19 +88,101 @@ static void cow_request_end(CowRequest *req)
qemu_co_queue_restart_all(&req->wait_queue);
}
+/* Copy range to target with a bounce buffer and return the bytes copied. If
+ * error occured, return a negative error number */
+static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
+ int64_t start,
+ int64_t end,
+ bool is_write_notifier,
+ bool *error_is_read,
+ void **bounce_buffer)
+{
+ int ret;
+ struct iovec iov;
+ QEMUIOVector qiov;
+ BlockBackend *blk = job->common.blk;
+ int nbytes;
+
+ hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
+ nbytes = MIN(job->cluster_size, job->len - start);
+ if (!*bounce_buffer) {
+ *bounce_buffer = blk_blockalign(blk, job->cluster_size);
+ }
+ iov.iov_base = *bounce_buffer;
+ iov.iov_len = nbytes;
+ qemu_iovec_init_external(&qiov, &iov, 1);
+
+ ret = blk_co_preadv(blk, start, qiov.size, &qiov,
+ is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
+ if (ret < 0) {
+ trace_backup_do_cow_read_fail(job, start, ret);
+ if (error_is_read) {
+ *error_is_read = true;
+ }
+ goto fail;
+ }
+
+ if (qemu_iovec_is_zero(&qiov)) {
+ ret = blk_co_pwrite_zeroes(job->target, start,
+ qiov.size, BDRV_REQ_MAY_UNMAP);
+ } else {
+ ret = blk_co_pwritev(job->target, start,
+ qiov.size, &qiov,
+ job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
+ }
+ if (ret < 0) {
+ trace_backup_do_cow_write_fail(job, start, ret);
+ if (error_is_read) {
+ *error_is_read = false;
+ }
+ goto fail;
+ }
+
+ return nbytes;
+fail:
+ hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
+ return ret;
+
+}
+
+/* Copy range to target and return the bytes copied. If error occured, return a
+ * negative error number. */
+static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
+ int64_t start,
+ int64_t end,
+ bool is_write_notifier)
+{
+ int ret;
+ int nr_clusters;
+ BlockBackend *blk = job->common.blk;
+ int nbytes;
+
+ assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
+ nbytes = MIN(job->copy_range_size, end - start);
+ nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
+ hbitmap_reset(job->copy_bitmap, start / job->cluster_size,
+ nr_clusters);
+ ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
+ is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
+ if (ret < 0) {
+ trace_backup_do_cow_copy_range_fail(job, start, ret);
+ hbitmap_set(job->copy_bitmap, start / job->cluster_size,
+ nr_clusters);
+ return ret;
+ }
+
+ return nbytes;
+}
+
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
int64_t offset, uint64_t bytes,
bool *error_is_read,
bool is_write_notifier)
{
- BlockBackend *blk = job->common.blk;
CowRequest cow_request;
- struct iovec iov;
- QEMUIOVector bounce_qiov;
- void *bounce_buffer = NULL;
int ret = 0;
int64_t start, end; /* bytes */
- int n; /* bytes */
+ void *bounce_buffer = NULL;
qemu_co_rwlock_rdlock(&job->flush_rwlock);
@@ -110,60 +194,38 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
wait_for_overlapping_requests(job, start, end);
cow_request_begin(&cow_request, job, start, end);
- for (; start < end; start += job->cluster_size) {
+ while (start < end) {
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
trace_backup_do_cow_skip(job, start);
+ start += job->cluster_size;
continue; /* already copied */
}
- hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
trace_backup_do_cow_process(job, start);
- n = MIN(job->cluster_size, job->len - start);
-
- if (!bounce_buffer) {
- bounce_buffer = blk_blockalign(blk, job->cluster_size);
- }
- iov.iov_base = bounce_buffer;
- iov.iov_len = n;
- qemu_iovec_init_external(&bounce_qiov, &iov, 1);
-
- ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
- is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
- if (ret < 0) {
- trace_backup_do_cow_read_fail(job, start, ret);
- if (error_is_read) {
- *error_is_read = true;
+ if (job->use_copy_range) {
+ ret = backup_cow_with_offload(job, start, end, is_write_notifier);
+ if (ret < 0) {
+ job->use_copy_range = false;
}
- hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
- goto out;
}
-
- if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
- ret = blk_co_pwrite_zeroes(job->target, start,
- bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
- } else {
- ret = blk_co_pwritev(job->target, start,
- bounce_qiov.size, &bounce_qiov,
- job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
+ if (!job->use_copy_range) {
+ ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier,
+ error_is_read, &bounce_buffer);
}
if (ret < 0) {
- trace_backup_do_cow_write_fail(job, start, ret);
- if (error_is_read) {
- *error_is_read = false;
- }
- hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
- goto out;
+ break;
}
/* Publish progress, guest I/O counts as progress too. Note that the
* offset field is an opaque progress value, it is not a disk offset.
*/
- job->bytes_read += n;
- job_progress_update(&job->common.job, n);
+ start += ret;
+ job->bytes_read += ret;
+ job_progress_update(&job->common.job, ret);
+ ret = 0;
}
-out:
if (bounce_buffer) {
qemu_vfree(bounce_buffer);
}
@@ -665,6 +727,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
} else {
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
}
+ job->use_copy_range = true;
+ job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
+ blk_get_max_transfer(job->target));
+ job->copy_range_size = MAX(job->cluster_size,
+ QEMU_ALIGN_UP(job->copy_range_size,
+ job->cluster_size));
/* Required permissions are already taken with target's blk_new() */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
diff --git a/block/io.c b/block/io.c
index 7035b78..1a2272f 100644
--- a/block/io.c
+++ b/block/io.c
@@ -2897,18 +2897,11 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src,
bool recurse_src)
{
BdrvTrackedRequest src_req, dst_req;
- BlockDriverState *src_bs = src->bs;
- BlockDriverState *dst_bs = dst->bs;
int ret;
- if (!src || !dst || !src->bs || !dst->bs) {
+ if (!dst || !dst->bs) {
return -ENOMEDIUM;
}
- ret = bdrv_check_byte_request(src->bs, src_offset, bytes);
- if (ret) {
- return ret;
- }
-
ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes);
if (ret) {
return ret;
@@ -2917,20 +2910,30 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src,
return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags);
}
+ if (!src || !src->bs) {
+ return -ENOMEDIUM;
+ }
+ ret = bdrv_check_byte_request(src->bs, src_offset, bytes);
+ if (ret) {
+ return ret;
+ }
+
if (!src->bs->drv->bdrv_co_copy_range_from
|| !dst->bs->drv->bdrv_co_copy_range_to
|| src->bs->encrypted || dst->bs->encrypted) {
return -ENOTSUP;
}
- bdrv_inc_in_flight(src_bs);
- bdrv_inc_in_flight(dst_bs);
- tracked_request_begin(&src_req, src_bs, src_offset,
+ bdrv_inc_in_flight(src->bs);
+ bdrv_inc_in_flight(dst->bs);
+ tracked_request_begin(&src_req, src->bs, src_offset,
bytes, BDRV_TRACKED_READ);
- tracked_request_begin(&dst_req, dst_bs, dst_offset,
+ tracked_request_begin(&dst_req, dst->bs, dst_offset,
bytes, BDRV_TRACKED_WRITE);
- wait_serialising_requests(&src_req);
- wait_serialising_requests(&dst_req);
+ if (!(flags & BDRV_REQ_NO_SERIALISING)) {
+ wait_serialising_requests(&src_req);
+ wait_serialising_requests(&dst_req);
+ }
if (recurse_src) {
ret = src->bs->drv->bdrv_co_copy_range_from(src->bs,
src, src_offset,
@@ -2944,8 +2947,8 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src,
}
tracked_request_end(&src_req);
tracked_request_end(&dst_req);
- bdrv_dec_in_flight(src_bs);
- bdrv_dec_in_flight(dst_bs);
+ bdrv_dec_in_flight(src->bs);
+ bdrv_dec_in_flight(dst->bs);
return ret;
}
diff --git a/block/trace-events b/block/trace-events
index 2d59b53..c35287b 100644
--- a/block/trace-events
+++ b/block/trace-events
@@ -42,6 +42,7 @@ backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64
backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
+backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
# blockdev.c
qmp_block_job_cancel(void *job) "job %p"
diff --git a/configure b/configure
index dcb605d..b99ebdd 100755
--- a/configure
+++ b/configure
@@ -3883,7 +3883,7 @@ libs_softmmu="$libs_softmmu $fdt_libs"
# opengl probe (for sdl2, gtk, milkymist-tmu2)
if test "$opengl" != "no" ; then
- opengl_pkgs="epoxy libdrm gbm"
+ opengl_pkgs="epoxy gbm"
if $pkg_config $opengl_pkgs; then
opengl_cflags="$($pkg_config --cflags $opengl_pkgs)"
opengl_libs="$($pkg_config --libs $opengl_pkgs)"
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index 851b4af..6f12bf8 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -22,11 +22,14 @@ CONFIG_OPENPIC_KVM=$(call land,$(CONFIG_E500),$(CONFIG_KVM))
CONFIG_PLATFORM_BUS=y
CONFIG_ETSEC=y
# For Sam460ex
+CONFIG_SAM460EX=y
CONFIG_USB_EHCI_SYSBUS=y
CONFIG_SM501=y
CONFIG_IDE_SII3112=y
CONFIG_I2C=y
CONFIG_BITBANG_I2C=y
+CONFIG_M41T80=y
+CONFIG_VGA_CIRRUS=y
# For Macs
CONFIG_MAC=y
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index 5e44f00..7583b18 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -3048,7 +3048,8 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp)
s->vram_size_mb);
return;
}
- vga_common_init(s, OBJECT(dev), true);
+ s->global_vmstate = true;
+ vga_common_init(s, OBJECT(dev));
cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0,
isa_address_space(isadev),
isa_address_space_io(isadev));
@@ -3062,7 +3063,7 @@ static Property isa_cirrus_vga_properties[] = {
DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState,
cirrus_vga.vga.vram_size_mb, 4),
DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState,
- cirrus_vga.enable_blitter, true),
+ cirrus_vga.enable_blitter, true),
DEFINE_PROP_END_OF_LIST(),
};
@@ -3105,7 +3106,7 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp)
return;
}
/* setup VGA */
- vga_common_init(&s->vga, OBJECT(dev), true);
+ vga_common_init(&s->vga, OBJECT(dev));
cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev),
pci_address_space_io(dev));
s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga);
@@ -3134,6 +3135,8 @@ static Property pci_vga_cirrus_properties[] = {
cirrus_vga.vga.vram_size_mb, 4),
DEFINE_PROP_BOOL("blitter", struct PCICirrusVGAState,
cirrus_vga.enable_blitter, true),
+ DEFINE_PROP_BOOL("global-vmstate", struct PCICirrusVGAState,
+ cirrus_vga.vga.global_vmstate, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index b09a039..830c392 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -2168,7 +2168,7 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp)
qxl_init_ramsize(qxl);
vga->vbe_size = qxl->vgamem_size;
vga->vram_size_mb = qxl->vga.vram_size / MiB;
- vga_common_init(vga, OBJECT(dev), true);
+ vga_common_init(vga, OBJECT(dev));
vga_init(vga, OBJECT(dev),
pci_address_space(dev), pci_address_space_io(dev), false);
portio_list_init(&qxl->vga_port_list, OBJECT(dev), qxl_vga_portio_list,
@@ -2408,6 +2408,7 @@ static Property qxl_properties[] = {
#endif
DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0),
DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0),
+ DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c
index bd58141..232216c 100644
--- a/hw/display/vga-isa-mm.c
+++ b/hw/display/vga-isa-mm.c
@@ -132,7 +132,8 @@ int isa_vga_mm_init(hwaddr vram_base,
s = g_malloc0(sizeof(*s));
s->vga.vram_size_mb = VGA_RAM_SIZE / MiB;
- vga_common_init(&s->vga, NULL, true);
+ s->vga.global_vmstate = true;
+ vga_common_init(&s->vga, NULL);
vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s);
diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c
index 469834a..fa44242 100644
--- a/hw/display/vga-isa.c
+++ b/hw/display/vga-isa.c
@@ -58,7 +58,8 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp)
MemoryRegion *vga_io_memory;
const MemoryRegionPortio *vga_ports, *vbe_ports;
- vga_common_init(s, OBJECT(dev), true);
+ s->global_vmstate = true;
+ vga_common_init(s, OBJECT(dev));
s->legacy_address_space = isa_address_space(isadev);
vga_io_memory = vga_init_io(s, OBJECT(dev), &vga_ports, &vbe_ports);
isa_register_portio_list(isadev, &d->portio_vga,
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index 1ea5597..e9e62ea 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -222,7 +222,7 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp)
bool qext = false;
/* vga + console init */
- vga_common_init(s, OBJECT(dev), true);
+ vga_common_init(s, OBJECT(dev));
vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev),
true);
@@ -265,7 +265,7 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp)
bool qext = false;
/* vga + console init */
- vga_common_init(s, OBJECT(dev), false);
+ vga_common_init(s, OBJECT(dev));
s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s);
/* mmio bar */
@@ -308,6 +308,7 @@ static Property vga_pci_properties[] = {
DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true),
DEFINE_PROP_BIT("qemu-extended-regs",
PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true),
+ DEFINE_PROP_BOOL("global-vmstate", PCIVGAState, vga.global_vmstate, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/display/vga.c b/hw/display/vga.c
index d759918..802cfd4 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -2164,7 +2164,7 @@ static inline uint32_t uint_clamp(uint32_t val, uint32_t vmin, uint32_t vmax)
return val;
}
-void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate)
+void vga_common_init(VGACommonState *s, Object *obj)
{
int i, j, v, b;
@@ -2203,7 +2203,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate)
s->is_vbe_vmstate = 1;
memory_region_init_ram_nomigrate(&s->vram, obj, "vga.vram", s->vram_size,
&error_fatal);
- vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj));
+ vmstate_register_ram(&s->vram, s->global_vmstate ? NULL : DEVICE(obj));
xen_register_framebuffer(&s->vram);
s->vram_ptr = memory_region_get_ram_ptr(&s->vram);
s->get_bpp = vga_get_bpp;
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index f8fcf62..339661b 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -133,6 +133,7 @@ typedef struct VGACommonState {
bool full_update_gfx;
bool big_endian_fb;
bool default_endian_fb;
+ bool global_vmstate;
/* hardware mouse cursor support */
uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32];
uint32_t hw_cursor_x;
@@ -157,7 +158,7 @@ static inline int c6_to_8(int v)
return (v << 2) | (b << 1) | b;
}
-void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate);
+void vga_common_init(VGACommonState *s, Object *obj);
void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space,
MemoryRegion *address_space_io, bool init_vga_ports);
MemoryRegion *vga_init_io(VGACommonState *s, Object *obj,
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 71a0071..ec366f4 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -400,9 +400,47 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
g->hostmem += res->hostmem;
}
+static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id)
+{
+ struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id];
+ struct virtio_gpu_simple_resource *res;
+ DisplaySurface *ds = NULL;
+
+ if (scanout->resource_id == 0) {
+ return;
+ }
+
+ res = virtio_gpu_find_resource(g, scanout->resource_id);
+ if (res) {
+ res->scanout_bitmask &= ~(1 << scanout_id);
+ }
+
+ if (scanout_id == 0) {
+ /* primary head */
+ ds = qemu_create_message_surface(scanout->width ?: 640,
+ scanout->height ?: 480,
+ "Guest disabled display.");
+ }
+ dpy_gfx_replace_surface(scanout->con, ds);
+ scanout->resource_id = 0;
+ scanout->ds = NULL;
+ scanout->width = 0;
+ scanout->height = 0;
+}
+
static void virtio_gpu_resource_destroy(VirtIOGPU *g,
struct virtio_gpu_simple_resource *res)
{
+ int i;
+
+ if (res->scanout_bitmask) {
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ if (res->scanout_bitmask & (1 << i)) {
+ virtio_gpu_disable_scanout(g, i);
+ }
+ }
+ }
+
pixman_image_unref(res->image);
virtio_gpu_cleanup_mapping(res);
QTAILQ_REMOVE(&g->reslist, res, next);
@@ -563,7 +601,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data)
static void virtio_gpu_set_scanout(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
- struct virtio_gpu_simple_resource *res;
+ struct virtio_gpu_simple_resource *res, *ores;
struct virtio_gpu_scanout *scanout;
pixman_format_code_t format;
uint32_t offset;
@@ -584,24 +622,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
g->enable = 1;
if (ss.resource_id == 0) {
- scanout = &g->scanout[ss.scanout_id];
- if (scanout->resource_id) {
- res = virtio_gpu_find_resource(g, scanout->resource_id);
- if (res) {
- res->scanout_bitmask &= ~(1 << ss.scanout_id);
- }
- }
- if (ss.scanout_id == 0) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: illegal scanout id specified %d",
- __func__, ss.scanout_id);
- cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
- return;
- }
- dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
- scanout->ds = NULL;
- scanout->width = 0;
- scanout->height = 0;
+ virtio_gpu_disable_scanout(g, ss.scanout_id);
return;
}
@@ -654,6 +675,11 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds);
}
+ ores = virtio_gpu_find_resource(g, scanout->resource_id);
+ if (ores) {
+ ores->scanout_bitmask &= ~(1 << ss.scanout_id);
+ }
+
res->scanout_bitmask |= (1 << ss.scanout_id);
scanout->resource_id = ss.resource_id;
scanout->x = ss.r.x;
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index 97db6c3..8d3d9e1 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -106,7 +106,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
/* init vga compat bits */
vga->vram_size_mb = 8;
- vga_common_init(vga, OBJECT(vpci_dev), false);
+ vga_common_init(vga, OBJECT(vpci_dev));
vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev),
pci_address_space_io(&vpci_dev->pci_dev), true);
pci_register_bar(&vpci_dev->pci_dev, 0,
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index 08deb08..0bbb78b 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -1242,7 +1242,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s,
&error_fatal);
s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram);
- vga_common_init(&s->vga, OBJECT(dev), true);
+ vga_common_init(&s->vga, OBJECT(dev));
vga_init(&s->vga, OBJECT(dev), address_space, io, true);
vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
s->new_depth = 32;
@@ -1322,6 +1322,8 @@ static void pci_vmsvga_realize(PCIDevice *dev, Error **errp)
static Property vga_vmware_properties[] = {
DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s,
chip.vga.vram_size_mb, 16),
+ DEFINE_PROP_BOOL("global-vmstate", struct pci_vmsvga_state_s,
+ chip.vga.global_vmstate, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c
index fca80d6..d6dfafa 100644
--- a/hw/i2c/ppc4xx_i2c.c
+++ b/hw/i2c/ppc4xx_i2c.c
@@ -34,16 +34,50 @@
#define PPC4xx_I2C_MEM_SIZE 18
+enum {
+ IIC_MDBUF = 0,
+ /* IIC_SDBUF = 2, */
+ IIC_LMADR = 4,
+ IIC_HMADR,
+ IIC_CNTL,
+ IIC_MDCNTL,
+ IIC_STS,
+ IIC_EXTSTS,
+ IIC_LSADR,
+ IIC_HSADR,
+ IIC_CLKDIV,
+ IIC_INTRMSK,
+ IIC_XFRCNT,
+ IIC_XTCNTLSS,
+ IIC_DIRECTCNTL
+ /* IIC_INTR */
+};
+
#define IIC_CNTL_PT (1 << 0)
#define IIC_CNTL_READ (1 << 1)
#define IIC_CNTL_CHT (1 << 2)
#define IIC_CNTL_RPST (1 << 3)
+#define IIC_CNTL_AMD (1 << 6)
+#define IIC_CNTL_HMT (1 << 7)
+
+#define IIC_MDCNTL_EINT (1 << 2)
+#define IIC_MDCNTL_ESM (1 << 3)
+#define IIC_MDCNTL_FMDB (1 << 6)
#define IIC_STS_PT (1 << 0)
+#define IIC_STS_IRQA (1 << 1)
#define IIC_STS_ERR (1 << 2)
+#define IIC_STS_MDBF (1 << 4)
#define IIC_STS_MDBS (1 << 5)
#define IIC_EXTSTS_XFRA (1 << 0)
+#define IIC_EXTSTS_BCS_FREE (4 << 4)
+#define IIC_EXTSTS_BCS_BUSY (5 << 4)
+
+#define IIC_INTRMSK_EIMTC (1 << 0)
+#define IIC_INTRMSK_EITA (1 << 1)
+#define IIC_INTRMSK_EIIC (1 << 2)
+#define IIC_INTRMSK_EIHE (1 << 3)
#define IIC_XTCNTLSS_SRST (1 << 0)
@@ -56,130 +90,83 @@ static void ppc4xx_i2c_reset(DeviceState *s)
{
PPC4xxI2CState *i2c = PPC4xx_I2C(s);
- /* FIXME: Should also reset bus?
- *if (s->address != ADDR_RESET) {
- * i2c_end_transfer(s->bus);
- *}
- */
-
- i2c->mdata = 0;
- i2c->lmadr = 0;
- i2c->hmadr = 0;
+ i2c->mdidx = -1;
+ memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata));
+ /* [hl][ms]addr are not affected by reset */
i2c->cntl = 0;
i2c->mdcntl = 0;
i2c->sts = 0;
- i2c->extsts = 0x8f;
- i2c->lsadr = 0;
- i2c->hsadr = 0;
+ i2c->extsts = IIC_EXTSTS_BCS_FREE;
i2c->clkdiv = 0;
i2c->intrmsk = 0;
i2c->xfrcnt = 0;
i2c->xtcntlss = 0;
- i2c->directcntl = 0xf;
-}
-
-static inline bool ppc4xx_i2c_is_master(PPC4xxI2CState *i2c)
-{
- return true;
+ i2c->directcntl = 0xf; /* all non-reserved bits set */
}
static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size)
{
PPC4xxI2CState *i2c = PPC4xx_I2C(opaque);
uint64_t ret;
+ int i;
switch (addr) {
- case 0:
- ret = i2c->mdata;
- if (ppc4xx_i2c_is_master(i2c)) {
+ case IIC_MDBUF:
+ if (i2c->mdidx < 0) {
ret = 0xff;
-
- if (!(i2c->sts & IIC_STS_MDBS)) {
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read "
- "without starting transfer\n",
- TYPE_PPC4xx_I2C, __func__);
- } else {
- int pending = (i2c->cntl >> 4) & 3;
-
- /* get the next byte */
- int byte = i2c_recv(i2c->bus);
-
- if (byte < 0) {
- qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed "
- "for device 0x%02x\n", TYPE_PPC4xx_I2C,
- __func__, i2c->lmadr);
- ret = 0xff;
- } else {
- ret = byte;
- /* Raise interrupt if enabled */
- /*ppc4xx_i2c_raise_interrupt(i2c)*/;
- }
-
- if (!pending) {
- i2c->sts &= ~IIC_STS_MDBS;
- /*i2c_end_transfer(i2c->bus);*/
- /*} else if (i2c->cntl & (IIC_CNTL_RPST | IIC_CNTL_CHT)) {*/
- } else if (pending) {
- /* current smbus implementation doesn't like
- multibyte xfer repeated start */
- i2c_end_transfer(i2c->bus);
- if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 1)) {
- /* if non zero is returned, the adress is not valid */
- i2c->sts &= ~IIC_STS_PT;
- i2c->sts |= IIC_STS_ERR;
- i2c->extsts |= IIC_EXTSTS_XFRA;
- } else {
- /*i2c->sts |= IIC_STS_PT;*/
- i2c->sts |= IIC_STS_MDBS;
- i2c->sts &= ~IIC_STS_ERR;
- i2c->extsts = 0;
- }
- }
- pending--;
- i2c->cntl = (i2c->cntl & 0xcf) | (pending << 4);
- }
- } else {
- qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n",
- TYPE_PPC4xx_I2C, __func__);
+ break;
+ }
+ ret = i2c->mdata[0];
+ if (i2c->mdidx == 3) {
+ i2c->sts &= ~IIC_STS_MDBF;
+ } else if (i2c->mdidx == 0) {
+ i2c->sts &= ~IIC_STS_MDBS;
+ }
+ for (i = 0; i < i2c->mdidx; i++) {
+ i2c->mdata[i] = i2c->mdata[i + 1];
+ }
+ if (i2c->mdidx >= 0) {
+ i2c->mdidx--;
}
break;
- case 4:
+ case IIC_LMADR:
ret = i2c->lmadr;
break;
- case 5:
+ case IIC_HMADR:
ret = i2c->hmadr;
break;
- case 6:
+ case IIC_CNTL:
ret = i2c->cntl;
break;
- case 7:
+ case IIC_MDCNTL:
ret = i2c->mdcntl;
break;
- case 8:
+ case IIC_STS:
ret = i2c->sts;
break;
- case 9:
- ret = i2c->extsts;
+ case IIC_EXTSTS:
+ ret = i2c_bus_busy(i2c->bus) ?
+ IIC_EXTSTS_BCS_BUSY : IIC_EXTSTS_BCS_FREE;
break;
- case 10:
+ case IIC_LSADR:
ret = i2c->lsadr;
break;
- case 11:
+ case IIC_HSADR:
ret = i2c->hsadr;
break;
- case 12:
+ case IIC_CLKDIV:
ret = i2c->clkdiv;
break;
- case 13:
+ case IIC_INTRMSK:
ret = i2c->intrmsk;
break;
- case 14:
+ case IIC_XFRCNT:
ret = i2c->xfrcnt;
break;
- case 15:
+ case IIC_XTCNTLSS:
ret = i2c->xtcntlss;
break;
- case 16:
+ case IIC_DIRECTCNTL:
ret = i2c->directcntl;
break;
default:
@@ -202,99 +189,127 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value,
PPC4xxI2CState *i2c = opaque;
switch (addr) {
- case 0:
- i2c->mdata = value;
- if (!i2c_bus_busy(i2c->bus)) {
- /* assume we start a write transfer */
- if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 0)) {
- /* if non zero is returned, the adress is not valid */
- i2c->sts &= ~IIC_STS_PT;
- i2c->sts |= IIC_STS_ERR;
- i2c->extsts |= IIC_EXTSTS_XFRA;
- } else {
- i2c->sts |= IIC_STS_PT;
- i2c->sts &= ~IIC_STS_ERR;
- i2c->extsts = 0;
- }
+ case IIC_MDBUF:
+ if (i2c->mdidx >= 3) {
+ break;
}
- if (i2c_bus_busy(i2c->bus)) {
- if (i2c_send(i2c->bus, i2c->mdata)) {
- /* if the target return non zero then end the transfer */
- i2c->sts &= ~IIC_STS_PT;
- i2c->sts |= IIC_STS_ERR;
- i2c->extsts |= IIC_EXTSTS_XFRA;
- i2c_end_transfer(i2c->bus);
- }
+ i2c->mdata[++i2c->mdidx] = value;
+ if (i2c->mdidx == 3) {
+ i2c->sts |= IIC_STS_MDBF;
+ } else if (i2c->mdidx == 0) {
+ i2c->sts |= IIC_STS_MDBS;
}
break;
- case 4:
+ case IIC_LMADR:
i2c->lmadr = value;
- if (i2c_bus_busy(i2c->bus)) {
- i2c_end_transfer(i2c->bus);
- }
break;
- case 5:
+ case IIC_HMADR:
i2c->hmadr = value;
break;
- case 6:
- i2c->cntl = value;
- if (i2c->cntl & IIC_CNTL_PT) {
- if (i2c->cntl & IIC_CNTL_READ) {
- if (i2c_bus_busy(i2c->bus)) {
- /* end previous transfer */
- i2c->sts &= ~IIC_STS_PT;
- i2c_end_transfer(i2c->bus);
+ case IIC_CNTL:
+ i2c->cntl = value & ~IIC_CNTL_PT;
+ if (value & IIC_CNTL_AMD) {
+ qemu_log_mask(LOG_UNIMP, "%s: only 7 bit addresses supported\n",
+ __func__);
+ }
+ if (value & IIC_CNTL_HMT && i2c_bus_busy(i2c->bus)) {
+ i2c_end_transfer(i2c->bus);
+ if (i2c->mdcntl & IIC_MDCNTL_EINT &&
+ i2c->intrmsk & IIC_INTRMSK_EIHE) {
+ i2c->sts |= IIC_STS_IRQA;
+ qemu_irq_raise(i2c->irq);
+ }
+ } else if (value & IIC_CNTL_PT) {
+ int recv = (value & IIC_CNTL_READ) >> 1;
+ int tct = value >> 4 & 3;
+ int i;
+
+ if (recv && (i2c->lmadr >> 1) >= 0x50 && (i2c->lmadr >> 1) < 0x58) {
+ /* smbus emulation does not like multi byte reads w/o restart */
+ value |= IIC_CNTL_RPST;
+ }
+
+ for (i = 0; i <= tct; i++) {
+ if (!i2c_bus_busy(i2c->bus)) {
+ i2c->extsts = IIC_EXTSTS_BCS_FREE;
+ if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, recv)) {
+ i2c->sts |= IIC_STS_ERR;
+ i2c->extsts |= IIC_EXTSTS_XFRA;
+ break;
+ } else {
+ i2c->sts &= ~IIC_STS_ERR;
+ }
}
- if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 1)) {
- /* if non zero is returned, the adress is not valid */
- i2c->sts &= ~IIC_STS_PT;
+ if (!(i2c->sts & IIC_STS_ERR) &&
+ i2c_send_recv(i2c->bus, &i2c->mdata[i], !recv)) {
i2c->sts |= IIC_STS_ERR;
i2c->extsts |= IIC_EXTSTS_XFRA;
- } else {
- /*i2c->sts |= IIC_STS_PT;*/
- i2c->sts |= IIC_STS_MDBS;
- i2c->sts &= ~IIC_STS_ERR;
- i2c->extsts = 0;
+ break;
+ }
+ if (value & IIC_CNTL_RPST || !(value & IIC_CNTL_CHT)) {
+ i2c_end_transfer(i2c->bus);
}
- } else {
- /* we actually already did the write transfer... */
- i2c->sts &= ~IIC_STS_PT;
+ }
+ i2c->xfrcnt = i;
+ i2c->mdidx = i - 1;
+ if (recv && i2c->mdidx >= 0) {
+ i2c->sts |= IIC_STS_MDBS;
+ }
+ if (recv && i2c->mdidx == 3) {
+ i2c->sts |= IIC_STS_MDBF;
+ }
+ if (i && i2c->mdcntl & IIC_MDCNTL_EINT &&
+ i2c->intrmsk & IIC_INTRMSK_EIMTC) {
+ i2c->sts |= IIC_STS_IRQA;
+ qemu_irq_raise(i2c->irq);
}
}
break;
- case 7:
- i2c->mdcntl = value & 0xdf;
+ case IIC_MDCNTL:
+ i2c->mdcntl = value & 0x3d;
+ if (value & IIC_MDCNTL_ESM) {
+ qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n",
+ __func__);
+ }
+ if (value & IIC_MDCNTL_FMDB) {
+ i2c->mdidx = -1;
+ memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata));
+ i2c->sts &= ~(IIC_STS_MDBF | IIC_STS_MDBS);
+ }
break;
- case 8:
- i2c->sts &= ~(value & 0xa);
+ case IIC_STS:
+ i2c->sts &= ~(value & 0x0a);
+ if (value & IIC_STS_IRQA && i2c->mdcntl & IIC_MDCNTL_EINT) {
+ qemu_irq_lower(i2c->irq);
+ }
break;
- case 9:
+ case IIC_EXTSTS:
i2c->extsts &= ~(value & 0x8f);
break;
- case 10:
+ case IIC_LSADR:
i2c->lsadr = value;
break;
- case 11:
+ case IIC_HSADR:
i2c->hsadr = value;
break;
- case 12:
+ case IIC_CLKDIV:
i2c->clkdiv = value;
break;
- case 13:
+ case IIC_INTRMSK:
i2c->intrmsk = value;
break;
- case 14:
+ case IIC_XFRCNT:
i2c->xfrcnt = value & 0x77;
break;
- case 15:
+ case IIC_XTCNTLSS:
+ i2c->xtcntlss &= ~(value & 0xf0);
if (value & IIC_XTCNTLSS_SRST) {
/* Is it actually a full reset? U-Boot sets some regs before */
ppc4xx_i2c_reset(DEVICE(i2c));
break;
}
- i2c->xtcntlss = value;
break;
- case 16:
+ case IIC_DIRECTCNTL:
i2c->directcntl = value & (IIC_DIRECTCNTL_SDAC & IIC_DIRECTCNTL_SCLC);
i2c->directcntl |= (value & IIC_DIRECTCNTL_SCLC ? 1 : 0);
bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SCL,
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index e73e623..b9f1a3c 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -294,7 +294,6 @@ static const VMStateDescription vmstate_icp_server = {
static void icp_reset(void *dev)
{
ICPState *icp = ICP(dev);
- ICPStateClass *icpc = ICP_GET_CLASS(icp);
icp->xirr = 0;
icp->pending_priority = 0xff;
@@ -302,16 +301,11 @@ static void icp_reset(void *dev)
/* Make all outputs are deasserted */
qemu_set_irq(icp->output, 0);
-
- if (icpc->reset) {
- icpc->reset(icp);
- }
}
static void icp_realize(DeviceState *dev, Error **errp)
{
ICPState *icp = ICP(dev);
- ICPStateClass *icpc = ICP_GET_CLASS(dev);
PowerPCCPU *cpu;
CPUPPCState *env;
Object *obj;
@@ -351,10 +345,6 @@ static void icp_realize(DeviceState *dev, Error **errp)
return;
}
- if (icpc->realize) {
- icpc->realize(icp, errp);
- }
-
qemu_register_reset(icp_reset, dev);
vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp);
}
@@ -547,9 +537,61 @@ static void ics_simple_eoi(ICSState *ics, uint32_t nr)
}
}
-static void ics_simple_reset(void *dev)
+static void ics_simple_reset(DeviceState *dev)
+{
+ ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev);
+
+ icsc->parent_reset(dev);
+}
+
+static void ics_simple_reset_handler(void *dev)
+{
+ ics_simple_reset(dev);
+}
+
+static void ics_simple_realize(DeviceState *dev, Error **errp)
{
ICSState *ics = ICS_SIMPLE(dev);
+ ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics);
+ Error *local_err = NULL;
+
+ icsc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs);
+
+ qemu_register_reset(ics_simple_reset_handler, ics);
+}
+
+static void ics_simple_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ICSStateClass *isc = ICS_BASE_CLASS(klass);
+
+ device_class_set_parent_realize(dc, ics_simple_realize,
+ &isc->parent_realize);
+ device_class_set_parent_reset(dc, ics_simple_reset,
+ &isc->parent_reset);
+
+ isc->reject = ics_simple_reject;
+ isc->resend = ics_simple_resend;
+ isc->eoi = ics_simple_eoi;
+}
+
+static const TypeInfo ics_simple_info = {
+ .name = TYPE_ICS_SIMPLE,
+ .parent = TYPE_ICS_BASE,
+ .instance_size = sizeof(ICSState),
+ .class_init = ics_simple_class_init,
+ .class_size = sizeof(ICSStateClass),
+};
+
+static void ics_base_reset(DeviceState *dev)
+{
+ ICSState *ics = ICS_BASE(dev);
int i;
uint8_t flags[ics->nr_irqs];
@@ -566,7 +608,35 @@ static void ics_simple_reset(void *dev)
}
}
-static int ics_simple_dispatch_pre_save(void *opaque)
+static void ics_base_realize(DeviceState *dev, Error **errp)
+{
+ ICSState *ics = ICS_BASE(dev);
+ Object *obj;
+ Error *err = NULL;
+
+ obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &err);
+ if (!obj) {
+ error_propagate(errp, err);
+ error_prepend(errp, "required link '" ICS_PROP_XICS "' not found: ");
+ return;
+ }
+ ics->xics = XICS_FABRIC(obj);
+
+ if (!ics->nr_irqs) {
+ error_setg(errp, "Number of interrupts needs to be greater 0");
+ return;
+ }
+ ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
+}
+
+static void ics_base_instance_init(Object *obj)
+{
+ ICSState *ics = ICS_BASE(obj);
+
+ ics->offset = XICS_IRQ_BASE;
+}
+
+static int ics_base_dispatch_pre_save(void *opaque)
{
ICSState *ics = opaque;
ICSStateClass *info = ICS_BASE_GET_CLASS(ics);
@@ -578,7 +648,7 @@ static int ics_simple_dispatch_pre_save(void *opaque)
return 0;
}
-static int ics_simple_dispatch_post_load(void *opaque, int version_id)
+static int ics_base_dispatch_post_load(void *opaque, int version_id)
{
ICSState *ics = opaque;
ICSStateClass *info = ICS_BASE_GET_CLASS(ics);
@@ -590,7 +660,7 @@ static int ics_simple_dispatch_post_load(void *opaque, int version_id)
return 0;
}
-static const VMStateDescription vmstate_ics_simple_irq = {
+static const VMStateDescription vmstate_ics_base_irq = {
.name = "ics/irq",
.version_id = 2,
.minimum_version_id = 1,
@@ -604,95 +674,36 @@ static const VMStateDescription vmstate_ics_simple_irq = {
},
};
-static const VMStateDescription vmstate_ics_simple = {
+static const VMStateDescription vmstate_ics_base = {
.name = "ics",
.version_id = 1,
.minimum_version_id = 1,
- .pre_save = ics_simple_dispatch_pre_save,
- .post_load = ics_simple_dispatch_post_load,
+ .pre_save = ics_base_dispatch_pre_save,
+ .post_load = ics_base_dispatch_post_load,
.fields = (VMStateField[]) {
/* Sanity check */
VMSTATE_UINT32_EQUAL(nr_irqs, ICSState, NULL),
VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs,
- vmstate_ics_simple_irq,
+ vmstate_ics_base_irq,
ICSIRQState),
VMSTATE_END_OF_LIST()
},
};
-static void ics_simple_initfn(Object *obj)
-{
- ICSState *ics = ICS_SIMPLE(obj);
-
- ics->offset = XICS_IRQ_BASE;
-}
-
-static void ics_simple_realize(ICSState *ics, Error **errp)
-{
- if (!ics->nr_irqs) {
- error_setg(errp, "Number of interrupts needs to be greater 0");
- return;
- }
- ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
- ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs);
-
- qemu_register_reset(ics_simple_reset, ics);
-}
-
-static Property ics_simple_properties[] = {
+static Property ics_base_properties[] = {
DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0),
DEFINE_PROP_END_OF_LIST(),
};
-static void ics_simple_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
- ICSStateClass *isc = ICS_BASE_CLASS(klass);
-
- isc->realize = ics_simple_realize;
- dc->props = ics_simple_properties;
- dc->vmsd = &vmstate_ics_simple;
- isc->reject = ics_simple_reject;
- isc->resend = ics_simple_resend;
- isc->eoi = ics_simple_eoi;
-}
-
-static const TypeInfo ics_simple_info = {
- .name = TYPE_ICS_SIMPLE,
- .parent = TYPE_ICS_BASE,
- .instance_size = sizeof(ICSState),
- .class_init = ics_simple_class_init,
- .class_size = sizeof(ICSStateClass),
- .instance_init = ics_simple_initfn,
-};
-
-static void ics_base_realize(DeviceState *dev, Error **errp)
-{
- ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev);
- ICSState *ics = ICS_BASE(dev);
- Object *obj;
- Error *err = NULL;
-
- obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &err);
- if (!obj) {
- error_propagate(errp, err);
- error_prepend(errp, "required link '" ICS_PROP_XICS "' not found: ");
- return;
- }
- ics->xics = XICS_FABRIC(obj);
-
-
- if (icsc->realize) {
- icsc->realize(ics, errp);
- }
-}
-
static void ics_base_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = ics_base_realize;
+ dc->props = ics_base_properties;
+ dc->reset = ics_base_reset;
+ dc->vmsd = &vmstate_ics_base;
}
static const TypeInfo ics_base_info = {
@@ -700,6 +711,7 @@ static const TypeInfo ics_base_info = {
.parent = TYPE_DEVICE,
.abstract = true,
.instance_size = sizeof(ICSState),
+ .instance_init = ics_base_instance_init,
.class_init = ics_base_class_init,
.class_size = sizeof(ICSStateClass),
};
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index 8dba2f8..30c3769 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -114,22 +114,38 @@ static int icp_set_kvm_state(ICPState *icp, int version_id)
return 0;
}
-static void icp_kvm_reset(ICPState *icp)
+static void icp_kvm_reset(DeviceState *dev)
{
- icp_set_kvm_state(icp, 1);
+ ICPStateClass *icpc = ICP_GET_CLASS(dev);
+
+ icpc->parent_reset(dev);
+
+ icp_set_kvm_state(ICP(dev), 1);
}
-static void icp_kvm_realize(ICPState *icp, Error **errp)
+static void icp_kvm_realize(DeviceState *dev, Error **errp)
{
- CPUState *cs = icp->cs;
+ ICPState *icp = ICP(dev);
+ ICPStateClass *icpc = ICP_GET_CLASS(icp);
+ Error *local_err = NULL;
+ CPUState *cs;
KVMEnabledICP *enabled_icp;
- unsigned long vcpu_id = kvm_arch_vcpu_id(cs);
+ unsigned long vcpu_id;
int ret;
if (kernel_xics_fd == -1) {
abort();
}
+ icpc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ cs = icp->cs;
+ vcpu_id = kvm_arch_vcpu_id(cs);
+
/*
* If we are reusing a parked vCPU fd corresponding to the CPU
* which was hot-removed earlier we don't have to renable
@@ -154,12 +170,16 @@ static void icp_kvm_realize(ICPState *icp, Error **errp)
static void icp_kvm_class_init(ObjectClass *klass, void *data)
{
+ DeviceClass *dc = DEVICE_CLASS(klass);
ICPStateClass *icpc = ICP_CLASS(klass);
+ device_class_set_parent_realize(dc, icp_kvm_realize,
+ &icpc->parent_realize);
+ device_class_set_parent_reset(dc, icp_kvm_reset,
+ &icpc->parent_reset);
+
icpc->pre_save = icp_get_kvm_state;
icpc->post_load = icp_set_kvm_state;
- icpc->realize = icp_kvm_realize;
- icpc->reset = icp_kvm_reset;
icpc->synchronize_state = icp_synchronize_state;
}
@@ -304,44 +324,46 @@ static void ics_kvm_set_irq(void *opaque, int srcno, int val)
}
}
-static void ics_kvm_reset(void *dev)
+static void ics_kvm_reset(DeviceState *dev)
{
- ICSState *ics = ICS_SIMPLE(dev);
- int i;
- uint8_t flags[ics->nr_irqs];
+ ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev);
- for (i = 0; i < ics->nr_irqs; i++) {
- flags[i] = ics->irqs[i].flags;
- }
-
- memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs);
+ icsc->parent_reset(dev);
- for (i = 0; i < ics->nr_irqs; i++) {
- ics->irqs[i].priority = 0xff;
- ics->irqs[i].saved_priority = 0xff;
- ics->irqs[i].flags = flags[i];
- }
+ ics_set_kvm_state(ICS_KVM(dev), 1);
+}
- ics_set_kvm_state(ics, 1);
+static void ics_kvm_reset_handler(void *dev)
+{
+ ics_kvm_reset(dev);
}
-static void ics_kvm_realize(ICSState *ics, Error **errp)
+static void ics_kvm_realize(DeviceState *dev, Error **errp)
{
- if (!ics->nr_irqs) {
- error_setg(errp, "Number of interrupts needs to be greater 0");
+ ICSState *ics = ICS_KVM(dev);
+ ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics);
+ Error *local_err = NULL;
+
+ icsc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
return;
}
- ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs);
- qemu_register_reset(ics_kvm_reset, ics);
+ qemu_register_reset(ics_kvm_reset_handler, ics);
}
static void ics_kvm_class_init(ObjectClass *klass, void *data)
{
ICSStateClass *icsc = ICS_BASE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ device_class_set_parent_realize(dc, ics_kvm_realize,
+ &icsc->parent_realize);
+ device_class_set_parent_reset(dc, ics_kvm_reset,
+ &icsc->parent_reset);
- icsc->realize = ics_kvm_realize;
icsc->pre_save = ics_get_kvm_state;
icsc->post_load = ics_set_kvm_state;
icsc->synchronize_state = ics_synchronize_state;
@@ -349,7 +371,7 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data)
static const TypeInfo ics_kvm_info = {
.name = TYPE_ICS_KVM,
- .parent = TYPE_ICS_SIMPLE,
+ .parent = TYPE_ICS_BASE,
.instance_size = sizeof(ICSState),
.class_init = ics_kvm_class_init,
};
diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c
index c87de21..fa48505 100644
--- a/hw/intc/xics_pnv.c
+++ b/hw/intc/xics_pnv.c
@@ -18,6 +18,7 @@
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "qemu/log.h"
#include "hw/ppc/xics.h"
@@ -158,9 +159,18 @@ static const MemoryRegionOps pnv_icp_ops = {
},
};
-static void pnv_icp_realize(ICPState *icp, Error **errp)
+static void pnv_icp_realize(DeviceState *dev, Error **errp)
{
+ ICPState *icp = ICP(dev);
PnvICPState *pnv_icp = PNV_ICP(icp);
+ ICPStateClass *icpc = ICP_GET_CLASS(icp);
+ Error *local_err = NULL;
+
+ icpc->parent_realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops,
icp, "icp-thread", 0x1000);
@@ -171,7 +181,8 @@ static void pnv_icp_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
ICPStateClass *icpc = ICP_CLASS(klass);
- icpc->realize = pnv_icp_realize;
+ device_class_set_parent_realize(dc, pnv_icp_realize,
+ &icpc->parent_realize);
dc->desc = "PowerNV ICP";
}
diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c
index 1b2a69b..87ae246 100644
--- a/hw/misc/macio/mac_dbdma.c
+++ b/hw/misc/macio/mac_dbdma.c
@@ -71,18 +71,19 @@ static DBDMAState *dbdma_from_ch(DBDMA_channel *ch)
}
#if DEBUG_DBDMA
-static void dump_dbdma_cmd(dbdma_cmd *cmd)
+static void dump_dbdma_cmd(DBDMA_channel *ch, dbdma_cmd *cmd)
{
- printf("dbdma_cmd %p\n", cmd);
- printf(" req_count 0x%04x\n", le16_to_cpu(cmd->req_count));
- printf(" command 0x%04x\n", le16_to_cpu(cmd->command));
- printf(" phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr));
- printf(" cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep));
- printf(" res_count 0x%04x\n", le16_to_cpu(cmd->res_count));
- printf(" xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status));
+ DBDMA_DPRINTFCH(ch, "dbdma_cmd %p\n", cmd);
+ DBDMA_DPRINTFCH(ch, " req_count 0x%04x\n", le16_to_cpu(cmd->req_count));
+ DBDMA_DPRINTFCH(ch, " command 0x%04x\n", le16_to_cpu(cmd->command));
+ DBDMA_DPRINTFCH(ch, " phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr));
+ DBDMA_DPRINTFCH(ch, " cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep));
+ DBDMA_DPRINTFCH(ch, " res_count 0x%04x\n", le16_to_cpu(cmd->res_count));
+ DBDMA_DPRINTFCH(ch, " xfer_status 0x%04x\n",
+ le16_to_cpu(cmd->xfer_status));
}
#else
-static void dump_dbdma_cmd(dbdma_cmd *cmd)
+static void dump_dbdma_cmd(DBDMA_channel *ch, dbdma_cmd *cmd)
{
}
#endif
@@ -448,7 +449,7 @@ static void channel_run(DBDMA_channel *ch)
uint32_t phy_addr;
DBDMA_DPRINTFCH(ch, "channel_run\n");
- dump_dbdma_cmd(current);
+ dump_dbdma_cmd(ch, current);
/* clear WAKE flag at command fetch */
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 86d82a6..bcab632 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -14,7 +14,8 @@ obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o
# PowerPC 4xx boards
obj-y += ppc4xx_devs.o ppc405_uc.o
obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o
-obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o sam460ex.o
+obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o
+obj-$(CONFIG_SAM460EX) += sam460ex.o
# PReP
obj-$(CONFIG_PREP) += prep.o
obj-$(CONFIG_PREP) += prep_systemio.o
diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c
index 84355b2..d119801 100644
--- a/hw/ppc/mac_newworld.c
+++ b/hw/ppc/mac_newworld.c
@@ -406,11 +406,11 @@ static void ppc_core99_init(MachineState *machine)
adb_bus = qdev_get_child_bus(dev, "adb.0");
dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD);
- qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu);
+ qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true);
qdev_init_nofail(dev);
dev = qdev_create(adb_bus, TYPE_ADB_MOUSE);
- qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu);
+ qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true);
qdev_init_nofail(dev);
}
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index a9f129f..9750464 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -150,6 +150,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
if (!chip) {
error_propagate(errp, local_err);
error_prepend(errp, "required link 'chip' not found: ");
+ return;
}
pc->threads = g_new(PowerPCCPU *, cc->nr_threads);
diff --git a/hw/ppc/ppc440.h b/hw/ppc/ppc440.h
index ad27db1..7cef936 100644
--- a/hw/ppc/ppc440.h
+++ b/hw/ppc/ppc440.h
@@ -21,6 +21,7 @@ void ppc440_sdram_init(CPUPPCState *env, int nbanks,
hwaddr *ram_bases, hwaddr *ram_sizes,
int do_init);
void ppc4xx_ahb_init(CPUPPCState *env);
+void ppc4xx_dma_init(CPUPPCState *env, int dcr_base);
void ppc460ex_pcie_init(CPUPPCState *env);
#endif /* PPC440_H */
diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c
index 1ab2235..0bbaa68 100644
--- a/hw/ppc/ppc440_uc.c
+++ b/hw/ppc/ppc440_uc.c
@@ -13,6 +13,7 @@
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
+#include "qemu/log.h"
#include "cpu.h"
#include "hw/hw.h"
#include "exec/address-spaces.h"
@@ -803,6 +804,227 @@ void ppc4xx_ahb_init(CPUPPCState *env)
}
/*****************************************************************************/
+/* DMA controller */
+
+#define DMA0_CR_CE (1 << 31)
+#define DMA0_CR_PW (1 << 26 | 1 << 25)
+#define DMA0_CR_DAI (1 << 24)
+#define DMA0_CR_SAI (1 << 23)
+#define DMA0_CR_DEC (1 << 2)
+
+enum {
+ DMA0_CR = 0x00,
+ DMA0_CT,
+ DMA0_SAH,
+ DMA0_SAL,
+ DMA0_DAH,
+ DMA0_DAL,
+ DMA0_SGH,
+ DMA0_SGL,
+
+ DMA0_SR = 0x20,
+ DMA0_SGC = 0x23,
+ DMA0_SLP = 0x25,
+ DMA0_POL = 0x26,
+};
+
+typedef struct {
+ uint32_t cr;
+ uint32_t ct;
+ uint64_t sa;
+ uint64_t da;
+ uint64_t sg;
+} PPC4xxDmaChnl;
+
+typedef struct {
+ int base;
+ PPC4xxDmaChnl ch[4];
+ uint32_t sr;
+} PPC4xxDmaState;
+
+static uint32_t dcr_read_dma(void *opaque, int dcrn)
+{
+ PPC4xxDmaState *dma = opaque;
+ uint32_t val = 0;
+ int addr = dcrn - dma->base;
+ int chnl = addr / 8;
+
+ switch (addr) {
+ case 0x00 ... 0x1f:
+ switch (addr % 8) {
+ case DMA0_CR:
+ val = dma->ch[chnl].cr;
+ break;
+ case DMA0_CT:
+ val = dma->ch[chnl].ct;
+ break;
+ case DMA0_SAH:
+ val = dma->ch[chnl].sa >> 32;
+ break;
+ case DMA0_SAL:
+ val = dma->ch[chnl].sa;
+ break;
+ case DMA0_DAH:
+ val = dma->ch[chnl].da >> 32;
+ break;
+ case DMA0_DAL:
+ val = dma->ch[chnl].da;
+ break;
+ case DMA0_SGH:
+ val = dma->ch[chnl].sg >> 32;
+ break;
+ case DMA0_SGL:
+ val = dma->ch[chnl].sg;
+ break;
+ }
+ break;
+ case DMA0_SR:
+ val = dma->sr;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unimplemented register %x (%d, %x)\n",
+ __func__, dcrn, chnl, addr);
+ }
+
+ return val;
+}
+
+static void dcr_write_dma(void *opaque, int dcrn, uint32_t val)
+{
+ PPC4xxDmaState *dma = opaque;
+ int addr = dcrn - dma->base;
+ int chnl = addr / 8;
+
+ switch (addr) {
+ case 0x00 ... 0x1f:
+ switch (addr % 8) {
+ case DMA0_CR:
+ dma->ch[chnl].cr = val;
+ if (val & DMA0_CR_CE) {
+ int count = dma->ch[chnl].ct & 0xffff;
+
+ if (count) {
+ int width, i, sidx, didx;
+ uint8_t *rptr, *wptr;
+ hwaddr rlen, wlen;
+
+ sidx = didx = 0;
+ width = 1 << ((val & DMA0_CR_PW) >> 25);
+ rptr = cpu_physical_memory_map(dma->ch[chnl].sa, &rlen, 0);
+ wptr = cpu_physical_memory_map(dma->ch[chnl].da, &wlen, 1);
+ if (rptr && wptr) {
+ if (!(val & DMA0_CR_DEC) &&
+ val & DMA0_CR_SAI && val & DMA0_CR_DAI) {
+ /* optimise common case */
+ memmove(wptr, rptr, count * width);
+ sidx = didx = count * width;
+ } else {
+ /* do it the slow way */
+ for (sidx = didx = i = 0; i < count; i++) {
+ uint64_t v = ldn_le_p(rptr + sidx, width);
+ stn_le_p(wptr + didx, width, v);
+ if (val & DMA0_CR_SAI) {
+ sidx += width;
+ }
+ if (val & DMA0_CR_DAI) {
+ didx += width;
+ }
+ }
+ }
+ }
+ if (wptr) {
+ cpu_physical_memory_unmap(wptr, wlen, 1, didx);
+ }
+ if (wptr) {
+ cpu_physical_memory_unmap(rptr, rlen, 0, sidx);
+ }
+ }
+ }
+ break;
+ case DMA0_CT:
+ dma->ch[chnl].ct = val;
+ break;
+ case DMA0_SAH:
+ dma->ch[chnl].sa &= 0xffffffffULL;
+ dma->ch[chnl].sa |= (uint64_t)val << 32;
+ break;
+ case DMA0_SAL:
+ dma->ch[chnl].sa &= 0xffffffff00000000ULL;
+ dma->ch[chnl].sa |= val;
+ break;
+ case DMA0_DAH:
+ dma->ch[chnl].da &= 0xffffffffULL;
+ dma->ch[chnl].da |= (uint64_t)val << 32;
+ break;
+ case DMA0_DAL:
+ dma->ch[chnl].da &= 0xffffffff00000000ULL;
+ dma->ch[chnl].da |= val;
+ break;
+ case DMA0_SGH:
+ dma->ch[chnl].sg &= 0xffffffffULL;
+ dma->ch[chnl].sg |= (uint64_t)val << 32;
+ break;
+ case DMA0_SGL:
+ dma->ch[chnl].sg &= 0xffffffff00000000ULL;
+ dma->ch[chnl].sg |= val;
+ break;
+ }
+ break;
+ case DMA0_SR:
+ dma->sr &= ~val;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: unimplemented register %x (%d, %x)\n",
+ __func__, dcrn, chnl, addr);
+ }
+}
+
+static void ppc4xx_dma_reset(void *opaque)
+{
+ PPC4xxDmaState *dma = opaque;
+ int dma_base = dma->base;
+
+ memset(dma, 0, sizeof(*dma));
+ dma->base = dma_base;
+}
+
+void ppc4xx_dma_init(CPUPPCState *env, int dcr_base)
+{
+ PPC4xxDmaState *dma;
+ int i;
+
+ dma = g_malloc0(sizeof(*dma));
+ dma->base = dcr_base;
+ qemu_register_reset(&ppc4xx_dma_reset, dma);
+ for (i = 0; i < 4; i++) {
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_CR,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_CT,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SAH,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SAL,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_DAH,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_DAL,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SGH,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SGL,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ }
+ ppc_dcr_register(env, dcr_base + DMA0_SR,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + DMA0_SGC,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + DMA0_SLP,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, dcr_base + DMA0_POL,
+ dma, &dcr_read_dma, &dcr_write_dma);
+}
+
+/*****************************************************************************/
/* PCI Express controller */
/* FIXME: This is not complete and does not work, only implemented partially
* to allow firmware and guests to find an empty bus. Cards should use PCI.
diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c
index c7c799b..7eed2ec 100644
--- a/hw/ppc/sam460ex.c
+++ b/hw/ppc/sam460ex.c
@@ -37,6 +37,8 @@
#include "hw/i2c/smbus.h"
#include "hw/usb/hcd-ehci.h"
+#include <libfdt.h>
+
#define BINARY_DEVICE_TREE_FILE "canyonlands.dtb"
#define UBOOT_FILENAME "u-boot-sam460-20100605.bin"
/* to extract the official U-Boot bin from the updater: */
@@ -67,6 +69,10 @@
*/
#define CPU_FREQ 1150000000
+#define PLB_FREQ 230000000
+#define OPB_FREQ 115000000
+#define EBC_FREQ 115000000
+#define UART_FREQ 11059200
#define SDRAM_NR_BANKS 4
/* FIXME: See u-boot.git 8ac41e, also fix in ppc440_uc.c */
@@ -255,6 +261,7 @@ static int sam460ex_load_device_tree(hwaddr addr,
void *fdt;
uint32_t tb_freq = CPU_FREQ;
uint32_t clock_freq = CPU_FREQ;
+ int offset;
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
if (!filename) {
@@ -308,6 +315,27 @@ static int sam460ex_load_device_tree(hwaddr addr,
qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency",
tb_freq);
+ /* Remove cpm node if it exists (it is not emulated) */
+ offset = fdt_path_offset(fdt, "/cpm");
+ if (offset >= 0) {
+ fdt_nop_node(fdt, offset);
+ }
+
+ /* set serial port clocks */
+ offset = fdt_node_offset_by_compatible(fdt, -1, "ns16550");
+ while (offset >= 0) {
+ fdt_setprop_cell(fdt, offset, "clock-frequency", UART_FREQ);
+ offset = fdt_node_offset_by_compatible(fdt, offset, "ns16550");
+ }
+
+ /* some more clocks */
+ qemu_fdt_setprop_cell(fdt, "/plb", "clock-frequency",
+ PLB_FREQ);
+ qemu_fdt_setprop_cell(fdt, "/plb/opb", "clock-frequency",
+ OPB_FREQ);
+ qemu_fdt_setprop_cell(fdt, "/plb/opb/ebc", "clock-frequency",
+ EBC_FREQ);
+
rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
g_free(fdt);
ret = fdt_size;
@@ -457,6 +485,7 @@ static void sam460ex_init(MachineState *machine)
object_property_set_bool(OBJECT(dev), true, "realized", NULL);
smbus_eeprom_init(i2c[0]->bus, 8, smbus_eeprom_buf, smbus_eeprom_size);
g_free(smbus_eeprom_buf);
+ i2c_create_slave(i2c[0]->bus, "m41t80", 0x68);
dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600800, uic[0][3]);
i2c[1] = PPC4xx_I2C(dev);
@@ -476,6 +505,9 @@ static void sam460ex_init(MachineState *machine)
/* MAL */
ppc4xx_mal_init(env, 4, 16, &uic[2][3]);
+ /* DMA */
+ ppc4xx_dma_init(env, 0x200);
+
/* 256K of L2 cache as memory */
ppc4xx_l2sram_init(env);
/* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 1602472..3f5e1d3 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -137,7 +137,7 @@ static ICSState *spapr_ics_create(sPAPRMachineState *spapr,
goto error;
}
- return ICS_SIMPLE(obj);
+ return ICS_BASE(obj);
error:
error_propagate(errp, local_err);
@@ -3962,6 +3962,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
mc->no_parallel = 1;
mc->default_boot_order = "";
mc->default_ram_size = 512 * MiB;
+ mc->default_display = "std";
mc->kvm_type = spapr_kvm_type;
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_SPAPR_PCI_HOST_BRIDGE);
mc->pci_allow_0_address = true;
@@ -4095,17 +4096,16 @@ static void spapr_machine_2_12_instance_options(MachineState *machine)
static void spapr_machine_2_12_class_options(MachineClass *mc)
{
sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc);
- uint8_t mps;
spapr_machine_3_0_class_options(mc);
SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12);
- if (kvmppc_hpt_needs_host_contiguous_pages()) {
- mps = ctz64(qemu_getrampagesize());
- } else {
- mps = 34; /* allow everything up to 16GiB, i.e. everything */
- }
- smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = mps;
+ /* We depend on kvm_enabled() to choose a default value for the
+ * hpt-max-page-size capability. Of course we can't do it here
+ * because this is too early and the HW accelerator isn't initialzed
+ * yet. Postpone this to machine init (see default_caps_with_cpu()).
+ */
+ smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 0;
}
DEFINE_SPAPR_MACHINE(2_12, "2.12", false);
diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c
index 62663eb..aa605ce 100644
--- a/hw/ppc/spapr_caps.c
+++ b/hw/ppc/spapr_caps.c
@@ -465,6 +465,19 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr,
caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN;
}
+ /* This is for pseries-2.12 and older */
+ if (smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] == 0) {
+ uint8_t mps;
+
+ if (kvmppc_hpt_needs_host_contiguous_pages()) {
+ mps = ctz64(qemu_getrampagesize());
+ } else {
+ mps = 34; /* allow everything up to 16GiB, i.e. everything */
+ }
+
+ caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = mps;
+ }
+
return caps;
}
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 8b27a4b..e16b2b9 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -6,6 +6,7 @@ common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
common-obj-$(CONFIG_DS1338) += ds1338.o
common-obj-$(CONFIG_HPET) += hpet.o
common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
+common-obj-$(CONFIG_M41T80) += m41t80.o
common-obj-$(CONFIG_M48T59) += m48t59.o
ifeq ($(CONFIG_ISA_BUS),y)
common-obj-$(CONFIG_M48T59) += m48t59-isa.o
diff --git a/hw/timer/m41t80.c b/hw/timer/m41t80.c
new file mode 100644
index 0000000..734d7d9
--- /dev/null
+++ b/hw/timer/m41t80.c
@@ -0,0 +1,117 @@
+/*
+ * M41T80 serial rtc emulation
+ *
+ * Copyright (c) 2018 BALATON Zoltan
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
+#include "qemu/bcd.h"
+#include "hw/i2c/i2c.h"
+
+#define TYPE_M41T80 "m41t80"
+#define M41T80(obj) OBJECT_CHECK(M41t80State, (obj), TYPE_M41T80)
+
+typedef struct M41t80State {
+ I2CSlave parent_obj;
+ int8_t addr;
+} M41t80State;
+
+static void m41t80_realize(DeviceState *dev, Error **errp)
+{
+ M41t80State *s = M41T80(dev);
+
+ s->addr = -1;
+}
+
+static int m41t80_send(I2CSlave *i2c, uint8_t data)
+{
+ M41t80State *s = M41T80(i2c);
+
+ if (s->addr < 0) {
+ s->addr = data;
+ } else {
+ s->addr++;
+ }
+ return 0;
+}
+
+static int m41t80_recv(I2CSlave *i2c)
+{
+ M41t80State *s = M41T80(i2c);
+ struct tm now;
+ qemu_timeval tv;
+
+ if (s->addr < 0) {
+ s->addr = 0;
+ }
+ if (s->addr >= 1 && s->addr <= 7) {
+ qemu_get_timedate(&now, -1);
+ }
+ switch (s->addr++) {
+ case 0:
+ qemu_gettimeofday(&tv);
+ return to_bcd(tv.tv_usec / 10000);
+ case 1:
+ return to_bcd(now.tm_sec);
+ case 2:
+ return to_bcd(now.tm_min);
+ case 3:
+ return to_bcd(now.tm_hour);
+ case 4:
+ return to_bcd(now.tm_wday);
+ case 5:
+ return to_bcd(now.tm_mday);
+ case 6:
+ return to_bcd(now.tm_mon + 1);
+ case 7:
+ return to_bcd(now.tm_year % 100);
+ case 8 ... 19:
+ qemu_log_mask(LOG_UNIMP, "%s: unimplemented register: %d\n",
+ __func__, s->addr - 1);
+ return 0;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register: %d\n",
+ __func__, s->addr - 1);
+ return 0;
+ }
+}
+
+static int m41t80_event(I2CSlave *i2c, enum i2c_event event)
+{
+ M41t80State *s = M41T80(i2c);
+
+ if (event == I2C_START_SEND) {
+ s->addr = -1;
+ }
+ return 0;
+}
+
+static void m41t80_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+
+ dc->realize = m41t80_realize;
+ sc->send = m41t80_send;
+ sc->recv = m41t80_recv;
+ sc->event = m41t80_event;
+}
+
+static const TypeInfo m41t80_info = {
+ .name = TYPE_M41T80,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(M41t80State),
+ .class_init = m41t80_class_init,
+};
+
+static void m41t80_register_types(void)
+{
+ type_register_static(&m41t80_info);
+}
+
+type_init(m41t80_register_types)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 01342326..e5acfc5 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1672,7 +1672,8 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
ehci_set_state(ehci, async, EST_HORIZONTALQH);
} else if ((q->qh.token & QTD_TOKEN_ACTIVE) &&
- (NLPTR_TBIT(q->qh.current_qtd) == 0)) {
+ (NLPTR_TBIT(q->qh.current_qtd) == 0) &&
+ (q->qh.current_qtd != 0)) {
q->qtdaddr = q->qh.current_qtd;
ehci_set_state(ehci, async, EST_FETCHQTD);
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 721beb5..8f1a01a 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1954,7 +1954,12 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
for (i = 0; i < length; i++) {
TRBType type;
type = xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL);
- assert(type);
+ if (!type) {
+ xhci_die(xhci);
+ xhci_ep_free_xfer(xfer);
+ epctx->kick_active--;
+ return;
+ }
}
xfer->streamid = streamid;
diff --git a/include/block/block.h b/include/block/block.h
index 2ffc1c6..e5c7759 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -659,13 +659,14 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host);
* @dst: Destination child to copy data to
* @dst_offset: offset in @dst image to write data
* @bytes: number of bytes to copy
- * @flags: request flags. Must be one of:
- * 0 - actually read data from src;
+ * @flags: request flags. Supported flags:
* BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero
* write on @dst as if bdrv_co_pwrite_zeroes is
* called. Used to simplify caller code, or
* during BlockDriver.bdrv_co_copy_range_from()
* recursion.
+ * BDRV_REQ_NO_SERIALISING - do not serialize with other overlapping
+ * requests currently in flight.
*
* Returns: 0 if succeeded; negative error code if failed.
**/
diff --git a/include/hw/compat.h b/include/hw/compat.h
index 44d5964..c08f404 100644
--- a/include/hw/compat.h
+++ b/include/hw/compat.h
@@ -10,6 +10,22 @@
.driver = "hda-audio",\
.property = "use-timer",\
.value = "false",\
+ },{\
+ .driver = "cirrus-vga",\
+ .property = "global-vmstate",\
+ .value = "true",\
+ },{\
+ .driver = "VGA",\
+ .property = "global-vmstate",\
+ .value = "true",\
+ },{\
+ .driver = "vmware-svga",\
+ .property = "global-vmstate",\
+ .value = "true",\
+ },{\
+ .driver = "qxl-vga",\
+ .property = "global-vmstate",\
+ .value = "true",\
},
#define HW_COMPAT_2_11 \
diff --git a/include/hw/i2c/ppc4xx_i2c.h b/include/hw/i2c/ppc4xx_i2c.h
index ea6c8e1..0891a9c 100644
--- a/include/hw/i2c/ppc4xx_i2c.h
+++ b/include/hw/i2c/ppc4xx_i2c.h
@@ -46,7 +46,8 @@ typedef struct PPC4xxI2CState {
qemu_irq irq;
MemoryRegion iomem;
bitbang_i2c_interface *bitbang;
- uint8_t mdata;
+ int mdidx;
+ uint8_t mdata[4];
uint8_t lmadr;
uint8_t hmadr;
uint8_t cntl;
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 6cebff4..6ac8a93 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -65,10 +65,11 @@ typedef struct XICSFabric XICSFabric;
struct ICPStateClass {
DeviceClass parent_class;
- void (*realize)(ICPState *icp, Error **errp);
+ DeviceRealize parent_realize;
+ DeviceReset parent_reset;
+
void (*pre_save)(ICPState *icp);
int (*post_load)(ICPState *icp, int version_id);
- void (*reset)(ICPState *icp);
void (*synchronize_state)(ICPState *icp);
};
@@ -114,7 +115,9 @@ struct PnvICPState {
struct ICSStateClass {
DeviceClass parent_class;
- void (*realize)(ICSState *s, Error **errp);
+ DeviceRealize parent_realize;
+ DeviceReset parent_reset;
+
void (*pre_save)(ICSState *s);
int (*post_load)(ICSState *s, int version_id);
void (*reject)(ICSState *s, uint32_t irq);
diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c
index 8be0b74..232ad82 100644
--- a/linux-user/openrisc/signal.c
+++ b/linux-user/openrisc/signal.c
@@ -21,124 +21,69 @@
#include "signal-common.h"
#include "linux-user/trace.h"
-struct target_sigcontext {
+typedef struct target_sigcontext {
struct target_pt_regs regs;
abi_ulong oldmask;
- abi_ulong usp;
-};
+} target_sigcontext;
-struct target_ucontext {
+typedef struct target_ucontext {
abi_ulong tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
- struct target_sigcontext tuc_mcontext;
+ target_sigcontext tuc_mcontext;
target_sigset_t tuc_sigmask; /* mask last for extensibility */
-};
+} target_ucontext;
-struct target_rt_sigframe {
- abi_ulong pinfo;
- uint64_t puc;
+typedef struct target_rt_sigframe {
struct target_siginfo info;
- struct target_sigcontext sc;
- struct target_ucontext uc;
- unsigned char retcode[16]; /* trampoline code */
-};
-
-/* This is the asm-generic/ucontext.h version */
-#if 0
-static int restore_sigcontext(CPUOpenRISCState *regs,
- struct target_sigcontext *sc)
-{
- unsigned int err = 0;
- unsigned long old_usp;
-
- /* Alwys make any pending restarted system call return -EINTR */
- current_thread_info()->restart_block.fn = do_no_restart_syscall;
+ target_ucontext uc;
+ uint32_t retcode[4]; /* trampoline code */
+} target_rt_sigframe;
- /* restore the regs from &sc->regs (same as sc, since regs is first)
- * (sc is already checked for VERIFY_READ since the sigframe was
- * checked in sys_sigreturn previously)
- */
+static void restore_sigcontext(CPUOpenRISCState *env, target_sigcontext *sc)
+{
+ int i;
+ abi_ulong v;
- if (copy_from_user(regs, &sc, sizeof(struct target_pt_regs))) {
- goto badframe;
+ for (i = 0; i < 32; ++i) {
+ __get_user(v, &sc->regs.gpr[i]);
+ cpu_set_gpr(env, i, v);
}
+ __get_user(env->pc, &sc->regs.pc);
- /* make sure the U-flag is set so user-mode cannot fool us */
-
- regs->sr &= ~SR_SM;
-
- /* restore the old USP as it was before we stacked the sc etc.
- * (we cannot just pop the sigcontext since we aligned the sp and
- * stuff after pushing it)
- */
-
- __get_user(old_usp, &sc->usp);
- phx_signal("old_usp 0x%lx", old_usp);
-
- __PHX__ REALLY /* ??? */
- wrusp(old_usp);
- regs->gpr[1] = old_usp;
-
- /* TODO: the other ports use regs->orig_XX to disable syscall checks
- * after this completes, but we don't use that mechanism. maybe we can
- * use it now ?
- */
-
- return err;
-
-badframe:
- return 1;
+ /* Make sure the supervisor flag is clear. */
+ __get_user(v, &sc->regs.sr);
+ cpu_set_sr(env, v & ~SR_SM);
}
-#endif
/* Set up a signal frame. */
-static void setup_sigcontext(struct target_sigcontext *sc,
- CPUOpenRISCState *regs,
- unsigned long mask)
+static void setup_sigcontext(target_sigcontext *sc, CPUOpenRISCState *env)
{
- unsigned long usp = cpu_get_gpr(regs, 1);
-
- /* copy the regs. they are first in sc so we can use sc directly */
+ int i;
- /*copy_to_user(&sc, regs, sizeof(struct target_pt_regs));*/
-
- /* Set the frametype to CRIS_FRAME_NORMAL for the execution of
- the signal handler. The frametype will be restored to its previous
- value in restore_sigcontext. */
- /*regs->frametype = CRIS_FRAME_NORMAL;*/
-
- /* then some other stuff */
- __put_user(mask, &sc->oldmask);
- __put_user(usp, &sc->usp);
-}
+ for (i = 0; i < 32; ++i) {
+ __put_user(cpu_get_gpr(env, i), &sc->regs.gpr[i]);
+ }
-static inline unsigned long align_sigframe(unsigned long sp)
-{
- return sp & ~3UL;
+ __put_user(env->pc, &sc->regs.pc);
+ __put_user(cpu_get_sr(env), &sc->regs.sr);
}
static inline abi_ulong get_sigframe(struct target_sigaction *ka,
- CPUOpenRISCState *regs,
+ CPUOpenRISCState *env,
size_t frame_size)
{
- unsigned long sp = get_sp_from_cpustate(regs);
- int onsigstack = on_sig_stack(sp);
-
- /* redzone */
- sp = target_sigsp(sp, ka);
-
- sp = align_sigframe(sp - frame_size);
+ target_ulong sp = get_sp_from_cpustate(env);
- /*
- * If we are on the alternate signal stack and would overflow it, don't.
- * Return an always-bogus address instead so we will die with SIGSEGV.
+ /* Honor redzone now. If we swap to signal stack, no need to waste
+ * the 128 bytes by subtracting afterward.
*/
+ sp -= 128;
- if (onsigstack && !likely(on_sig_stack(sp))) {
- return -1L;
- }
+ sp = target_sigsp(sp, ka);
+ sp -= frame_size;
+ sp = QEMU_ALIGN_DOWN(sp, 4);
return sp;
}
@@ -147,11 +92,9 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUOpenRISCState *env)
{
- int err = 0;
abi_ulong frame_addr;
- unsigned long return_ip;
- struct target_rt_sigframe *frame;
- abi_ulong info_addr, uc_addr;
+ target_rt_sigframe *frame;
+ int i;
frame_addr = get_sigframe(ka, env, sizeof(*frame));
trace_user_setup_rt_frame(env, frame_addr);
@@ -159,47 +102,37 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
goto give_sigsegv;
}
- info_addr = frame_addr + offsetof(struct target_rt_sigframe, info);
- __put_user(info_addr, &frame->pinfo);
- uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc);
- __put_user(uc_addr, &frame->puc);
-
if (ka->sa_flags & SA_SIGINFO) {
tswap_siginfo(&frame->info, info);
}
- /*err |= __clear_user(&frame->uc, offsetof(ucontext_t, uc_mcontext));*/
__put_user(0, &frame->uc.tuc_flags);
__put_user(0, &frame->uc.tuc_link);
- target_save_altstack(&frame->uc.tuc_stack, env);
- setup_sigcontext(&frame->sc, env, set->sig[0]);
- /*err |= copy_to_user(frame->uc.tuc_sigmask, set, sizeof(*set));*/
-
- /* trampoline - the desired return ip is the retcode itself */
- return_ip = (unsigned long)&frame->retcode;
- /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */
- __put_user(0xa960, (short *)(frame->retcode + 0));
- __put_user(TARGET_NR_rt_sigreturn, (short *)(frame->retcode + 2));
- __put_user(0x20000001, (unsigned long *)(frame->retcode + 4));
- __put_user(0x15000000, (unsigned long *)(frame->retcode + 8));
-
- if (err) {
- goto give_sigsegv;
+ target_save_altstack(&frame->uc.tuc_stack, env);
+ setup_sigcontext(&frame->uc.tuc_mcontext, env);
+ for (i = 0; i < TARGET_NSIG_WORDS; ++i) {
+ __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
- /* TODO what is the current->exec_domain stuff and invmap ? */
+ /* This is l.ori r11,r0,__NR_sigreturn; l.sys 1; l.nop; l.nop */
+ __put_user(0xa9600000 | TARGET_NR_rt_sigreturn, frame->retcode + 0);
+ __put_user(0x20000001, frame->retcode + 1);
+ __put_user(0x15000000, frame->retcode + 2);
+ __put_user(0x15000000, frame->retcode + 3);
/* Set up registers for signal handler */
- env->pc = (unsigned long)ka->_sa_handler; /* what we enter NOW */
- cpu_set_gpr(env, 9, (unsigned long)return_ip); /* what we enter LATER */
- cpu_set_gpr(env, 3, (unsigned long)sig); /* arg 1: signo */
- cpu_set_gpr(env, 4, (unsigned long)&frame->info); /* arg 2: (siginfo_t*) */
- cpu_set_gpr(env, 5, (unsigned long)&frame->uc); /* arg 3: ucontext */
-
- /* actually move the usp to reflect the stacked frame */
- cpu_set_gpr(env, 1, (unsigned long)frame);
-
+ cpu_set_gpr(env, 9, frame_addr + offsetof(target_rt_sigframe, retcode));
+ cpu_set_gpr(env, 3, sig);
+ cpu_set_gpr(env, 4, frame_addr + offsetof(target_rt_sigframe, info));
+ cpu_set_gpr(env, 5, frame_addr + offsetof(target_rt_sigframe, uc));
+ cpu_set_gpr(env, 1, frame_addr);
+
+ /* For debugging convenience, set ppc to the insn that faulted. */
+ env->ppc = env->pc;
+ /* When setting the PC for the signal handler, exit delay slot. */
+ env->pc = ka->_sa_handler;
+ env->dflag = 0;
return;
give_sigsegv:
@@ -207,16 +140,34 @@ give_sigsegv:
force_sigsegv(sig);
}
-long do_sigreturn(CPUOpenRISCState *env)
-{
- trace_user_do_sigreturn(env, 0);
- fprintf(stderr, "do_sigreturn: not implemented\n");
- return -TARGET_ENOSYS;
-}
-
long do_rt_sigreturn(CPUOpenRISCState *env)
{
+ abi_ulong frame_addr = get_sp_from_cpustate(env);
+ target_rt_sigframe *frame;
+ sigset_t set;
+
trace_user_do_rt_sigreturn(env, 0);
- fprintf(stderr, "do_rt_sigreturn: not implemented\n");
- return -TARGET_ENOSYS;
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+ goto badframe;
+ }
+ if (frame_addr & 3) {
+ goto badframe;
+ }
+
+ target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
+ set_sigmask(&set);
+
+ restore_sigcontext(env, &frame->uc.tuc_mcontext);
+ if (do_sigaltstack(frame_addr + offsetof(target_rt_sigframe, uc.tuc_stack),
+ 0, frame_addr) == -EFAULT) {
+ goto badframe;
+ }
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return cpu_get_gpr(env, 11);
+
+ badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
}
diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h
index c352a8b..8283eaf 100644
--- a/linux-user/openrisc/target_signal.h
+++ b/linux-user/openrisc/target_signal.h
@@ -5,8 +5,8 @@
typedef struct target_sigaltstack {
abi_long ss_sp;
+ abi_int ss_flags;
abi_ulong ss_size;
- abi_long ss_flags;
} target_stack_t;
/* sigaltstack controls */
diff --git a/linux-user/openrisc/target_syscall.h b/linux-user/openrisc/target_syscall.h
index 03104f8..d586d2a 100644
--- a/linux-user/openrisc/target_syscall.h
+++ b/linux-user/openrisc/target_syscall.h
@@ -1,27 +1,15 @@
#ifndef OPENRISC_TARGET_SYSCALL_H
#define OPENRISC_TARGET_SYSCALL_H
+/* Note that in linux/arch/openrisc/include/uapi/asm/ptrace.h,
+ * this is called user_regs_struct. Given that this is what
+ * is used within struct sigcontext we need this definition.
+ * However, elfload.c wants this name.
+ */
struct target_pt_regs {
- union {
- struct {
- /* Named registers */
- uint32_t sr; /* Stored in place of r0 */
- target_ulong sp; /* r1 */
- };
- struct {
- /* Old style */
- target_ulong offset[2];
- target_ulong gprs[30];
- };
- struct {
- /* New style */
- target_ulong gpr[32];
- };
- };
- target_ulong pc;
- target_ulong orig_gpr11; /* For restarting system calls */
- uint32_t syscallno; /* Syscall number (used by strace) */
- target_ulong dummy; /* Cheap alignment fix */
+ abi_ulong gpr[32];
+ abi_ulong pc;
+ abi_ulong sr;
};
#define UNAME_MACHINE "openrisc"
diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c
index 2fb516c..133a87f 100644
--- a/linux-user/ppc/cpu_loop.c
+++ b/linux-user/ppc/cpu_loop.c
@@ -65,99 +65,23 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
return -1;
}
-static int do_store_exclusive(CPUPPCState *env)
-{
- target_ulong addr;
- target_ulong page_addr;
- target_ulong val, val2 __attribute__((unused)) = 0;
- int flags;
- int segv = 0;
-
- addr = env->reserve_ea;
- page_addr = addr & TARGET_PAGE_MASK;
- start_exclusive();
- mmap_lock();
- flags = page_get_flags(page_addr);
- if ((flags & PAGE_READ) == 0) {
- segv = 1;
- } else {
- int reg = env->reserve_info & 0x1f;
- int size = env->reserve_info >> 5;
- int stored = 0;
-
- if (addr == env->reserve_addr) {
- switch (size) {
- case 1: segv = get_user_u8(val, addr); break;
- case 2: segv = get_user_u16(val, addr); break;
- case 4: segv = get_user_u32(val, addr); break;
-#if defined(TARGET_PPC64)
- case 8: segv = get_user_u64(val, addr); break;
- case 16: {
- segv = get_user_u64(val, addr);
- if (!segv) {
- segv = get_user_u64(val2, addr + 8);
- }
- break;
- }
-#endif
- default: abort();
- }
- if (!segv && val == env->reserve_val) {
- val = env->gpr[reg];
- switch (size) {
- case 1: segv = put_user_u8(val, addr); break;
- case 2: segv = put_user_u16(val, addr); break;
- case 4: segv = put_user_u32(val, addr); break;
-#if defined(TARGET_PPC64)
- case 8: segv = put_user_u64(val, addr); break;
- case 16: {
- if (val2 == env->reserve_val2) {
- if (msr_le) {
- val2 = val;
- val = env->gpr[reg+1];
- } else {
- val2 = env->gpr[reg+1];
- }
- segv = put_user_u64(val, addr);
- if (!segv) {
- segv = put_user_u64(val2, addr + 8);
- }
- }
- break;
- }
-#endif
- default: abort();
- }
- if (!segv) {
- stored = 1;
- }
- }
- }
- env->crf[0] = (stored << 1) | xer_so;
- env->reserve_addr = (target_ulong)-1;
- }
- if (!segv) {
- env->nip += 4;
- }
- mmap_unlock();
- end_exclusive();
- return segv;
-}
-
void cpu_loop(CPUPPCState *env)
{
CPUState *cs = CPU(ppc_env_get_cpu(env));
target_siginfo_t info;
- int trapnr;
+ int trapnr, sig;
target_ulong ret;
for(;;) {
+ bool arch_interrupt;
+
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
- switch(trapnr) {
+ arch_interrupt = true;
+ switch (trapnr) {
case POWERPC_EXCP_NONE:
/* Just go on */
break;
@@ -524,26 +448,15 @@ void cpu_loop(CPUPPCState *env)
}
env->gpr[3] = ret;
break;
- case POWERPC_EXCP_STCX:
- if (do_store_exclusive(env)) {
- info.si_signo = TARGET_SIGSEGV;
+ case EXCP_DEBUG:
+ sig = gdb_handlesig(cs, TARGET_SIGTRAP);
+ if (sig) {
+ info.si_signo = sig;
info.si_errno = 0;
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->nip;
+ info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
- break;
- case EXCP_DEBUG:
- {
- int sig;
-
- sig = gdb_handlesig(cs, TARGET_SIGTRAP);
- if (sig) {
- info.si_signo = sig;
- info.si_errno = 0;
- info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
+ } else {
+ arch_interrupt = false;
}
break;
case EXCP_INTERRUPT:
@@ -551,12 +464,22 @@ void cpu_loop(CPUPPCState *env)
break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
+ arch_interrupt = false;
break;
default:
cpu_abort(cs, "Unknown exception 0x%x. Aborting\n", trapnr);
break;
}
process_pending_signals(env);
+
+ /* Most of the traps imply a transition through kernel mode,
+ * which implies an REI instruction has been executed. Which
+ * means that RX and LOCK_ADDR should be cleared. But there
+ * are a few exceptions for traps internal to QEMU.
+ */
+ if (arch_interrupt) {
+ env->reserve_addr = -1;
+ }
}
}
diff --git a/linux-user/signal.c b/linux-user/signal.c
index be2815b..602b631 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -236,7 +236,7 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
return 0;
}
-#if !defined(TARGET_OPENRISC) && !defined(TARGET_NIOS2)
+#if !defined(TARGET_NIOS2)
/* Just set the guest's signal mask to the specified value; the
* caller is assumed to have called block_signals() already.
*/
diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
index d7eefda..a5cb96d 100755
--- a/scripts/qemu-binfmt-conf.sh
+++ b/scripts/qemu-binfmt-conf.sh
@@ -1,10 +1,10 @@
#!/bin/sh
-# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390/HPPA/Xtensa/microblaze
-# program execution by the kernel
+# Enable automatic program execution by the kernel.
qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \
mips mipsel mipsn32 mipsn32el mips64 mips64el \
-sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb microblaze microblazeel"
+sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb \
+microblaze microblazeel or1k"
i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00'
i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
@@ -124,6 +124,10 @@ microblazeel_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\
microblazeel_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
microblazeel_family=microblazeel
+or1k_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x5c'
+or1k_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
+or1k_family=or1k
+
qemu_get_family() {
cpu=${HOST_ARCH:-$(uname -m)}
case "$cpu" in
diff --git a/target/openrisc/Makefile.objs b/target/openrisc/Makefile.objs
index 1b98a91..b5432f4 100644
--- a/target/openrisc/Makefile.objs
+++ b/target/openrisc/Makefile.objs
@@ -1,7 +1,7 @@
obj-$(CONFIG_SOFTMMU) += machine.o
-obj-y += cpu.o exception.o interrupt.o mmu.o translate.o
+obj-y += cpu.o exception.o interrupt.o mmu.o translate.o disas.o
obj-y += exception_helper.o fpu_helper.o \
- interrupt_helper.o mmu_helper.o sys_helper.o
+ interrupt_helper.o sys_helper.o
obj-y += gdbstub.o
DECODETREE = $(SRC_PATH)/scripts/decodetree.py
@@ -12,3 +12,4 @@ target/openrisc/decode.inc.c: \
$(PYTHON) $(DECODETREE) -o $@ $<, "GEN", $(TARGET_DIR)$@)
target/openrisc/translate.o: target/openrisc/decode.inc.c
+target/openrisc/disas.o: target/openrisc/decode.inc.c
diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c
index a692a98..fb7cb5c 100644
--- a/target/openrisc/cpu.c
+++ b/target/openrisc/cpu.c
@@ -27,6 +27,7 @@ static void openrisc_cpu_set_pc(CPUState *cs, vaddr value)
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
cpu->env.pc = value;
+ cpu->env.dflag = 0;
}
static bool openrisc_cpu_has_work(CPUState *cs)
@@ -35,6 +36,11 @@ static bool openrisc_cpu_has_work(CPUState *cs)
CPU_INTERRUPT_TIMER);
}
+static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info)
+{
+ info->print_insn = print_insn_or1k;
+}
+
/* CPUClass::reset() */
static void openrisc_cpu_reset(CPUState *s)
{
@@ -52,8 +58,10 @@ static void openrisc_cpu_reset(CPUState *s)
cpu->env.upr = UPR_UP | UPR_DMP | UPR_IMP | UPR_PICP | UPR_TTP |
UPR_PMP;
- cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) | (DMMUCFGR_NTS & (6 << 2));
- cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) | (IMMUCFGR_NTS & (6 << 2));
+ cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2))
+ | (DMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2));
+ cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2))
+ | (IMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2));
#ifndef CONFIG_USER_ONLY
cpu->env.picmr = 0x00000000;
@@ -87,10 +95,6 @@ static void openrisc_cpu_initfn(Object *obj)
OpenRISCCPU *cpu = OPENRISC_CPU(obj);
cs->env_ptr = &cpu->env;
-
-#ifndef CONFIG_USER_ONLY
- cpu_openrisc_mmu_init(cpu);
-#endif
}
/* CPU models */
@@ -152,6 +156,7 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data)
#endif
cc->gdb_num_core_regs = 32 + 3;
cc->tcg_initialize = openrisc_translate_init;
+ cc->disas_set_info = openrisc_disas_set_info;
}
/* Sort alphabetically by type name, except for "any". */
diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h
index 35cab65..f1b31bc 100644
--- a/target/openrisc/cpu.h
+++ b/target/openrisc/cpu.h
@@ -222,12 +222,8 @@ enum {
/* TLB size */
enum {
- DTLB_WAYS = 1,
- DTLB_SIZE = 64,
- DTLB_MASK = (DTLB_SIZE-1),
- ITLB_WAYS = 1,
- ITLB_SIZE = 64,
- ITLB_MASK = (ITLB_SIZE-1),
+ TLB_SIZE = 128,
+ TLB_MASK = TLB_SIZE - 1,
};
/* TLB prot */
@@ -241,14 +237,6 @@ enum {
UXE = (1 << 7),
};
-/* check if tlb available */
-enum {
- TLBRET_INVALID = -3,
- TLBRET_NOMATCH = -2,
- TLBRET_BADADDR = -1,
- TLBRET_MATCH = 0
-};
-
typedef struct OpenRISCTLBEntry {
uint32_t mr;
uint32_t tr;
@@ -256,8 +244,8 @@ typedef struct OpenRISCTLBEntry {
#ifndef CONFIG_USER_ONLY
typedef struct CPUOpenRISCTLBContext {
- OpenRISCTLBEntry itlb[ITLB_WAYS][ITLB_SIZE];
- OpenRISCTLBEntry dtlb[DTLB_WAYS][DTLB_SIZE];
+ OpenRISCTLBEntry itlb[TLB_SIZE];
+ OpenRISCTLBEntry dtlb[TLB_SIZE];
int (*cpu_openrisc_map_address_code)(struct OpenRISCCPU *cpu,
hwaddr *physical,
@@ -301,6 +289,10 @@ typedef struct CPUOpenRISCState {
uint32_t dflag; /* In delay slot (boolean) */
+#ifndef CONFIG_USER_ONLY
+ CPUOpenRISCTLBContext tlb;
+#endif
+
/* Fields up to this point are cleared by a CPU reset */
struct {} end_reset_fields;
@@ -310,8 +302,6 @@ typedef struct CPUOpenRISCState {
uint32_t cpucfgr; /* CPU configure register */
#ifndef CONFIG_USER_ONLY
- CPUOpenRISCTLBContext * tlb;
-
QEMUTimer *timer;
uint32_t ttmr; /* Timer tick mode register */
int is_counting;
@@ -358,6 +348,7 @@ void openrisc_translate_init(void);
int openrisc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
int rw, int mmu_idx);
int cpu_openrisc_signal_handler(int host_signum, void *pinfo, void *puc);
+int print_insn_or1k(bfd_vma addr, disassemble_info *info);
#define cpu_list cpu_openrisc_list
#define cpu_signal_handler cpu_openrisc_signal_handler
@@ -376,17 +367,6 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu);
void cpu_openrisc_timer_update(OpenRISCCPU *cpu);
void cpu_openrisc_count_start(OpenRISCCPU *cpu);
void cpu_openrisc_count_stop(OpenRISCCPU *cpu);
-
-void cpu_openrisc_mmu_init(OpenRISCCPU *cpu);
-int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu,
- hwaddr *physical,
- int *prot, target_ulong address, int rw);
-int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu,
- hwaddr *physical,
- int *prot, target_ulong address, int rw);
-int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
- hwaddr *physical,
- int *prot, target_ulong address, int rw);
#endif
#define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU
@@ -395,9 +375,12 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
#include "exec/cpu-all.h"
-#define TB_FLAGS_DFLAG 1
-#define TB_FLAGS_R0_0 2
+#define TB_FLAGS_SM SR_SM
+#define TB_FLAGS_DME SR_DME
+#define TB_FLAGS_IME SR_IME
#define TB_FLAGS_OVE SR_OVE
+#define TB_FLAGS_DFLAG 2 /* reuse SR_TEE */
+#define TB_FLAGS_R0_0 4 /* reuse SR_IEE */
static inline uint32_t cpu_get_gpr(const CPUOpenRISCState *env, int i)
{
@@ -415,17 +398,21 @@ static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env,
{
*pc = env->pc;
*cs_base = 0;
- *flags = (env->dflag
- | (cpu_get_gpr(env, 0) == 0 ? TB_FLAGS_R0_0 : 0)
- | (env->sr & SR_OVE));
+ *flags = (env->dflag ? TB_FLAGS_DFLAG : 0)
+ | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0)
+ | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE));
}
static inline int cpu_mmu_index(CPUOpenRISCState *env, bool ifetch)
{
- if (!(env->sr & SR_IME)) {
- return MMU_NOMMU_IDX;
+ int ret = MMU_NOMMU_IDX; /* mmu is disabled */
+
+ if (env->sr & (ifetch ? SR_IME : SR_DME)) {
+ /* The mmu is enabled; test supervisor state. */
+ ret = env->sr & SR_SM ? MMU_SUPERVISOR_IDX : MMU_USER_IDX;
}
- return (env->sr & SR_SM) == 0 ? MMU_USER_IDX : MMU_SUPERVISOR_IDX;
+
+ return ret;
}
static inline uint32_t cpu_get_sr(const CPUOpenRISCState *env)
diff --git a/target/openrisc/disas.c b/target/openrisc/disas.c
new file mode 100644
index 0000000..4bfd2dd
--- /dev/null
+++ b/target/openrisc/disas.c
@@ -0,0 +1,170 @@
+/*
+ * OpenRISC disassembler
+ *
+ * Copyright (c) 2018 Richard Henderson <rth@twiddle.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "disas/bfd.h"
+#include "qemu/bitops.h"
+#include "cpu.h"
+
+typedef disassemble_info DisasContext;
+
+/* Include the auto-generated decoder. */
+#include "decode.inc.c"
+
+#define output(mnemonic, format, ...) \
+ (info->fprintf_func(info->stream, "%-9s " format, \
+ mnemonic, ##__VA_ARGS__))
+
+int print_insn_or1k(bfd_vma addr, disassemble_info *info)
+{
+ bfd_byte buffer[4];
+ uint32_t insn;
+ int status;
+
+ status = info->read_memory_func(addr, buffer, 4, info);
+ if (status != 0) {
+ info->memory_error_func(status, addr, info);
+ return -1;
+ }
+ insn = bfd_getb32(buffer);
+
+ if (!decode(info, insn)) {
+ output(".long", "%#08x", insn);
+ }
+ return 4;
+}
+
+#define INSN(opcode, format, ...) \
+static bool trans_l_##opcode(disassemble_info *info, \
+ arg_l_##opcode *a, uint32_t insn) \
+{ \
+ output("l." #opcode, format, ##__VA_ARGS__); \
+ return true; \
+}
+
+INSN(add, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(addc, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(sub, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(and, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(or, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(xor, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(sll, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(srl, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(sra, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(ror, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(exths, "r%d, r%d", a->d, a->a)
+INSN(extbs, "r%d, r%d", a->d, a->a)
+INSN(exthz, "r%d, r%d", a->d, a->a)
+INSN(extbz, "r%d, r%d", a->d, a->a)
+INSN(cmov, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(ff1, "r%d, r%d", a->d, a->a)
+INSN(fl1, "r%d, r%d", a->d, a->a)
+INSN(mul, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(mulu, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(div, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(divu, "r%d, r%d, r%d", a->d, a->a, a->b)
+INSN(muld, "r%d, r%d", a->a, a->b)
+INSN(muldu, "r%d, r%d", a->a, a->b)
+INSN(j, "%d", a->n)
+INSN(jal, "%d", a->n)
+INSN(bf, "%d", a->n)
+INSN(bnf, "%d", a->n)
+INSN(jr, "r%d", a->b)
+INSN(jalr, "r%d", a->b)
+INSN(lwa, "r%d, %d(r%d)", a->d, a->i, a->a)
+INSN(lwz, "r%d, %d(r%d)", a->d, a->i, a->a)
+INSN(lws, "r%d, %d(r%d)", a->d, a->i, a->a)
+INSN(lbz, "r%d, %d(r%d)", a->d, a->i, a->a)
+INSN(lbs, "r%d, %d(r%d)", a->d, a->i, a->a)
+INSN(lhz, "r%d, %d(r%d)", a->d, a->i, a->a)
+INSN(lhs, "r%d, %d(r%d)", a->d, a->i, a->a)
+INSN(swa, "%d(r%d), r%d", a->i, a->a, a->b)
+INSN(sw, "%d(r%d), r%d", a->i, a->a, a->b)
+INSN(sb, "%d(r%d), r%d", a->i, a->a, a->b)
+INSN(sh, "%d(r%d), r%d", a->i, a->a, a->b)
+INSN(nop, "")
+INSN(addi, "r%d, r%d, %d", a->d, a->a, a->i)
+INSN(addic, "r%d, r%d, %d", a->d, a->a, a->i)
+INSN(muli, "r%d, r%d, %d", a->d, a->a, a->i)
+INSN(maci, "r%d, %d", a->a, a->i)
+INSN(andi, "r%d, r%d, %d", a->d, a->a, a->k)
+INSN(ori, "r%d, r%d, %d", a->d, a->a, a->k)
+INSN(xori, "r%d, r%d, %d", a->d, a->a, a->i)
+INSN(mfspr, "r%d, r%d, %d", a->d, a->a, a->k)
+INSN(mtspr, "r%d, r%d, %d", a->a, a->b, a->k)
+INSN(mac, "r%d, r%d", a->a, a->b)
+INSN(msb, "r%d, r%d", a->a, a->b)
+INSN(macu, "r%d, r%d", a->a, a->b)
+INSN(msbu, "r%d, r%d", a->a, a->b)
+INSN(slli, "r%d, r%d, %d", a->d, a->a, a->l)
+INSN(srli, "r%d, r%d, %d", a->d, a->a, a->l)
+INSN(srai, "r%d, r%d, %d", a->d, a->a, a->l)
+INSN(rori, "r%d, r%d, %d", a->d, a->a, a->l)
+INSN(movhi, "r%d, %d", a->d, a->k)
+INSN(macrc, "r%d", a->d)
+INSN(sfeq, "r%d, r%d", a->a, a->b)
+INSN(sfne, "r%d, r%d", a->a, a->b)
+INSN(sfgtu, "r%d, r%d", a->a, a->b)
+INSN(sfgeu, "r%d, r%d", a->a, a->b)
+INSN(sfltu, "r%d, r%d", a->a, a->b)
+INSN(sfleu, "r%d, r%d", a->a, a->b)
+INSN(sfgts, "r%d, r%d", a->a, a->b)
+INSN(sfges, "r%d, r%d", a->a, a->b)
+INSN(sflts, "r%d, r%d", a->a, a->b)
+INSN(sfles, "r%d, r%d", a->a, a->b)
+INSN(sfeqi, "r%d, %d", a->a, a->i)
+INSN(sfnei, "r%d, %d", a->a, a->i)
+INSN(sfgtui, "r%d, %d", a->a, a->i)
+INSN(sfgeui, "r%d, %d", a->a, a->i)
+INSN(sfltui, "r%d, %d", a->a, a->i)
+INSN(sfleui, "r%d, %d", a->a, a->i)
+INSN(sfgtsi, "r%d, %d", a->a, a->i)
+INSN(sfgesi, "r%d, %d", a->a, a->i)
+INSN(sfltsi, "r%d, %d", a->a, a->i)
+INSN(sflesi, "r%d, %d", a->a, a->i)
+INSN(sys, "%d", a->k)
+INSN(trap, "%d", a->k)
+INSN(msync, "")
+INSN(psync, "")
+INSN(csync, "")
+INSN(rfe, "")
+
+#define FP_INSN(opcode, suffix, format, ...) \
+static bool trans_lf_##opcode##_##suffix(disassemble_info *info, \
+ arg_lf_##opcode##_##suffix *a, uint32_t insn) \
+{ \
+ output("lf." #opcode "." #suffix, format, ##__VA_ARGS__); \
+ return true; \
+}
+
+FP_INSN(add, s, "r%d, r%d, r%d", a->d, a->a, a->b)
+FP_INSN(sub, s, "r%d, r%d, r%d", a->d, a->a, a->b)
+FP_INSN(mul, s, "r%d, r%d, r%d", a->d, a->a, a->b)
+FP_INSN(div, s, "r%d, r%d, r%d", a->d, a->a, a->b)
+FP_INSN(rem, s, "r%d, r%d, r%d", a->d, a->a, a->b)
+FP_INSN(itof, s, "r%d, r%d", a->d, a->a)
+FP_INSN(ftoi, s, "r%d, r%d", a->d, a->a)
+FP_INSN(madd, s, "r%d, r%d, r%d", a->d, a->a, a->b)
+FP_INSN(sfeq, s, "r%d, r%d", a->a, a->b)
+FP_INSN(sfne, s, "r%d, r%d", a->a, a->b)
+FP_INSN(sfgt, s, "r%d, r%d", a->a, a->b)
+FP_INSN(sfge, s, "r%d, r%d", a->a, a->b)
+FP_INSN(sflt, s, "r%d, r%d", a->a, a->b)
+FP_INSN(sfle, s, "r%d, r%d", a->a, a->b)
diff --git a/target/openrisc/helper.h b/target/openrisc/helper.h
index e37dabc..9db9bf3 100644
--- a/target/openrisc/helper.h
+++ b/target/openrisc/helper.h
@@ -56,5 +56,5 @@ FOP_CMP(le)
DEF_HELPER_FLAGS_1(rfe, 0, void, env)
/* sys */
-DEF_HELPER_FLAGS_4(mtspr, 0, void, env, tl, tl, tl)
-DEF_HELPER_FLAGS_4(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl, tl)
+DEF_HELPER_FLAGS_3(mtspr, 0, void, env, tl, tl)
+DEF_HELPER_FLAGS_3(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl)
diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c
index 3959671..bbae956 100644
--- a/target/openrisc/interrupt.c
+++ b/target/openrisc/interrupt.c
@@ -32,29 +32,22 @@ void openrisc_cpu_do_interrupt(CPUState *cs)
#ifndef CONFIG_USER_ONLY
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
CPUOpenRISCState *env = &cpu->env;
+ int exception = cs->exception_index;
env->epcr = env->pc;
- if (env->dflag) {
- env->dflag = 0;
- env->sr |= SR_DSX;
- env->epcr -= 4;
- } else {
- env->sr &= ~SR_DSX;
- }
- if (cs->exception_index == EXCP_SYSCALL) {
+ if (exception == EXCP_SYSCALL) {
env->epcr += 4;
}
/* When we have an illegal instruction the error effective address
shall be set to the illegal instruction address. */
- if (cs->exception_index == EXCP_ILLEGAL) {
+ if (exception == EXCP_ILLEGAL) {
env->eear = env->pc;
}
- /* For machine-state changed between user-mode and supervisor mode,
- we need flush TLB when we enter&exit EXCP. */
- tlb_flush(cs);
-
+ /* During exceptions esr is populared with the pre-exception sr. */
env->esr = cpu_get_sr(env);
+ /* In parallel sr is updated to disable mmu, interrupts, timers and
+ set the delay slot exception flag. */
env->sr &= ~SR_DME;
env->sr &= ~SR_IME;
env->sr |= SR_SM;
@@ -62,12 +55,38 @@ void openrisc_cpu_do_interrupt(CPUState *cs)
env->sr &= ~SR_TEE;
env->pmr &= ~PMR_DME;
env->pmr &= ~PMR_SME;
- env->tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu;
- env->tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu;
env->lock_addr = -1;
- if (cs->exception_index > 0 && cs->exception_index < EXCP_NR) {
- hwaddr vect_pc = cs->exception_index << 8;
+ /* Set/clear dsx to indicate if we are in a delay slot exception. */
+ if (env->dflag) {
+ env->dflag = 0;
+ env->sr |= SR_DSX;
+ env->epcr -= 4;
+ } else {
+ env->sr &= ~SR_DSX;
+ }
+
+ if (exception > 0 && exception < EXCP_NR) {
+ static const char * const int_name[EXCP_NR] = {
+ [EXCP_RESET] = "RESET",
+ [EXCP_BUSERR] = "BUSERR (bus error)",
+ [EXCP_DPF] = "DFP (data protection fault)",
+ [EXCP_IPF] = "IPF (code protection fault)",
+ [EXCP_TICK] = "TICK (timer interrupt)",
+ [EXCP_ALIGN] = "ALIGN",
+ [EXCP_ILLEGAL] = "ILLEGAL",
+ [EXCP_INT] = "INT (device interrupt)",
+ [EXCP_DTLBMISS] = "DTLBMISS (data tlb miss)",
+ [EXCP_ITLBMISS] = "ITLBMISS (code tlb miss)",
+ [EXCP_RANGE] = "RANGE",
+ [EXCP_SYSCALL] = "SYSCALL",
+ [EXCP_FPE] = "FPE",
+ [EXCP_TRAP] = "TRAP",
+ };
+
+ qemu_log_mask(CPU_LOG_INT, "INT: %s\n", int_name[exception]);
+
+ hwaddr vect_pc = exception << 8;
if (env->cpucfgr & CPUCFGR_EVBARP) {
vect_pc |= env->evbar;
}
@@ -76,7 +95,7 @@ void openrisc_cpu_do_interrupt(CPUState *cs)
}
env->pc = vect_pc;
} else {
- cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
+ cpu_abort(cs, "Unhandled exception 0x%x\n", exception);
}
#endif
diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c
index 56620e0..9c5489f 100644
--- a/target/openrisc/interrupt_helper.c
+++ b/target/openrisc/interrupt_helper.c
@@ -25,36 +25,7 @@
void HELPER(rfe)(CPUOpenRISCState *env)
{
- OpenRISCCPU *cpu = openrisc_env_get_cpu(env);
- CPUState *cs = CPU(cpu);
-#ifndef CONFIG_USER_ONLY
- int need_flush_tlb = (cpu->env.sr & (SR_SM | SR_IME | SR_DME)) ^
- (cpu->env.esr & (SR_SM | SR_IME | SR_DME));
-#endif
- cpu->env.pc = cpu->env.epcr;
- cpu_set_sr(&cpu->env, cpu->env.esr);
- cpu->env.lock_addr = -1;
-
-#ifndef CONFIG_USER_ONLY
- if (cpu->env.sr & SR_DME) {
- cpu->env.tlb->cpu_openrisc_map_address_data =
- &cpu_openrisc_get_phys_data;
- } else {
- cpu->env.tlb->cpu_openrisc_map_address_data =
- &cpu_openrisc_get_phys_nommu;
- }
-
- if (cpu->env.sr & SR_IME) {
- cpu->env.tlb->cpu_openrisc_map_address_code =
- &cpu_openrisc_get_phys_code;
- } else {
- cpu->env.tlb->cpu_openrisc_map_address_code =
- &cpu_openrisc_get_phys_nommu;
- }
-
- if (need_flush_tlb) {
- tlb_flush(cs);
- }
-#endif
- cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ env->pc = env->epcr;
+ env->lock_addr = -1;
+ cpu_set_sr(env, env->esr);
}
diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c
index 0a793eb..1eedbf3 100644
--- a/target/openrisc/machine.c
+++ b/target/openrisc/machine.c
@@ -24,31 +24,6 @@
#include "hw/boards.h"
#include "migration/cpu.h"
-static int env_post_load(void *opaque, int version_id)
-{
- CPUOpenRISCState *env = opaque;
-
- /* Restore MMU handlers */
- if (env->sr & SR_DME) {
- env->tlb->cpu_openrisc_map_address_data =
- &cpu_openrisc_get_phys_data;
- } else {
- env->tlb->cpu_openrisc_map_address_data =
- &cpu_openrisc_get_phys_nommu;
- }
-
- if (env->sr & SR_IME) {
- env->tlb->cpu_openrisc_map_address_code =
- &cpu_openrisc_get_phys_code;
- } else {
- env->tlb->cpu_openrisc_map_address_code =
- &cpu_openrisc_get_phys_nommu;
- }
-
-
- return 0;
-}
-
static const VMStateDescription vmstate_tlb_entry = {
.name = "tlb_entry",
.version_id = 1,
@@ -63,24 +38,17 @@ static const VMStateDescription vmstate_tlb_entry = {
static const VMStateDescription vmstate_cpu_tlb = {
.name = "cpu_tlb",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
- VMSTATE_STRUCT_2DARRAY(itlb, CPUOpenRISCTLBContext,
- ITLB_WAYS, ITLB_SIZE, 0,
+ VMSTATE_STRUCT_ARRAY(itlb, CPUOpenRISCTLBContext, TLB_SIZE, 0,
vmstate_tlb_entry, OpenRISCTLBEntry),
- VMSTATE_STRUCT_2DARRAY(dtlb, CPUOpenRISCTLBContext,
- DTLB_WAYS, DTLB_SIZE, 0,
+ VMSTATE_STRUCT_ARRAY(dtlb, CPUOpenRISCTLBContext, TLB_SIZE, 0,
vmstate_tlb_entry, OpenRISCTLBEntry),
VMSTATE_END_OF_LIST()
}
};
-#define VMSTATE_CPU_TLB(_f, _s) \
- VMSTATE_STRUCT_POINTER(_f, _s, vmstate_cpu_tlb, CPUOpenRISCTLBContext)
-
-
static int get_sr(QEMUFile *f, void *opaque, size_t size, VMStateField *field)
{
CPUOpenRISCState *env = opaque;
@@ -106,7 +74,6 @@ static const VMStateDescription vmstate_env = {
.name = "env",
.version_id = 6,
.minimum_version_id = 6,
- .post_load = env_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINTTL_2DARRAY(shadow_gpr, CPUOpenRISCState, 16, 32),
VMSTATE_UINTTL(pc, CPUOpenRISCState),
@@ -143,7 +110,8 @@ static const VMStateDescription vmstate_env = {
VMSTATE_UINT32(fpcsr, CPUOpenRISCState),
VMSTATE_UINT64(mac, CPUOpenRISCState),
- VMSTATE_CPU_TLB(tlb, CPUOpenRISCState),
+ VMSTATE_STRUCT(tlb, CPUOpenRISCState, 1,
+ vmstate_cpu_tlb, CPUOpenRISCTLBContext),
VMSTATE_TIMER_PTR(timer, CPUOpenRISCState),
VMSTATE_UINT32(ttmr, CPUOpenRISCState),
diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c
index 2bd782f..e7d5219 100644
--- a/target/openrisc/mmu.c
+++ b/target/openrisc/mmu.c
@@ -29,227 +29,156 @@
#endif
#ifndef CONFIG_USER_ONLY
-int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu,
- hwaddr *physical,
- int *prot, target_ulong address, int rw)
+static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
+ target_ulong address)
{
- *physical = address;
+ *phys_addr = address;
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
- return TLBRET_MATCH;
}
-int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu,
- hwaddr *physical,
- int *prot, target_ulong address, int rw)
+static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
+ target_ulong addr, int need, bool super)
{
- int vpn = address >> TARGET_PAGE_BITS;
- int idx = vpn & ITLB_MASK;
- int right = 0;
-
- if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
- return TLBRET_NOMATCH;
- }
- if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) {
- return TLBRET_INVALID;
- }
-
- if (cpu->env.sr & SR_SM) { /* supervisor mode */
- if (cpu->env.tlb->itlb[0][idx].tr & SXE) {
- right |= PAGE_EXEC;
- }
- } else {
- if (cpu->env.tlb->itlb[0][idx].tr & UXE) {
- right |= PAGE_EXEC;
+ int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
+ uint32_t imr = cpu->env.tlb.itlb[idx].mr;
+ uint32_t itr = cpu->env.tlb.itlb[idx].tr;
+ uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
+ uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
+ int right, match, valid;
+
+ /* If the ITLB and DTLB indexes map to the same page, we want to
+ load all permissions all at once. If the destination pages do
+ not match, zap the one we don't need. */
+ if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
+ if (need & PAGE_EXEC) {
+ dmr = dtr = 0;
+ } else {
+ imr = itr = 0;
}
}
- if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
- return TLBRET_BADADDR;
- }
-
- *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) |
- (address & (TARGET_PAGE_SIZE-1));
+ /* Check if either of the entries matches the source address. */
+ match = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
+ match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
+
+ /* Check if either of the entries is valid. */
+ valid = imr & 1 ? PAGE_EXEC : 0;
+ valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
+ valid &= match;
+
+ /* Collect the permissions from the entries. */
+ right = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
+ right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
+ right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
+ right &= valid;
+
+ /* Note that above we validated that itr and dtr match on page.
+ So oring them together changes nothing without having to
+ check which one we needed. We also want to store to these
+ variables even on failure, as it avoids compiler warnings. */
+ *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
*prot = right;
- return TLBRET_MATCH;
-}
-int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
- hwaddr *physical,
- int *prot, target_ulong address, int rw)
-{
- int vpn = address >> TARGET_PAGE_BITS;
- int idx = vpn & DTLB_MASK;
- int right = 0;
+ qemu_log_mask(CPU_LOG_MMU,
+ "MMU lookup: need %d match %d valid %d right %d -> %s\n",
+ need, match, valid, right, (need & right) ? "OK" : "FAIL");
- if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
- return TLBRET_NOMATCH;
- }
- if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) {
- return TLBRET_INVALID;
+ /* Check the collective permissions are present. */
+ if (likely(need & right)) {
+ return 0; /* success! */
}
- if (cpu->env.sr & SR_SM) { /* supervisor mode */
- if (cpu->env.tlb->dtlb[0][idx].tr & SRE) {
- right |= PAGE_READ;
- }
- if (cpu->env.tlb->dtlb[0][idx].tr & SWE) {
- right |= PAGE_WRITE;
- }
+ /* Determine what kind of failure we have. */
+ if (need & valid) {
+ return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
} else {
- if (cpu->env.tlb->dtlb[0][idx].tr & URE) {
- right |= PAGE_READ;
- }
- if (cpu->env.tlb->dtlb[0][idx].tr & UWE) {
- right |= PAGE_WRITE;
- }
+ return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
}
-
- if (!(rw & 1) && ((right & PAGE_READ) == 0)) {
- return TLBRET_BADADDR;
- }
- if ((rw & 1) && ((right & PAGE_WRITE) == 0)) {
- return TLBRET_BADADDR;
- }
-
- *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) |
- (address & (TARGET_PAGE_SIZE-1));
- *prot = right;
- return TLBRET_MATCH;
-}
-
-static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu,
- hwaddr *physical,
- int *prot, target_ulong address,
- int rw)
-{
- int ret = TLBRET_MATCH;
-
- if (rw == MMU_INST_FETCH) { /* ITLB */
- *physical = 0;
- ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical,
- prot, address, rw);
- } else { /* DTLB */
- ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical,
- prot, address, rw);
- }
-
- return ret;
}
#endif
-static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu,
- target_ulong address,
- int rw, int tlb_error)
+static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
+ int exception)
{
CPUState *cs = CPU(cpu);
- int exception = 0;
-
- switch (tlb_error) {
- default:
- if (rw == 2) {
- exception = EXCP_IPF;
- } else {
- exception = EXCP_DPF;
- }
- break;
-#ifndef CONFIG_USER_ONLY
- case TLBRET_BADADDR:
- if (rw == 2) {
- exception = EXCP_IPF;
- } else {
- exception = EXCP_DPF;
- }
- break;
- case TLBRET_INVALID:
- case TLBRET_NOMATCH:
- /* No TLB match for a mapped address */
- if (rw == 2) {
- exception = EXCP_ITLBMISS;
- } else {
- exception = EXCP_DTLBMISS;
- }
- break;
-#endif
- }
cs->exception_index = exception;
cpu->env.eear = address;
cpu->env.lock_addr = -1;
}
-#ifndef CONFIG_USER_ONLY
int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
int rw, int mmu_idx)
{
+#ifdef CONFIG_USER_ONLY
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
- int ret = 0;
- hwaddr physical = 0;
- int prot = 0;
-
- ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot,
- address, rw);
-
- if (ret == TLBRET_MATCH) {
- tlb_set_page(cs, address & TARGET_PAGE_MASK,
- physical & TARGET_PAGE_MASK, prot,
- mmu_idx, TARGET_PAGE_SIZE);
- ret = 0;
- } else if (ret < 0) {
- cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
- ret = 1;
- }
-
- return ret;
-}
+ raise_mmu_exception(cpu, address, EXCP_DPF);
+ return 1;
#else
-int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
- int rw, int mmu_idx)
-{
- OpenRISCCPU *cpu = OPENRISC_CPU(cs);
- int ret = 0;
-
- cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
- ret = 1;
-
- return ret;
-}
+ g_assert_not_reached();
#endif
+}
#ifndef CONFIG_USER_ONLY
hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
+ int prot, excp, sr = cpu->env.sr;
hwaddr phys_addr;
- int prot;
- int miss;
- /* Check memory for any kind of address, since during debug the
- gdb can ask for anything, check data tlb for address */
- miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0);
-
- /* Check instruction tlb */
- if (miss) {
- miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr,
- MMU_INST_FETCH);
- }
+ switch (sr & (SR_DME | SR_IME)) {
+ case SR_DME | SR_IME:
+ /* The mmu is definitely enabled. */
+ excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
+ PAGE_EXEC | PAGE_READ | PAGE_WRITE,
+ (sr & SR_SM) != 0);
+ return excp ? -1 : phys_addr;
- /* Last, fall back to a plain address */
- if (miss) {
- miss = cpu_openrisc_get_phys_nommu(cpu, &phys_addr, &prot, addr, 0);
- }
+ default:
+ /* The mmu is partially enabled, and we don't really have
+ a "real" access type. Begin by trying the mmu, but if
+ that fails try again without. */
+ excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
+ PAGE_EXEC | PAGE_READ | PAGE_WRITE,
+ (sr & SR_SM) != 0);
+ if (!excp) {
+ return phys_addr;
+ }
+ /* fallthru */
- if (miss) {
- return -1;
- } else {
+ case 0:
+ /* The mmu is definitely disabled; lookups never fail. */
+ get_phys_nommu(&phys_addr, &prot, addr);
return phys_addr;
}
}
-void cpu_openrisc_mmu_init(OpenRISCCPU *cpu)
+void tlb_fill(CPUState *cs, target_ulong addr, int size,
+ MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
- cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext));
+ OpenRISCCPU *cpu = OPENRISC_CPU(cs);
+ int prot, excp;
+ hwaddr phys_addr;
+
+ if (mmu_idx == MMU_NOMMU_IDX) {
+ /* The mmu is disabled; lookups never fail. */
+ get_phys_nommu(&phys_addr, &prot, addr);
+ excp = 0;
+ } else {
+ bool super = mmu_idx == MMU_SUPERVISOR_IDX;
+ int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
+ : access_type == MMU_DATA_STORE ? PAGE_WRITE
+ : PAGE_READ);
+ excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
+ }
+
+ if (unlikely(excp)) {
+ raise_mmu_exception(cpu, addr, excp);
+ cpu_loop_exit_restore(cs, retaddr);
+ }
- cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu;
- cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu;
+ tlb_set_page(cs, addr & TARGET_PAGE_MASK,
+ phys_addr & TARGET_PAGE_MASK, prot,
+ mmu_idx, TARGET_PAGE_SIZE);
}
#endif
diff --git a/target/openrisc/mmu_helper.c b/target/openrisc/mmu_helper.c
deleted file mode 100644
index 97e1d17..0000000
--- a/target/openrisc/mmu_helper.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * OpenRISC MMU helper routines
- *
- * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
- * Zhizhou Zhang <etouzh@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 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 "cpu.h"
-#include "exec/exec-all.h"
-#include "exec/cpu_ldst.h"
-
-#ifndef CONFIG_USER_ONLY
-
-void tlb_fill(CPUState *cs, target_ulong addr, int size,
- MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
-{
- int ret;
-
- ret = openrisc_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx);
-
- if (ret) {
- /* Raise Exception. */
- cpu_loop_exit_restore(cs, retaddr);
- }
-}
-#endif
diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c
index b284064..b66a45c 100644
--- a/target/openrisc/sys_helper.c
+++ b/target/openrisc/sys_helper.c
@@ -27,13 +27,12 @@
#define TO_SPR(group, number) (((group) << 11) + (number))
-void HELPER(mtspr)(CPUOpenRISCState *env,
- target_ulong ra, target_ulong rb, target_ulong offset)
+void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb)
{
#ifndef CONFIG_USER_ONLY
OpenRISCCPU *cpu = openrisc_env_get_cpu(env);
CPUState *cs = CPU(cpu);
- int spr = (ra | offset);
+ target_ulong mr;
int idx;
switch (spr) {
@@ -57,26 +56,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env,
break;
case TO_SPR(0, 17): /* SR */
- if ((env->sr & (SR_IME | SR_DME | SR_SM)) ^
- (rb & (SR_IME | SR_DME | SR_SM))) {
- tlb_flush(cs);
- }
cpu_set_sr(env, rb);
- if (env->sr & SR_DME) {
- env->tlb->cpu_openrisc_map_address_data =
- &cpu_openrisc_get_phys_data;
- } else {
- env->tlb->cpu_openrisc_map_address_data =
- &cpu_openrisc_get_phys_nommu;
- }
-
- if (env->sr & SR_IME) {
- env->tlb->cpu_openrisc_map_address_code =
- &cpu_openrisc_get_phys_code;
- } else {
- env->tlb->cpu_openrisc_map_address_code =
- &cpu_openrisc_get_phys_nommu;
- }
break;
case TO_SPR(0, 18): /* PPC */
@@ -98,18 +78,22 @@ void HELPER(mtspr)(CPUOpenRISCState *env,
case TO_SPR(0, 1024) ... TO_SPR(0, 1024 + (16 * 32)): /* Shadow GPRs */
idx = (spr - 1024);
env->shadow_gpr[idx / 32][idx % 32] = rb;
+ break;
- case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */
+ case TO_SPR(1, 512) ... TO_SPR(1, 512 + TLB_SIZE - 1): /* DTLBW0MR 0-127 */
idx = spr - TO_SPR(1, 512);
- if (!(rb & 1)) {
- tlb_flush_page(cs, env->tlb->dtlb[0][idx].mr & TARGET_PAGE_MASK);
+ mr = env->tlb.dtlb[idx].mr;
+ if (mr & 1) {
+ tlb_flush_page(cs, mr & TARGET_PAGE_MASK);
+ }
+ if (rb & 1) {
+ tlb_flush_page(cs, rb & TARGET_PAGE_MASK);
}
- env->tlb->dtlb[0][idx].mr = rb;
+ env->tlb.dtlb[idx].mr = rb;
break;
-
- case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */
+ case TO_SPR(1, 640) ... TO_SPR(1, 640 + TLB_SIZE - 1): /* DTLBW0TR 0-127 */
idx = spr - TO_SPR(1, 640);
- env->tlb->dtlb[0][idx].tr = rb;
+ env->tlb.dtlb[idx].tr = rb;
break;
case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */
case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */
@@ -118,17 +102,21 @@ void HELPER(mtspr)(CPUOpenRISCState *env,
case TO_SPR(1, 1280) ... TO_SPR(1, 1407): /* DTLBW3MR 0-127 */
case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */
break;
- case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */
+
+ case TO_SPR(2, 512) ... TO_SPR(2, 512 + TLB_SIZE - 1): /* ITLBW0MR 0-127 */
idx = spr - TO_SPR(2, 512);
- if (!(rb & 1)) {
- tlb_flush_page(cs, env->tlb->itlb[0][idx].mr & TARGET_PAGE_MASK);
+ mr = env->tlb.itlb[idx].mr;
+ if (mr & 1) {
+ tlb_flush_page(cs, mr & TARGET_PAGE_MASK);
+ }
+ if (rb & 1) {
+ tlb_flush_page(cs, rb & TARGET_PAGE_MASK);
}
- env->tlb->itlb[0][idx].mr = rb;
+ env->tlb.itlb[idx].mr = rb;
break;
-
- case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */
+ case TO_SPR(2, 640) ... TO_SPR(2, 640 + TLB_SIZE - 1): /* ITLBW0TR 0-127 */
idx = spr - TO_SPR(2, 640);
- env->tlb->itlb[0][idx].tr = rb;
+ env->tlb.itlb[idx].tr = rb;
break;
case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */
case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */
@@ -137,6 +125,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env,
case TO_SPR(2, 1280) ... TO_SPR(2, 1407): /* ITLBW3MR 0-127 */
case TO_SPR(2, 1408) ... TO_SPR(2, 1535): /* ITLBW3TR 0-127 */
break;
+
case TO_SPR(5, 1): /* MACLO */
env->mac = deposit64(env->mac, 0, 32, rb);
break;
@@ -153,7 +142,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env,
}
break;
case TO_SPR(9, 0): /* PICMR */
- env->picmr |= rb;
+ env->picmr = rb;
break;
case TO_SPR(9, 2): /* PICSR */
env->picsr &= ~rb;
@@ -201,13 +190,12 @@ void HELPER(mtspr)(CPUOpenRISCState *env,
#endif
}
-target_ulong HELPER(mfspr)(CPUOpenRISCState *env,
- target_ulong rd, target_ulong ra, uint32_t offset)
+target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd,
+ target_ulong spr)
{
#ifndef CONFIG_USER_ONLY
OpenRISCCPU *cpu = openrisc_env_get_cpu(env);
CPUState *cs = CPU(cpu);
- int spr = (ra | offset);
int idx;
switch (spr) {
@@ -259,13 +247,13 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env,
idx = (spr - 1024);
return env->shadow_gpr[idx / 32][idx % 32];
- case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */
+ case TO_SPR(1, 512) ... TO_SPR(1, 512 + TLB_SIZE - 1): /* DTLBW0MR 0-127 */
idx = spr - TO_SPR(1, 512);
- return env->tlb->dtlb[0][idx].mr;
+ return env->tlb.dtlb[idx].mr;
- case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */
+ case TO_SPR(1, 640) ... TO_SPR(1, 640 + TLB_SIZE - 1): /* DTLBW0TR 0-127 */
idx = spr - TO_SPR(1, 640);
- return env->tlb->dtlb[0][idx].tr;
+ return env->tlb.dtlb[idx].tr;
case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */
case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */
@@ -275,13 +263,13 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env,
case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */
break;
- case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */
+ case TO_SPR(2, 512) ... TO_SPR(2, 512 + TLB_SIZE - 1): /* ITLBW0MR 0-127 */
idx = spr - TO_SPR(2, 512);
- return env->tlb->itlb[0][idx].mr;
+ return env->tlb.itlb[idx].mr;
- case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */
+ case TO_SPR(2, 640) ... TO_SPR(2, 640 + TLB_SIZE - 1): /* ITLBW0TR 0-127 */
idx = spr - TO_SPR(2, 640);
- return env->tlb->itlb[0][idx].tr;
+ return env->tlb.itlb[idx].tr;
case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */
case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */
diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c
index d69f8d0..a271cd3 100644
--- a/target/openrisc/translate.c
+++ b/target/openrisc/translate.c
@@ -36,22 +36,29 @@
#include "trace-tcg.h"
#include "exec/log.h"
-#define LOG_DIS(str, ...) \
- qemu_log_mask(CPU_LOG_TB_IN_ASM, "%08x: " str, dc->base.pc_next, \
- ## __VA_ARGS__)
-
/* is_jmp field values */
-#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */
-#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */
-#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */
+#define DISAS_EXIT DISAS_TARGET_0 /* force exit to main loop */
+#define DISAS_JUMP DISAS_TARGET_1 /* exit via jmp_pc/jmp_pc_imm */
typedef struct DisasContext {
DisasContextBase base;
uint32_t mem_idx;
uint32_t tb_flags;
uint32_t delayed_branch;
+
+ /* If not -1, jmp_pc contains this value and so is a direct jump. */
+ target_ulong jmp_pc_imm;
} DisasContext;
+static inline bool is_user(DisasContext *dc)
+{
+#ifdef CONFIG_USER_ONLY
+ return true;
+#else
+ return !(dc->tb_flags & TB_FLAGS_SM);
+#endif
+}
+
/* Include the auto-generated decoder. */
#include "decode.inc.c"
@@ -165,34 +172,6 @@ static void check_ov64s(DisasContext *dc)
} \
} while (0)
-static inline bool use_goto_tb(DisasContext *dc, target_ulong dest)
-{
- if (unlikely(dc->base.singlestep_enabled)) {
- return false;
- }
-
-#ifndef CONFIG_USER_ONLY
- return (dc->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
-#else
- return true;
-#endif
-}
-
-static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
-{
- if (use_goto_tb(dc, dest)) {
- tcg_gen_movi_tl(cpu_pc, dest);
- tcg_gen_goto_tb(n);
- tcg_gen_exit_tb(dc->base.tb, n);
- } else {
- tcg_gen_movi_tl(cpu_pc, dest);
- if (dc->base.singlestep_enabled) {
- gen_exception(dc, EXCP_DEBUG);
- }
- tcg_gen_exit_tb(NULL, 0);
- }
-}
-
static void gen_ove_cy(DisasContext *dc)
{
if (dc->tb_flags & SR_OVE) {
@@ -457,7 +436,6 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb)
static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.add r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
gen_add(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -465,7 +443,6 @@ static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.addc r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
gen_addc(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -473,7 +450,6 @@ static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.sub r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
gen_sub(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -481,7 +457,6 @@ static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.and r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
tcg_gen_and_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -489,7 +464,6 @@ static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.or r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
tcg_gen_or_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -497,7 +471,6 @@ static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.xor r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
tcg_gen_xor_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -505,7 +478,6 @@ static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.sll r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
tcg_gen_shl_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -513,7 +485,6 @@ static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.srl r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
tcg_gen_shr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -521,7 +492,6 @@ static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.sra r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
tcg_gen_sar_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -529,7 +499,6 @@ static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.ror r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
tcg_gen_rotr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -537,7 +506,6 @@ static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn)
{
- LOG_DIS("l.exths r%d, r%d\n", a->d, a->a);
check_r0_write(a->d);
tcg_gen_ext16s_tl(cpu_R[a->d], cpu_R[a->a]);
return true;
@@ -545,7 +513,6 @@ static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn)
static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn)
{
- LOG_DIS("l.extbs r%d, r%d\n", a->d, a->a);
check_r0_write(a->d);
tcg_gen_ext8s_tl(cpu_R[a->d], cpu_R[a->a]);
return true;
@@ -553,7 +520,6 @@ static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn)
static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn)
{
- LOG_DIS("l.exthz r%d, r%d\n", a->d, a->a);
check_r0_write(a->d);
tcg_gen_ext16u_tl(cpu_R[a->d], cpu_R[a->a]);
return true;
@@ -561,7 +527,6 @@ static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn)
static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn)
{
- LOG_DIS("l.extbz r%d, r%d\n", a->d, a->a);
check_r0_write(a->d);
tcg_gen_ext8u_tl(cpu_R[a->d], cpu_R[a->a]);
return true;
@@ -570,7 +535,6 @@ static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn)
static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn)
{
TCGv zero;
- LOG_DIS("l.cmov r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
zero = tcg_const_tl(0);
@@ -582,8 +546,6 @@ static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn)
{
- LOG_DIS("l.ff1 r%d, r%d\n", a->d, a->a);
-
check_r0_write(a->d);
tcg_gen_ctzi_tl(cpu_R[a->d], cpu_R[a->a], -1);
tcg_gen_addi_tl(cpu_R[a->d], cpu_R[a->d], 1);
@@ -592,8 +554,6 @@ static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn)
static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn)
{
- LOG_DIS("l.fl1 r%d, r%d\n", a->d, a->a);
-
check_r0_write(a->d);
tcg_gen_clzi_tl(cpu_R[a->d], cpu_R[a->a], TARGET_LONG_BITS);
tcg_gen_subfi_tl(cpu_R[a->d], TARGET_LONG_BITS, cpu_R[a->d]);
@@ -602,8 +562,6 @@ static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn)
static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.mul r%d, r%d, r%d\n", a->d, a->a, a->b);
-
check_r0_write(a->d);
gen_mul(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -611,8 +569,6 @@ static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.mulu r%d, r%d, r%d\n", a->d, a->a, a->b);
-
check_r0_write(a->d);
gen_mulu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -620,8 +576,6 @@ static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.div r%d, r%d, r%d\n", a->d, a->a, a->b);
-
check_r0_write(a->d);
gen_div(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -629,8 +583,6 @@ static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("l.divu r%d, r%d, r%d\n", a->d, a->a, a->b);
-
check_r0_write(a->d);
gen_divu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]);
return true;
@@ -638,14 +590,12 @@ static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_l_muld(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("l.muld r%d, r%d\n", a->a, a->b);
gen_muld(dc, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_muldu(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("l.muldu r%d, r%d\n", a->a, a->b);
gen_muldu(dc, cpu_R[a->a], cpu_R[a->b]);
return true;
}
@@ -654,8 +604,8 @@ static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn)
{
target_ulong tmp_pc = dc->base.pc_next + a->n * 4;
- LOG_DIS("l.j %d\n", a->n);
tcg_gen_movi_tl(jmp_pc, tmp_pc);
+ dc->jmp_pc_imm = tmp_pc;
dc->delayed_branch = 2;
return true;
}
@@ -665,11 +615,11 @@ static bool trans_l_jal(DisasContext *dc, arg_l_jal *a, uint32_t insn)
target_ulong tmp_pc = dc->base.pc_next + a->n * 4;
target_ulong ret_pc = dc->base.pc_next + 8;
- LOG_DIS("l.jal %d\n", a->n);
tcg_gen_movi_tl(cpu_R[9], ret_pc);
/* Optimize jal being used to load the PC for PIC. */
if (tmp_pc != ret_pc) {
tcg_gen_movi_tl(jmp_pc, tmp_pc);
+ dc->jmp_pc_imm = tmp_pc;
dc->delayed_branch = 2;
}
return true;
@@ -692,21 +642,18 @@ static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond)
static bool trans_l_bf(DisasContext *dc, arg_l_bf *a, uint32_t insn)
{
- LOG_DIS("l.bf %d\n", a->n);
do_bf(dc, a, TCG_COND_NE);
return true;
}
static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a, uint32_t insn)
{
- LOG_DIS("l.bnf %d\n", a->n);
do_bf(dc, a, TCG_COND_EQ);
return true;
}
static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn)
{
- LOG_DIS("l.jr r%d\n", a->b);
tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]);
dc->delayed_branch = 2;
return true;
@@ -714,7 +661,6 @@ static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn)
static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a, uint32_t insn)
{
- LOG_DIS("l.jalr r%d\n", a->b);
tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]);
tcg_gen_movi_tl(cpu_R[9], dc->base.pc_next + 8);
dc->delayed_branch = 2;
@@ -725,8 +671,6 @@ static bool trans_l_lwa(DisasContext *dc, arg_load *a, uint32_t insn)
{
TCGv ea;
- LOG_DIS("l.lwa r%d, r%d, %d\n", a->d, a->a, a->i);
-
check_r0_write(a->d);
ea = tcg_temp_new();
tcg_gen_addi_tl(ea, cpu_R[a->a], a->i);
@@ -750,42 +694,36 @@ static void do_load(DisasContext *dc, arg_load *a, TCGMemOp mop)
static bool trans_l_lwz(DisasContext *dc, arg_load *a, uint32_t insn)
{
- LOG_DIS("l.lwz r%d, r%d, %d\n", a->d, a->a, a->i);
do_load(dc, a, MO_TEUL);
return true;
}
static bool trans_l_lws(DisasContext *dc, arg_load *a, uint32_t insn)
{
- LOG_DIS("l.lws r%d, r%d, %d\n", a->d, a->a, a->i);
do_load(dc, a, MO_TESL);
return true;
}
static bool trans_l_lbz(DisasContext *dc, arg_load *a, uint32_t insn)
{
- LOG_DIS("l.lbz r%d, r%d, %d\n", a->d, a->a, a->i);
do_load(dc, a, MO_UB);
return true;
}
static bool trans_l_lbs(DisasContext *dc, arg_load *a, uint32_t insn)
{
- LOG_DIS("l.lbs r%d, r%d, %d\n", a->d, a->a, a->i);
do_load(dc, a, MO_SB);
return true;
}
static bool trans_l_lhz(DisasContext *dc, arg_load *a, uint32_t insn)
{
- LOG_DIS("l.lhz r%d, r%d, %d\n", a->d, a->a, a->i);
do_load(dc, a, MO_TEUW);
return true;
}
static bool trans_l_lhs(DisasContext *dc, arg_load *a, uint32_t insn)
{
- LOG_DIS("l.lhs r%d, r%d, %d\n", a->d, a->a, a->i);
do_load(dc, a, MO_TESW);
return true;
}
@@ -795,8 +733,6 @@ static bool trans_l_swa(DisasContext *dc, arg_store *a, uint32_t insn)
TCGv ea, val;
TCGLabel *lab_fail, *lab_done;
- LOG_DIS("l.swa r%d, r%d, %d\n", a->a, a->b, a->i);
-
ea = tcg_temp_new();
tcg_gen_addi_tl(ea, cpu_R[a->a], a->i);
@@ -837,28 +773,24 @@ static void do_store(DisasContext *dc, arg_store *a, TCGMemOp mop)
static bool trans_l_sw(DisasContext *dc, arg_store *a, uint32_t insn)
{
- LOG_DIS("l.sw r%d, r%d, %d\n", a->a, a->b, a->i);
do_store(dc, a, MO_TEUL);
return true;
}
static bool trans_l_sb(DisasContext *dc, arg_store *a, uint32_t insn)
{
- LOG_DIS("l.sb r%d, r%d, %d\n", a->a, a->b, a->i);
do_store(dc, a, MO_UB);
return true;
}
static bool trans_l_sh(DisasContext *dc, arg_store *a, uint32_t insn)
{
- LOG_DIS("l.sh r%d, r%d, %d\n", a->a, a->b, a->i);
do_store(dc, a, MO_TEUW);
return true;
}
static bool trans_l_nop(DisasContext *dc, arg_l_nop *a, uint32_t insn)
{
- LOG_DIS("l.nop %d\n", a->k);
return true;
}
@@ -866,7 +798,6 @@ static bool trans_l_addi(DisasContext *dc, arg_rri *a, uint32_t insn)
{
TCGv t0;
- LOG_DIS("l.addi r%d, r%d, %d\n", a->d, a->a, a->i);
check_r0_write(a->d);
t0 = tcg_const_tl(a->i);
gen_add(dc, cpu_R[a->d], cpu_R[a->a], t0);
@@ -878,7 +809,6 @@ static bool trans_l_addic(DisasContext *dc, arg_rri *a, uint32_t insn)
{
TCGv t0;
- LOG_DIS("l.addic r%d, r%d, %d\n", a->d, a->a, a->i);
check_r0_write(a->d);
t0 = tcg_const_tl(a->i);
gen_addc(dc, cpu_R[a->d], cpu_R[a->a], t0);
@@ -890,7 +820,6 @@ static bool trans_l_muli(DisasContext *dc, arg_rri *a, uint32_t insn)
{
TCGv t0;
- LOG_DIS("l.muli r%d, r%d, %d\n", a->d, a->a, a->i);
check_r0_write(a->d);
t0 = tcg_const_tl(a->i);
gen_mul(dc, cpu_R[a->d], cpu_R[a->a], t0);
@@ -902,7 +831,6 @@ static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn)
{
TCGv t0;
- LOG_DIS("l.maci r%d, %d\n", a->a, a->i);
t0 = tcg_const_tl(a->i);
gen_mac(dc, cpu_R[a->a], t0);
tcg_temp_free(t0);
@@ -911,7 +839,6 @@ static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn)
static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn)
{
- LOG_DIS("l.andi r%d, r%d, %d\n", a->d, a->a, a->k);
check_r0_write(a->d);
tcg_gen_andi_tl(cpu_R[a->d], cpu_R[a->a], a->k);
return true;
@@ -919,7 +846,6 @@ static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn)
static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn)
{
- LOG_DIS("l.ori r%d, r%d, %d\n", a->d, a->a, a->k);
check_r0_write(a->d);
tcg_gen_ori_tl(cpu_R[a->d], cpu_R[a->a], a->k);
return true;
@@ -927,7 +853,6 @@ static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn)
static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn)
{
- LOG_DIS("l.xori r%d, r%d, %d\n", a->d, a->a, a->i);
check_r0_write(a->d);
tcg_gen_xori_tl(cpu_R[a->d], cpu_R[a->a], a->i);
return true;
@@ -935,72 +860,73 @@ static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn)
static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn)
{
- LOG_DIS("l.mfspr r%d, r%d, %d\n", a->d, a->a, a->k);
check_r0_write(a->d);
-#ifdef CONFIG_USER_ONLY
- gen_illegal_exception(dc);
-#else
- if (dc->mem_idx == MMU_USER_IDX) {
+ if (is_user(dc)) {
gen_illegal_exception(dc);
} else {
- TCGv_i32 ti = tcg_const_i32(a->k);
- gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], cpu_R[a->a], ti);
- tcg_temp_free_i32(ti);
+ TCGv spr = tcg_temp_new();
+ tcg_gen_ori_tl(spr, cpu_R[a->a], a->k);
+ gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], spr);
+ tcg_temp_free(spr);
}
-#endif
return true;
}
static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn)
{
- LOG_DIS("l.mtspr r%d, r%d, %d\n", a->a, a->b, a->k);
-
-#ifdef CONFIG_USER_ONLY
- gen_illegal_exception(dc);
-#else
- if (dc->mem_idx == MMU_USER_IDX) {
+ if (is_user(dc)) {
gen_illegal_exception(dc);
} else {
- TCGv_i32 ti = tcg_const_i32(a->k);
- gen_helper_mtspr(cpu_env, cpu_R[a->a], cpu_R[a->b], ti);
- tcg_temp_free_i32(ti);
+ TCGv spr;
+
+ /* For SR, we will need to exit the TB to recognize the new
+ * exception state. For NPC, in theory this counts as a branch
+ * (although the SPR only exists for use by an ICE). Save all
+ * of the cpu state first, allowing it to be overwritten.
+ */
+ if (dc->delayed_branch) {
+ tcg_gen_mov_tl(cpu_pc, jmp_pc);
+ tcg_gen_discard_tl(jmp_pc);
+ } else {
+ tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4);
+ }
+ dc->base.is_jmp = DISAS_EXIT;
+
+ spr = tcg_temp_new();
+ tcg_gen_ori_tl(spr, cpu_R[a->a], a->k);
+ gen_helper_mtspr(cpu_env, spr, cpu_R[a->b]);
+ tcg_temp_free(spr);
}
-#endif
return true;
}
static bool trans_l_mac(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("l.mac r%d, r%d\n", a->a, a->b);
gen_mac(dc, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_msb(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("l.msb r%d, r%d\n", a->a, a->b);
gen_msb(dc, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_macu(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("l.mac r%d, r%d\n", a->a, a->b);
gen_macu(dc, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_msbu(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("l.msb r%d, r%d\n", a->a, a->b);
gen_msbu(dc, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn)
{
- LOG_DIS("l.slli r%d, r%d, %d\n", a->d, a->a, a->l);
check_r0_write(a->d);
tcg_gen_shli_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1));
return true;
@@ -1008,7 +934,6 @@ static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn)
static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn)
{
- LOG_DIS("l.srli r%d, r%d, %d\n", a->d, a->a, a->l);
check_r0_write(a->d);
tcg_gen_shri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1));
return true;
@@ -1016,7 +941,6 @@ static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn)
static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn)
{
- LOG_DIS("l.srai r%d, r%d, %d\n", a->d, a->a, a->l);
check_r0_write(a->d);
tcg_gen_sari_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1));
return true;
@@ -1024,7 +948,6 @@ static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn)
static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn)
{
- LOG_DIS("l.rori r%d, r%d, %d\n", a->d, a->a, a->l);
check_r0_write(a->d);
tcg_gen_rotri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1));
return true;
@@ -1032,7 +955,6 @@ static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn)
static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn)
{
- LOG_DIS("l.movhi r%d, %d\n", a->d, a->k);
check_r0_write(a->d);
tcg_gen_movi_tl(cpu_R[a->d], a->k << 16);
return true;
@@ -1040,7 +962,6 @@ static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn)
static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn)
{
- LOG_DIS("l.macrc r%d\n", a->d);
check_r0_write(a->d);
tcg_gen_trunc_i64_tl(cpu_R[a->d], cpu_mac);
tcg_gen_movi_i64(cpu_mac, 0);
@@ -1049,147 +970,126 @@ static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn)
static bool trans_l_sfeq(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfeq r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfne(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfne r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfgtu r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfgeu r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfltu(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfltu r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfleu(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfleu r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfgts(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfgts r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfges(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfges r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sflts(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sflts r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfles(DisasContext *dc, arg_ab *a, TCGCond cond)
{
- LOG_DIS("l.sfles r%d, r%d\n", a->a, a->b);
tcg_gen_setcond_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]);
return true;
}
static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfeqi r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sfnei(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfnei r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfgtui r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfgeui r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sfltui(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfltui r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sfleui(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfleui r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfgtsi r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfgesi r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sfltsi r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sflesi(DisasContext *dc, arg_ai *a, TCGCond cond)
{
- LOG_DIS("l.sflesi r%d, %d\n", a->a, a->i);
tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], a->i);
return true;
}
static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn)
{
- LOG_DIS("l.sys %d\n", a->k);
tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
gen_exception(dc, EXCP_SYSCALL);
dc->base.is_jmp = DISAS_NORETURN;
@@ -1198,7 +1098,6 @@ static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn)
static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn)
{
- LOG_DIS("l.trap %d\n", a->k);
tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
gen_exception(dc, EXCP_TRAP);
dc->base.is_jmp = DISAS_NORETURN;
@@ -1207,37 +1106,28 @@ static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn)
static bool trans_l_msync(DisasContext *dc, arg_l_msync *a, uint32_t insn)
{
- LOG_DIS("l.msync\n");
tcg_gen_mb(TCG_MO_ALL);
return true;
}
static bool trans_l_psync(DisasContext *dc, arg_l_psync *a, uint32_t insn)
{
- LOG_DIS("l.psync\n");
return true;
}
static bool trans_l_csync(DisasContext *dc, arg_l_csync *a, uint32_t insn)
{
- LOG_DIS("l.csync\n");
return true;
}
static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn)
{
- LOG_DIS("l.rfe\n");
-
-#ifdef CONFIG_USER_ONLY
- gen_illegal_exception(dc);
-#else
- if (dc->mem_idx == MMU_USER_IDX) {
+ if (is_user(dc)) {
gen_illegal_exception(dc);
} else {
gen_helper_rfe(cpu_env);
- dc->base.is_jmp = DISAS_UPDATE;
+ dc->base.is_jmp = DISAS_EXIT;
}
-#endif
return true;
}
@@ -1274,56 +1164,48 @@ static void do_fpcmp(DisasContext *dc, arg_ab *a,
static bool trans_lf_add_s(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("lf.add.s r%d, r%d, r%d\n", a->d, a->a, a->b);
do_fp3(dc, a, gen_helper_float_add_s);
return true;
}
static bool trans_lf_sub_s(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("lf.sub.s r%d, r%d, r%d\n", a->d, a->a, a->b);
do_fp3(dc, a, gen_helper_float_sub_s);
return true;
}
static bool trans_lf_mul_s(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("lf.mul.s r%d, r%d, r%d\n", a->d, a->a, a->b);
do_fp3(dc, a, gen_helper_float_mul_s);
return true;
}
static bool trans_lf_div_s(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("lf.div.s r%d, r%d, r%d\n", a->d, a->a, a->b);
do_fp3(dc, a, gen_helper_float_div_s);
return true;
}
static bool trans_lf_rem_s(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("lf.rem.s r%d, r%d, r%d\n", a->d, a->a, a->b);
do_fp3(dc, a, gen_helper_float_rem_s);
return true;
}
static bool trans_lf_itof_s(DisasContext *dc, arg_da *a, uint32_t insn)
{
- LOG_DIS("lf.itof.s r%d, r%d\n", a->d, a->a);
do_fp2(dc, a, gen_helper_itofs);
return true;
}
static bool trans_lf_ftoi_s(DisasContext *dc, arg_da *a, uint32_t insn)
{
- LOG_DIS("lf.ftoi.s r%d, r%d\n", a->d, a->a);
do_fp2(dc, a, gen_helper_ftois);
return true;
}
static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn)
{
- LOG_DIS("lf.madd.s r%d, r%d, r%d\n", a->d, a->a, a->b);
check_r0_write(a->d);
gen_helper_float_madd_s(cpu_R[a->d], cpu_env, cpu_R[a->d],
cpu_R[a->a], cpu_R[a->b]);
@@ -1333,42 +1215,36 @@ static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn)
static bool trans_lf_sfeq_s(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("lf.sfeq.s r%d, r%d\n", a->a, a->b);
do_fpcmp(dc, a, gen_helper_float_eq_s, false, false);
return true;
}
static bool trans_lf_sfne_s(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("lf.sfne.s r%d, r%d\n", a->a, a->b);
do_fpcmp(dc, a, gen_helper_float_eq_s, true, false);
return true;
}
static bool trans_lf_sfgt_s(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("lf.sfgt.s r%d, r%d\n", a->a, a->b);
do_fpcmp(dc, a, gen_helper_float_lt_s, false, true);
return true;
}
static bool trans_lf_sfge_s(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("lf.sfge.s r%d, r%d\n", a->a, a->b);
do_fpcmp(dc, a, gen_helper_float_le_s, false, true);
return true;
}
static bool trans_lf_sflt_s(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("lf.sflt.s r%d, r%d\n", a->a, a->b);
do_fpcmp(dc, a, gen_helper_float_lt_s, false, false);
return true;
}
static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a, uint32_t insn)
{
- LOG_DIS("lf.sfle.s r%d, r%d\n", a->a, a->b);
do_fpcmp(dc, a, gen_helper_float_le_s, false, false);
return true;
}
@@ -1382,6 +1258,8 @@ static void openrisc_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs)
dc->mem_idx = cpu_mmu_index(env, false);
dc->tb_flags = dc->base.tb->flags;
dc->delayed_branch = (dc->tb_flags & TB_FLAGS_DFLAG) != 0;
+ dc->jmp_pc_imm = -1;
+
bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
dc->base.max_insns = MIN(dc->base.max_insns, bound);
}
@@ -1434,50 +1312,81 @@ static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
}
dc->base.pc_next += 4;
- /* delay slot */
- if (dc->delayed_branch) {
- dc->delayed_branch--;
- if (!dc->delayed_branch) {
- tcg_gen_mov_tl(cpu_pc, jmp_pc);
- tcg_gen_discard_tl(jmp_pc);
- dc->base.is_jmp = DISAS_UPDATE;
- return;
- }
+ /* When exiting the delay slot normally, exit via jmp_pc.
+ * For DISAS_NORETURN, we have raised an exception and already exited.
+ * For DISAS_EXIT, we found l.rfe in a delay slot. There's nothing
+ * in the manual saying this is illegal, but it surely it should.
+ * At least or1ksim overrides pcnext and ignores the branch.
+ */
+ if (dc->delayed_branch
+ && --dc->delayed_branch == 0
+ && dc->base.is_jmp == DISAS_NEXT) {
+ dc->base.is_jmp = DISAS_JUMP;
}
}
static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
+ target_ulong jmp_dest;
+
+ /* If we have already exited the TB, nothing following has effect. */
+ if (dc->base.is_jmp == DISAS_NORETURN) {
+ return;
+ }
+ /* Adjust the delayed branch state for the next TB. */
if ((dc->tb_flags & TB_FLAGS_DFLAG ? 1 : 0) != (dc->delayed_branch != 0)) {
tcg_gen_movi_i32(cpu_dflag, dc->delayed_branch != 0);
}
- tcg_gen_movi_tl(cpu_ppc, dc->base.pc_next - 4);
- if (dc->base.is_jmp == DISAS_NEXT) {
- dc->base.is_jmp = DISAS_UPDATE;
- tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
- }
- if (unlikely(dc->base.singlestep_enabled)) {
- gen_exception(dc, EXCP_DEBUG);
- } else {
- switch (dc->base.is_jmp) {
- case DISAS_TOO_MANY:
- gen_goto_tb(dc, 0, dc->base.pc_next);
- break;
- case DISAS_NORETURN:
- case DISAS_JUMP:
- case DISAS_TB_JUMP:
+ /* For DISAS_TOO_MANY, jump to the next insn. */
+ jmp_dest = dc->base.pc_next;
+ tcg_gen_movi_tl(cpu_ppc, jmp_dest - 4);
+
+ switch (dc->base.is_jmp) {
+ case DISAS_JUMP:
+ jmp_dest = dc->jmp_pc_imm;
+ if (jmp_dest == -1) {
+ /* The jump destination is indirect/computed; use jmp_pc. */
+ tcg_gen_mov_tl(cpu_pc, jmp_pc);
+ tcg_gen_discard_tl(jmp_pc);
+ if (unlikely(dc->base.singlestep_enabled)) {
+ gen_exception(dc, EXCP_DEBUG);
+ } else {
+ tcg_gen_lookup_and_goto_ptr();
+ }
break;
- case DISAS_UPDATE:
- /* indicate that the hash table must be used
- to find the next TB */
+ }
+ /* The jump destination is direct; use jmp_pc_imm.
+ However, we will have stored into jmp_pc as well;
+ we know now that it wasn't needed. */
+ tcg_gen_discard_tl(jmp_pc);
+ /* fallthru */
+
+ case DISAS_TOO_MANY:
+ if (unlikely(dc->base.singlestep_enabled)) {
+ tcg_gen_movi_tl(cpu_pc, jmp_dest);
+ gen_exception(dc, EXCP_DEBUG);
+ } else if ((dc->base.pc_first ^ jmp_dest) & TARGET_PAGE_MASK) {
+ tcg_gen_movi_tl(cpu_pc, jmp_dest);
+ tcg_gen_lookup_and_goto_ptr();
+ } else {
+ tcg_gen_goto_tb(0);
+ tcg_gen_movi_tl(cpu_pc, jmp_dest);
+ tcg_gen_exit_tb(dc->base.tb, 0);
+ }
+ break;
+
+ case DISAS_EXIT:
+ if (unlikely(dc->base.singlestep_enabled)) {
+ gen_exception(dc, EXCP_DEBUG);
+ } else {
tcg_gen_exit_tb(NULL, 0);
- break;
- default:
- g_assert_not_reached();
}
+ break;
+ default:
+ g_assert_not_reached();
}
}
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index c7f3fb6..4edcf62 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -196,7 +196,6 @@ enum {
/* 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 */
- POWERPC_EXCP_STCX = 0x204 /* Conditional stores in user mode */
};
/* Exceptions error codes */
@@ -994,10 +993,6 @@ struct CPUPPCState {
/* Reservation value */
target_ulong reserve_val;
target_ulong reserve_val2;
- /* Reservation store address */
- target_ulong reserve_ea;
- /* Reserved store source register and size */
- target_ulong reserve_info;
/* Those ones are used in supervisor mode only */
/* machine state register */
@@ -1015,6 +1010,9 @@ struct CPUPPCState {
/* Next instruction pointer */
target_ulong nip;
+ /* High part of 128-bit helper return. */
+ uint64_t retxh;
+
int access_type; /* when a memory exception occurs, the access
type is stored here */
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index c092fbe..d6e97a9 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -22,7 +22,7 @@
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h"
-
+#include "internal.h"
#include "helper_regs.h"
//#define DEBUG_OP
@@ -1198,3 +1198,19 @@ void helper_book3s_msgsnd(target_ulong rb)
qemu_mutex_unlock_iothread();
}
#endif
+
+void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
+ MMUAccessType access_type,
+ int mmu_idx, uintptr_t retaddr)
+{
+ CPUPPCState *env = cs->env_ptr;
+ uint32_t insn;
+
+ /* Restore state and reload the insn we executed, for filling in DSISR. */
+ cpu_restore_state(cs, retaddr, true);
+ insn = cpu_ldl_code(env, env->nip);
+
+ cs->exception_index = POWERPC_EXCP_ALIGN;
+ env->error_code = insn & 0x03FF0000;
+ cpu_loop_exit(cs);
+}
diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c
index 7714bfe..8675d93 100644
--- a/target/ppc/fpu_helper.c
+++ b/target/ppc/fpu_helper.c
@@ -274,6 +274,7 @@ static inline void float_inexact_excp(CPUPPCState *env)
{
CPUState *cs = CPU(ppc_env_get_cpu(env));
+ env->fpscr |= 1 << FPSCR_FI;
env->fpscr |= 1 << FPSCR_XX;
/* Update the floating-point exception summary */
env->fpscr |= FP_FX;
@@ -533,6 +534,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr)
{
CPUState *cs = CPU(ppc_env_get_cpu(env));
int status = get_float_exception_flags(&env->fp_status);
+ bool inexact_happened = false;
if (status & float_flag_divbyzero) {
float_zero_divide_excp(env, raddr);
@@ -542,6 +544,12 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr)
float_underflow_excp(env);
} else if (status & float_flag_inexact) {
float_inexact_excp(env);
+ inexact_happened = true;
+ }
+
+ /* if the inexact flag was not set */
+ if (inexact_happened == false) {
+ env->fpscr &= ~(1 << FPSCR_FI); /* clear the FPSCR[FI] bit */
}
if (cs->exception_index == POWERPC_EXCP_PROGRAM &&
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index d751f0e..5706c24 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -799,3 +799,14 @@ DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32)
DEF_HELPER_1(tbegin, void, env)
DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env)
+
+#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128)
+DEF_HELPER_FLAGS_3(lq_le_parallel, TCG_CALL_NO_WG, i64, env, tl, i32)
+DEF_HELPER_FLAGS_3(lq_be_parallel, TCG_CALL_NO_WG, i64, env, tl, i32)
+DEF_HELPER_FLAGS_5(stq_le_parallel, TCG_CALL_NO_WG,
+ void, env, tl, i64, i64, i32)
+DEF_HELPER_FLAGS_5(stq_be_parallel, TCG_CALL_NO_WG,
+ void, env, tl, i64, i64, i32)
+DEF_HELPER_5(stqcx_le_parallel, i32, env, tl, i64, i64, i32)
+DEF_HELPER_5(stqcx_be_parallel, i32, env, tl, i64, i64, i32)
+#endif
diff --git a/target/ppc/internal.h b/target/ppc/internal.h
index 1f441c6..a9bcadf 100644
--- a/target/ppc/internal.h
+++ b/target/ppc/internal.h
@@ -252,4 +252,9 @@ static inline void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env)
void helper_compute_fprf_float16(CPUPPCState *env, float16 arg);
void helper_compute_fprf_float32(CPUPPCState *env, float32 arg);
void helper_compute_fprf_float128(CPUPPCState *env, float128 arg);
+
+/* Raise a data fault alignment exception for the specified virtual address */
+void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+ MMUAccessType access_type,
+ int mmu_idx, uintptr_t retaddr);
#endif /* PPC_INTERNAL_H */
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 4df4ff6..9211ee2 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -248,107 +248,25 @@ static int kvm_booke206_tlb_init(PowerPCCPU *cpu)
#if defined(TARGET_PPC64)
-static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu,
- struct kvm_ppc_smmu_info *info)
+static void kvm_get_smmu_info(struct kvm_ppc_smmu_info *info, Error **errp)
{
- CPUPPCState *env = &cpu->env;
- CPUState *cs = CPU(cpu);
-
- memset(info, 0, sizeof(*info));
-
- /* We don't have the new KVM_PPC_GET_SMMU_INFO ioctl, so
- * need to "guess" what the supported page sizes are.
- *
- * For that to work we make a few assumptions:
- *
- * - Check whether we are running "PR" KVM which only supports 4K
- * and 16M pages, but supports them regardless of the backing
- * store characteritics. We also don't support 1T segments.
- *
- * This is safe as if HV KVM ever supports that capability or PR
- * KVM grows supports for more page/segment sizes, those versions
- * will have implemented KVM_CAP_PPC_GET_SMMU_INFO and thus we
- * will not hit this fallback
- *
- * - Else we are running HV KVM. This means we only support page
- * sizes that fit in the backing store. Additionally we only
- * advertize 64K pages if the processor is ARCH 2.06 and we assume
- * P7 encodings for the SLB and hash table. Here too, we assume
- * support for any newer processor will mean a kernel that
- * implements KVM_CAP_PPC_GET_SMMU_INFO and thus doesn't hit
- * this fallback.
- */
- if (kvmppc_is_pr(cs->kvm_state)) {
- /* No flags */
- info->flags = 0;
- info->slb_size = 64;
-
- /* Standard 4k base page size segment */
- info->sps[0].page_shift = 12;
- info->sps[0].slb_enc = 0;
- info->sps[0].enc[0].page_shift = 12;
- info->sps[0].enc[0].pte_enc = 0;
-
- /* Standard 16M large page size segment */
- info->sps[1].page_shift = 24;
- info->sps[1].slb_enc = SLB_VSID_L;
- info->sps[1].enc[0].page_shift = 24;
- info->sps[1].enc[0].pte_enc = 0;
- } else {
- int i = 0;
-
- /* HV KVM has backing store size restrictions */
- info->flags = KVM_PPC_PAGE_SIZES_REAL;
-
- if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) {
- info->flags |= KVM_PPC_1T_SEGMENTS;
- }
-
- if (env->mmu_model == POWERPC_MMU_2_06 ||
- env->mmu_model == POWERPC_MMU_2_07) {
- info->slb_size = 32;
- } else {
- info->slb_size = 64;
- }
+ int ret;
- /* Standard 4k base page size segment */
- info->sps[i].page_shift = 12;
- info->sps[i].slb_enc = 0;
- info->sps[i].enc[0].page_shift = 12;
- info->sps[i].enc[0].pte_enc = 0;
- i++;
-
- /* 64K on MMU 2.06 and later */
- if (env->mmu_model == POWERPC_MMU_2_06 ||
- env->mmu_model == POWERPC_MMU_2_07) {
- info->sps[i].page_shift = 16;
- info->sps[i].slb_enc = 0x110;
- info->sps[i].enc[0].page_shift = 16;
- info->sps[i].enc[0].pte_enc = 1;
- i++;
- }
+ assert(kvm_state != NULL);
- /* Standard 16M large page size segment */
- info->sps[i].page_shift = 24;
- info->sps[i].slb_enc = SLB_VSID_L;
- info->sps[i].enc[0].page_shift = 24;
- info->sps[i].enc[0].pte_enc = 0;
+ if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) {
+ error_setg(errp, "KVM doesn't expose the MMU features it supports");
+ error_append_hint(errp, "Consider switching to a newer KVM\n");
+ return;
}
-}
-
-static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info)
-{
- CPUState *cs = CPU(cpu);
- int ret;
- if (kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) {
- ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_SMMU_INFO, info);
- if (ret == 0) {
- return;
- }
+ ret = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_SMMU_INFO, info);
+ if (ret == 0) {
+ return;
}
- kvm_get_fallback_smmu_info(cpu, info);
+ error_setg_errno(errp, -ret,
+ "KVM failed to provide the MMU features it supports");
}
struct ppc_radix_page_info *kvm_get_radix_page_info(void)
@@ -408,14 +326,13 @@ target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu,
bool kvmppc_hpt_needs_host_contiguous_pages(void)
{
- PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
static struct kvm_ppc_smmu_info smmu_info;
if (!kvm_enabled()) {
return false;
}
- kvm_get_smmu_info(cpu, &smmu_info);
+ kvm_get_smmu_info(&smmu_info, &error_fatal);
return !!(smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL);
}
@@ -423,13 +340,18 @@ void kvm_check_mmu(PowerPCCPU *cpu, Error **errp)
{
struct kvm_ppc_smmu_info smmu_info;
int iq, ik, jq, jk;
+ Error *local_err = NULL;
/* For now, we only have anything to check on hash64 MMUs */
if (!cpu->hash64_opts || !kvm_enabled()) {
return;
}
- kvm_get_smmu_info(cpu, &smmu_info);
+ kvm_get_smmu_info(&smmu_info, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)
&& !(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) {
@@ -2168,7 +2090,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift)
/* Find the largest hardware supported page size that's less than
* or equal to the (logical) backing page size of guest RAM */
- kvm_get_smmu_info(POWERPC_CPU(first_cpu), &info);
+ kvm_get_smmu_info(&info, &error_fatal);
rampagesize = qemu_getrampagesize();
best_page_shift = 0;
diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c
index a34e604..8f0d86d 100644
--- a/target/ppc/mem_helper.c
+++ b/target/ppc/mem_helper.c
@@ -21,9 +21,9 @@
#include "exec/exec-all.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
-
#include "helper_regs.h"
#include "exec/cpu_ldst.h"
+#include "tcg.h"
#include "internal.h"
//#define DEBUG_OP
@@ -215,6 +215,76 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg,
return i;
}
+#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128)
+uint64_t helper_lq_le_parallel(CPUPPCState *env, target_ulong addr,
+ uint32_t opidx)
+{
+ Int128 ret = helper_atomic_ldo_le_mmu(env, addr, opidx, GETPC());
+ env->retxh = int128_gethi(ret);
+ return int128_getlo(ret);
+}
+
+uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr,
+ uint32_t opidx)
+{
+ Int128 ret = helper_atomic_ldo_be_mmu(env, addr, opidx, GETPC());
+ env->retxh = int128_gethi(ret);
+ return int128_getlo(ret);
+}
+
+void helper_stq_le_parallel(CPUPPCState *env, target_ulong addr,
+ uint64_t lo, uint64_t hi, uint32_t opidx)
+{
+ Int128 val = int128_make128(lo, hi);
+ helper_atomic_sto_le_mmu(env, addr, val, opidx, GETPC());
+}
+
+void helper_stq_be_parallel(CPUPPCState *env, target_ulong addr,
+ uint64_t lo, uint64_t hi, uint32_t opidx)
+{
+ Int128 val = int128_make128(lo, hi);
+ helper_atomic_sto_be_mmu(env, addr, val, opidx, GETPC());
+}
+
+uint32_t helper_stqcx_le_parallel(CPUPPCState *env, target_ulong addr,
+ uint64_t new_lo, uint64_t new_hi,
+ uint32_t opidx)
+{
+ bool success = false;
+
+ if (likely(addr == env->reserve_addr)) {
+ Int128 oldv, cmpv, newv;
+
+ cmpv = int128_make128(env->reserve_val2, env->reserve_val);
+ newv = int128_make128(new_lo, new_hi);
+ oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv,
+ opidx, GETPC());
+ success = int128_eq(oldv, cmpv);
+ }
+ env->reserve_addr = -1;
+ return env->so + success * CRF_EQ_BIT;
+}
+
+uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr,
+ uint64_t new_lo, uint64_t new_hi,
+ uint32_t opidx)
+{
+ bool success = false;
+
+ if (likely(addr == env->reserve_addr)) {
+ Int128 oldv, cmpv, newv;
+
+ cmpv = int128_make128(env->reserve_val2, env->reserve_val);
+ newv = int128_make128(new_lo, new_hi);
+ oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv,
+ opidx, GETPC());
+ success = int128_eq(oldv, cmpv);
+ }
+ env->reserve_addr = -1;
+ return env->so + success * CRF_EQ_BIT;
+}
+#endif
+
/*****************************************************************************/
/* Altivec extension helpers */
#if defined(HOST_WORDS_BIGENDIAN)
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 3a215a1..9eaa10b 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -2388,23 +2388,6 @@ static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1,
}
}
-static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask)
-{
- TCGLabel *l1 = gen_new_label();
- TCGv t0 = tcg_temp_new();
- TCGv_i32 t1, t2;
- tcg_gen_andi_tl(t0, EA, mask);
- tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
- t1 = tcg_const_i32(POWERPC_EXCP_ALIGN);
- t2 = tcg_const_i32(ctx->opcode & 0x03FF0000);
- gen_update_nip(ctx, ctx->base.pc_next - 4);
- gen_helper_raise_exception_err(cpu_env, t1, t2);
- tcg_temp_free_i32(t1);
- tcg_temp_free_i32(t2);
- gen_set_label(l1);
- tcg_temp_free(t0);
-}
-
static inline void gen_align_no_le(DisasContext *ctx)
{
gen_exception_err(ctx, POWERPC_EXCP_ALIGN,
@@ -2607,7 +2590,7 @@ static void gen_ld(DisasContext *ctx)
static void gen_lq(DisasContext *ctx)
{
int ra, rd;
- TCGv EA;
+ TCGv EA, hi, lo;
/* lq is a legal user mode instruction starting in ISA 2.07 */
bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0;
@@ -2633,16 +2616,35 @@ static void gen_lq(DisasContext *ctx)
EA = tcg_temp_new();
gen_addr_imm_index(ctx, EA, 0x0F);
- /* We only need to swap high and low halves. gen_qemu_ld64_i64 does
- necessary 64-bit byteswap already. */
- if (unlikely(ctx->le_mode)) {
- gen_qemu_ld64_i64(ctx, cpu_gpr[rd + 1], EA);
+ /* Note that the low part is always in RD+1, even in LE mode. */
+ lo = cpu_gpr[rd + 1];
+ hi = cpu_gpr[rd];
+
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+#ifdef CONFIG_ATOMIC128
+ TCGv_i32 oi = tcg_temp_new_i32();
+ if (ctx->le_mode) {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx));
+ gen_helper_lq_le_parallel(lo, cpu_env, EA, oi);
+ } else {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx));
+ gen_helper_lq_be_parallel(lo, cpu_env, EA, oi);
+ }
+ tcg_temp_free_i32(oi);
+ tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh));
+#else
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+#endif
+ } else if (ctx->le_mode) {
+ tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ);
gen_addr_add(ctx, EA, EA, 8);
- gen_qemu_ld64_i64(ctx, cpu_gpr[rd], EA);
+ tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_LEQ);
} else {
- gen_qemu_ld64_i64(ctx, cpu_gpr[rd], EA);
+ tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_BEQ);
gen_addr_add(ctx, EA, EA, 8);
- gen_qemu_ld64_i64(ctx, cpu_gpr[rd + 1], EA);
+ tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_BEQ);
}
tcg_temp_free(EA);
}
@@ -2741,6 +2743,7 @@ static void gen_std(DisasContext *ctx)
if ((ctx->opcode & 0x3) == 0x2) { /* stq */
bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0;
bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0;
+ TCGv hi, lo;
if (!(ctx->insns_flags & PPC_64BX)) {
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
@@ -2764,20 +2767,38 @@ static void gen_std(DisasContext *ctx)
EA = tcg_temp_new();
gen_addr_imm_index(ctx, EA, 0x03);
- /* We only need to swap high and low halves. gen_qemu_st64_i64 does
- necessary 64-bit byteswap already. */
- if (unlikely(ctx->le_mode)) {
- gen_qemu_st64_i64(ctx, cpu_gpr[rs + 1], EA);
+ /* Note that the low part is always in RS+1, even in LE mode. */
+ lo = cpu_gpr[rs + 1];
+ hi = cpu_gpr[rs];
+
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+#ifdef CONFIG_ATOMIC128
+ TCGv_i32 oi = tcg_temp_new_i32();
+ if (ctx->le_mode) {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx));
+ gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi);
+ } else {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx));
+ gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi);
+ }
+ tcg_temp_free_i32(oi);
+#else
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+#endif
+ } else if (ctx->le_mode) {
+ tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_LEQ);
gen_addr_add(ctx, EA, EA, 8);
- gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA);
+ tcg_gen_qemu_st_i64(hi, EA, ctx->mem_idx, MO_LEQ);
} else {
- gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA);
+ tcg_gen_qemu_st_i64(hi, EA, ctx->mem_idx, MO_BEQ);
gen_addr_add(ctx, EA, EA, 8);
- gen_qemu_st64_i64(ctx, cpu_gpr[rs + 1], EA);
+ tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_BEQ);
}
tcg_temp_free(EA);
} else {
- /* std / stdu*/
+ /* std / stdu */
if (Rc(ctx->opcode)) {
if (unlikely(rA(ctx->opcode) == 0)) {
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
@@ -3032,23 +3053,24 @@ static void gen_isync(DisasContext *ctx)
#define MEMOP_GET_SIZE(x) (1 << ((x) & MO_SIZE))
-#define LARX(name, memop) \
-static void gen_##name(DisasContext *ctx) \
-{ \
- TCGv t0; \
- TCGv gpr = cpu_gpr[rD(ctx->opcode)]; \
- int len = MEMOP_GET_SIZE(memop); \
- gen_set_access_type(ctx, ACCESS_RES); \
- t0 = tcg_temp_local_new(); \
- gen_addr_reg_index(ctx, t0); \
- if ((len) > 1) { \
- gen_check_align(ctx, t0, (len)-1); \
- } \
- tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop); \
- tcg_gen_mov_tl(cpu_reserve, t0); \
- tcg_gen_mov_tl(cpu_reserve_val, gpr); \
- tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); \
- tcg_temp_free(t0); \
+static void gen_load_locked(DisasContext *ctx, TCGMemOp memop)
+{
+ TCGv gpr = cpu_gpr[rD(ctx->opcode)];
+ TCGv t0 = tcg_temp_new();
+
+ gen_set_access_type(ctx, ACCESS_RES);
+ gen_addr_reg_index(ctx, t0);
+ tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop | MO_ALIGN);
+ tcg_gen_mov_tl(cpu_reserve, t0);
+ tcg_gen_mov_tl(cpu_reserve_val, gpr);
+ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ);
+ tcg_temp_free(t0);
+}
+
+#define LARX(name, memop) \
+static void gen_##name(DisasContext *ctx) \
+{ \
+ gen_load_locked(ctx, memop); \
}
/* lwarx */
@@ -3056,134 +3078,239 @@ LARX(lbarx, DEF_MEMOP(MO_UB))
LARX(lharx, DEF_MEMOP(MO_UW))
LARX(lwarx, DEF_MEMOP(MO_UL))
-#define LD_ATOMIC(name, memop, tp, op, eop) \
-static void gen_##name(DisasContext *ctx) \
-{ \
- int len = MEMOP_GET_SIZE(memop); \
- uint32_t gpr_FC = FC(ctx->opcode); \
- TCGv EA = tcg_temp_local_new(); \
- TCGv_##tp t0, t1; \
- \
- gen_addr_register(ctx, EA); \
- if (len > 1) { \
- gen_check_align(ctx, EA, len - 1); \
- } \
- t0 = tcg_temp_new_##tp(); \
- t1 = tcg_temp_new_##tp(); \
- tcg_gen_##op(t0, cpu_gpr[rD(ctx->opcode) + 1]); \
- \
- switch (gpr_FC) { \
- case 0: /* Fetch and add */ \
- tcg_gen_atomic_fetch_add_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 1: /* Fetch and xor */ \
- tcg_gen_atomic_fetch_xor_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 2: /* Fetch and or */ \
- tcg_gen_atomic_fetch_or_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 3: /* Fetch and 'and' */ \
- tcg_gen_atomic_fetch_and_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 8: /* Swap */ \
- tcg_gen_atomic_xchg_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 4: /* Fetch and max unsigned */ \
- case 5: /* Fetch and max signed */ \
- case 6: /* Fetch and min unsigned */ \
- case 7: /* Fetch and min signed */ \
- case 16: /* compare and swap not equal */ \
- case 24: /* Fetch and increment bounded */ \
- case 25: /* Fetch and increment equal */ \
- case 28: /* Fetch and decrement bounded */ \
- gen_invalid(ctx); \
- break; \
- default: \
- /* invoke data storage error handler */ \
- gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); \
- } \
- tcg_gen_##eop(cpu_gpr[rD(ctx->opcode)], t1); \
- tcg_temp_free_##tp(t0); \
- tcg_temp_free_##tp(t1); \
- tcg_temp_free(EA); \
-}
-
-LD_ATOMIC(lwat, DEF_MEMOP(MO_UL), i32, trunc_tl_i32, extu_i32_tl)
-#if defined(TARGET_PPC64)
-LD_ATOMIC(ldat, DEF_MEMOP(MO_Q), i64, mov_i64, mov_i64)
-#endif
+static void gen_fetch_inc_conditional(DisasContext *ctx, TCGMemOp memop,
+ TCGv EA, TCGCond cond, int addend)
+{
+ TCGv t = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ TCGv u = tcg_temp_new();
-#define ST_ATOMIC(name, memop, tp, op) \
-static void gen_##name(DisasContext *ctx) \
-{ \
- int len = MEMOP_GET_SIZE(memop); \
- uint32_t gpr_FC = FC(ctx->opcode); \
- TCGv EA = tcg_temp_local_new(); \
- TCGv_##tp t0, t1; \
- \
- gen_addr_register(ctx, EA); \
- if (len > 1) { \
- gen_check_align(ctx, EA, len - 1); \
- } \
- t0 = tcg_temp_new_##tp(); \
- t1 = tcg_temp_new_##tp(); \
- tcg_gen_##op(t0, cpu_gpr[rD(ctx->opcode) + 1]); \
- \
- switch (gpr_FC) { \
- case 0: /* add and Store */ \
- tcg_gen_atomic_add_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 1: /* xor and Store */ \
- tcg_gen_atomic_xor_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 2: /* Or and Store */ \
- tcg_gen_atomic_or_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 3: /* 'and' and Store */ \
- tcg_gen_atomic_and_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \
- break; \
- case 4: /* Store max unsigned */ \
- case 5: /* Store max signed */ \
- case 6: /* Store min unsigned */ \
- case 7: /* Store min signed */ \
- case 24: /* Store twin */ \
- gen_invalid(ctx); \
- break; \
- default: \
- /* invoke data storage error handler */ \
- gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); \
- } \
- tcg_temp_free_##tp(t0); \
- tcg_temp_free_##tp(t1); \
- tcg_temp_free(EA); \
-}
-
-ST_ATOMIC(stwat, DEF_MEMOP(MO_UL), i32, trunc_tl_i32)
-#if defined(TARGET_PPC64)
-ST_ATOMIC(stdat, DEF_MEMOP(MO_Q), i64, mov_i64)
+ tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop);
+ tcg_gen_addi_tl(t2, EA, MEMOP_GET_SIZE(memop));
+ tcg_gen_qemu_ld_tl(t2, t2, ctx->mem_idx, memop);
+ tcg_gen_addi_tl(u, t, addend);
+
+ /* E.g. for fetch and increment bounded... */
+ /* mem(EA,s) = (t != t2 ? u = t + 1 : t) */
+ tcg_gen_movcond_tl(cond, u, t, t2, u, t);
+ tcg_gen_qemu_st_tl(u, EA, ctx->mem_idx, memop);
+
+ /* RT = (t != t2 ? t : u = 1<<(s*8-1)) */
+ tcg_gen_movi_tl(u, 1 << (MEMOP_GET_SIZE(memop) * 8 - 1));
+ tcg_gen_movcond_tl(cond, cpu_gpr[rD(ctx->opcode)], t, t2, t, u);
+
+ tcg_temp_free(t);
+ tcg_temp_free(t2);
+ tcg_temp_free(u);
+}
+
+static void gen_ld_atomic(DisasContext *ctx, TCGMemOp memop)
+{
+ uint32_t gpr_FC = FC(ctx->opcode);
+ TCGv EA = tcg_temp_new();
+ int rt = rD(ctx->opcode);
+ bool need_serial;
+ TCGv src, dst;
+
+ gen_addr_register(ctx, EA);
+ dst = cpu_gpr[rt];
+ src = cpu_gpr[(rt + 1) & 31];
+
+ need_serial = false;
+ memop |= MO_ALIGN;
+ switch (gpr_FC) {
+ case 0: /* Fetch and add */
+ tcg_gen_atomic_fetch_add_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+ case 1: /* Fetch and xor */
+ tcg_gen_atomic_fetch_xor_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+ case 2: /* Fetch and or */
+ tcg_gen_atomic_fetch_or_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+ case 3: /* Fetch and 'and' */
+ tcg_gen_atomic_fetch_and_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+ case 4: /* Fetch and max unsigned */
+ tcg_gen_atomic_fetch_umax_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+ case 5: /* Fetch and max signed */
+ tcg_gen_atomic_fetch_smax_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+ case 6: /* Fetch and min unsigned */
+ tcg_gen_atomic_fetch_umin_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+ case 7: /* Fetch and min signed */
+ tcg_gen_atomic_fetch_smin_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+ case 8: /* Swap */
+ tcg_gen_atomic_xchg_tl(dst, EA, src, ctx->mem_idx, memop);
+ break;
+
+ case 16: /* Compare and swap not equal */
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+ need_serial = true;
+ } else {
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ tcg_gen_qemu_ld_tl(t0, EA, ctx->mem_idx, memop);
+ if ((memop & MO_SIZE) == MO_64 || TARGET_LONG_BITS == 32) {
+ tcg_gen_mov_tl(t1, src);
+ } else {
+ tcg_gen_ext32u_tl(t1, src);
+ }
+ tcg_gen_movcond_tl(TCG_COND_NE, t1, t0, t1,
+ cpu_gpr[(rt + 2) & 31], t0);
+ tcg_gen_qemu_st_tl(t1, EA, ctx->mem_idx, memop);
+ tcg_gen_mov_tl(dst, t0);
+
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ }
+ break;
+
+ case 24: /* Fetch and increment bounded */
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+ need_serial = true;
+ } else {
+ gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_NE, 1);
+ }
+ break;
+ case 25: /* Fetch and increment equal */
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+ need_serial = true;
+ } else {
+ gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_EQ, 1);
+ }
+ break;
+ case 28: /* Fetch and decrement bounded */
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+ need_serial = true;
+ } else {
+ gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_NE, -1);
+ }
+ break;
+
+ default:
+ /* invoke data storage error handler */
+ gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL);
+ }
+ tcg_temp_free(EA);
+
+ if (need_serial) {
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ }
+}
+
+static void gen_lwat(DisasContext *ctx)
+{
+ gen_ld_atomic(ctx, DEF_MEMOP(MO_UL));
+}
+
+#ifdef TARGET_PPC64
+static void gen_ldat(DisasContext *ctx)
+{
+ gen_ld_atomic(ctx, DEF_MEMOP(MO_Q));
+}
#endif
-#if defined(CONFIG_USER_ONLY)
-static void gen_conditional_store(DisasContext *ctx, TCGv EA,
- int reg, int memop)
+static void gen_st_atomic(DisasContext *ctx, TCGMemOp memop)
{
- TCGv t0 = tcg_temp_new();
+ uint32_t gpr_FC = FC(ctx->opcode);
+ TCGv EA = tcg_temp_new();
+ TCGv src, discard;
- tcg_gen_st_tl(EA, cpu_env, offsetof(CPUPPCState, reserve_ea));
- tcg_gen_movi_tl(t0, (MEMOP_GET_SIZE(memop) << 5) | reg);
- tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, reserve_info));
- tcg_temp_free(t0);
- gen_exception_err(ctx, POWERPC_EXCP_STCX, 0);
+ gen_addr_register(ctx, EA);
+ src = cpu_gpr[rD(ctx->opcode)];
+ discard = tcg_temp_new();
+
+ memop |= MO_ALIGN;
+ switch (gpr_FC) {
+ case 0: /* add and Store */
+ tcg_gen_atomic_add_fetch_tl(discard, EA, src, ctx->mem_idx, memop);
+ break;
+ case 1: /* xor and Store */
+ tcg_gen_atomic_xor_fetch_tl(discard, EA, src, ctx->mem_idx, memop);
+ break;
+ case 2: /* Or and Store */
+ tcg_gen_atomic_or_fetch_tl(discard, EA, src, ctx->mem_idx, memop);
+ break;
+ case 3: /* 'and' and Store */
+ tcg_gen_atomic_and_fetch_tl(discard, EA, src, ctx->mem_idx, memop);
+ break;
+ case 4: /* Store max unsigned */
+ tcg_gen_atomic_umax_fetch_tl(discard, EA, src, ctx->mem_idx, memop);
+ break;
+ case 5: /* Store max signed */
+ tcg_gen_atomic_smax_fetch_tl(discard, EA, src, ctx->mem_idx, memop);
+ break;
+ case 6: /* Store min unsigned */
+ tcg_gen_atomic_umin_fetch_tl(discard, EA, src, ctx->mem_idx, memop);
+ break;
+ case 7: /* Store min signed */
+ tcg_gen_atomic_smin_fetch_tl(discard, EA, src, ctx->mem_idx, memop);
+ break;
+ case 24: /* Store twin */
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ } else {
+ TCGv t = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ TCGv s = tcg_temp_new();
+ TCGv s2 = tcg_temp_new();
+ TCGv ea_plus_s = tcg_temp_new();
+
+ tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop);
+ tcg_gen_addi_tl(ea_plus_s, EA, MEMOP_GET_SIZE(memop));
+ tcg_gen_qemu_ld_tl(t2, ea_plus_s, ctx->mem_idx, memop);
+ tcg_gen_movcond_tl(TCG_COND_EQ, s, t, t2, src, t);
+ tcg_gen_movcond_tl(TCG_COND_EQ, s2, t, t2, src, t2);
+ tcg_gen_qemu_st_tl(s, EA, ctx->mem_idx, memop);
+ tcg_gen_qemu_st_tl(s2, ea_plus_s, ctx->mem_idx, memop);
+
+ tcg_temp_free(ea_plus_s);
+ tcg_temp_free(s2);
+ tcg_temp_free(s);
+ tcg_temp_free(t2);
+ tcg_temp_free(t);
+ }
+ break;
+ default:
+ /* invoke data storage error handler */
+ gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL);
+ }
+ tcg_temp_free(discard);
+ tcg_temp_free(EA);
}
-#else
-static void gen_conditional_store(DisasContext *ctx, TCGv EA,
- int reg, int memop)
+
+static void gen_stwat(DisasContext *ctx)
+{
+ gen_st_atomic(ctx, DEF_MEMOP(MO_UL));
+}
+
+#ifdef TARGET_PPC64
+static void gen_stdat(DisasContext *ctx)
+{
+ gen_st_atomic(ctx, DEF_MEMOP(MO_Q));
+}
+#endif
+
+static void gen_conditional_store(DisasContext *ctx, TCGMemOp memop)
{
TCGLabel *l1 = gen_new_label();
TCGLabel *l2 = gen_new_label();
- TCGv t0;
+ TCGv t0 = tcg_temp_new();
+ int reg = rS(ctx->opcode);
- tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1);
+ gen_set_access_type(ctx, ACCESS_RES);
+ gen_addr_reg_index(ctx, t0);
+ tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1);
+ tcg_temp_free(t0);
t0 = tcg_temp_new();
tcg_gen_atomic_cmpxchg_tl(t0, cpu_reserve, cpu_reserve_val,
@@ -3206,21 +3333,11 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA,
gen_set_label(l2);
tcg_gen_movi_tl(cpu_reserve, -1);
}
-#endif
-#define STCX(name, memop) \
-static void gen_##name(DisasContext *ctx) \
-{ \
- TCGv t0; \
- int len = MEMOP_GET_SIZE(memop); \
- gen_set_access_type(ctx, ACCESS_RES); \
- t0 = tcg_temp_local_new(); \
- gen_addr_reg_index(ctx, t0); \
- if (len > 1) { \
- gen_check_align(ctx, t0, (len) - 1); \
- } \
- gen_conditional_store(ctx, t0, rS(ctx->opcode), memop); \
- tcg_temp_free(t0); \
+#define STCX(name, memop) \
+static void gen_##name(DisasContext *ctx) \
+{ \
+ gen_conditional_store(ctx, memop); \
}
STCX(stbcx_, DEF_MEMOP(MO_UB))
@@ -3236,9 +3353,8 @@ STCX(stdcx_, DEF_MEMOP(MO_Q))
/* lqarx */
static void gen_lqarx(DisasContext *ctx)
{
- TCGv EA;
int rd = rD(ctx->opcode);
- TCGv gpr1, gpr2;
+ TCGv EA, hi, lo;
if (unlikely((rd & 1) || (rd == rA(ctx->opcode)) ||
(rd == rB(ctx->opcode)))) {
@@ -3247,73 +3363,125 @@ static void gen_lqarx(DisasContext *ctx)
}
gen_set_access_type(ctx, ACCESS_RES);
- EA = tcg_temp_local_new();
+ EA = tcg_temp_new();
gen_addr_reg_index(ctx, EA);
- gen_check_align(ctx, EA, 15);
- if (unlikely(ctx->le_mode)) {
- gpr1 = cpu_gpr[rd+1];
- gpr2 = cpu_gpr[rd];
+
+ /* Note that the low part is always in RD+1, even in LE mode. */
+ lo = cpu_gpr[rd + 1];
+ hi = cpu_gpr[rd];
+
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+#ifdef CONFIG_ATOMIC128
+ TCGv_i32 oi = tcg_temp_new_i32();
+ if (ctx->le_mode) {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ | MO_ALIGN_16,
+ ctx->mem_idx));
+ gen_helper_lq_le_parallel(lo, cpu_env, EA, oi);
+ } else {
+ tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ | MO_ALIGN_16,
+ ctx->mem_idx));
+ gen_helper_lq_be_parallel(lo, cpu_env, EA, oi);
+ }
+ tcg_temp_free_i32(oi);
+ tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh));
+#else
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ tcg_temp_free(EA);
+ return;
+#endif
+ } else if (ctx->le_mode) {
+ tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ | MO_ALIGN_16);
+ tcg_gen_mov_tl(cpu_reserve, EA);
+ gen_addr_add(ctx, EA, EA, 8);
+ tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_LEQ);
} else {
- gpr1 = cpu_gpr[rd];
- gpr2 = cpu_gpr[rd+1];
+ tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_BEQ | MO_ALIGN_16);
+ tcg_gen_mov_tl(cpu_reserve, EA);
+ gen_addr_add(ctx, EA, EA, 8);
+ tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_BEQ);
}
- tcg_gen_qemu_ld_i64(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q));
- tcg_gen_mov_tl(cpu_reserve, EA);
- gen_addr_add(ctx, EA, EA, 8);
- tcg_gen_qemu_ld_i64(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q));
-
- tcg_gen_st_tl(gpr1, cpu_env, offsetof(CPUPPCState, reserve_val));
- tcg_gen_st_tl(gpr2, cpu_env, offsetof(CPUPPCState, reserve_val2));
tcg_temp_free(EA);
+
+ tcg_gen_st_tl(hi, cpu_env, offsetof(CPUPPCState, reserve_val));
+ tcg_gen_st_tl(lo, cpu_env, offsetof(CPUPPCState, reserve_val2));
}
/* stqcx. */
static void gen_stqcx_(DisasContext *ctx)
{
- TCGv EA;
- int reg = rS(ctx->opcode);
- int len = 16;
-#if !defined(CONFIG_USER_ONLY)
- TCGLabel *l1;
- TCGv gpr1, gpr2;
-#endif
+ int rs = rS(ctx->opcode);
+ TCGv EA, hi, lo;
- if (unlikely((rD(ctx->opcode) & 1))) {
+ if (unlikely(rs & 1)) {
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
return;
}
+
gen_set_access_type(ctx, ACCESS_RES);
- EA = tcg_temp_local_new();
+ EA = tcg_temp_new();
gen_addr_reg_index(ctx, EA);
- if (len > 1) {
- gen_check_align(ctx, EA, (len) - 1);
- }
-#if defined(CONFIG_USER_ONLY)
- gen_conditional_store(ctx, EA, reg, 16);
-#else
- tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
- l1 = gen_new_label();
- tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1);
- tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
+ /* Note that the low part is always in RS+1, even in LE mode. */
+ lo = cpu_gpr[rs + 1];
+ hi = cpu_gpr[rs];
- if (unlikely(ctx->le_mode)) {
- gpr1 = cpu_gpr[reg + 1];
- gpr2 = cpu_gpr[reg];
+ if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
+ TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16);
+#ifdef CONFIG_ATOMIC128
+ if (ctx->le_mode) {
+ gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi);
+ } else {
+ gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi);
+ }
+#else
+ /* Restart with exclusive lock. */
+ gen_helper_exit_atomic(cpu_env);
+ ctx->base.is_jmp = DISAS_NORETURN;
+#endif
+ tcg_temp_free(EA);
+ tcg_temp_free_i32(oi);
} else {
- gpr1 = cpu_gpr[reg];
- gpr2 = cpu_gpr[reg + 1];
- }
- tcg_gen_qemu_st_tl(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q));
- gen_addr_add(ctx, EA, EA, 8);
- tcg_gen_qemu_st_tl(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q));
+ TCGLabel *lab_fail = gen_new_label();
+ TCGLabel *lab_over = gen_new_label();
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
- gen_set_label(l1);
- tcg_gen_movi_tl(cpu_reserve, -1);
-#endif
- tcg_temp_free(EA);
-}
+ tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lab_fail);
+ tcg_temp_free(EA);
+ gen_qemu_ld64_i64(ctx, t0, cpu_reserve);
+ tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode
+ ? offsetof(CPUPPCState, reserve_val2)
+ : offsetof(CPUPPCState, reserve_val)));
+ tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail);
+
+ tcg_gen_addi_i64(t0, cpu_reserve, 8);
+ gen_qemu_ld64_i64(ctx, t0, t0);
+ tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode
+ ? offsetof(CPUPPCState, reserve_val)
+ : offsetof(CPUPPCState, reserve_val2)));
+ tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail);
+
+ /* Success */
+ gen_qemu_st64_i64(ctx, ctx->le_mode ? lo : hi, cpu_reserve);
+ tcg_gen_addi_i64(t0, cpu_reserve, 8);
+ gen_qemu_st64_i64(ctx, ctx->le_mode ? hi : lo, t0);
+
+ tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
+ tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ);
+ tcg_gen_br(lab_over);
+
+ gen_set_label(lab_fail);
+ tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so);
+
+ gen_set_label(lab_over);
+ tcg_gen_movi_tl(cpu_reserve, -1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+ }
+}
#endif /* defined(TARGET_PPC64) */
/* sync */
@@ -4636,8 +4804,8 @@ static void gen_eciwx(DisasContext *ctx)
gen_set_access_type(ctx, ACCESS_EXT);
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
- gen_check_align(ctx, t0, 0x03);
- gen_qemu_ld32u(ctx, cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_gen_qemu_ld_tl(cpu_gpr[rD(ctx->opcode)], t0, ctx->mem_idx,
+ DEF_MEMOP(MO_UL | MO_ALIGN));
tcg_temp_free(t0);
}
@@ -4649,8 +4817,8 @@ static void gen_ecowx(DisasContext *ctx)
gen_set_access_type(ctx, ACCESS_EXT);
t0 = tcg_temp_new();
gen_addr_reg_index(ctx, t0);
- gen_check_align(ctx, t0, 0x03);
- gen_qemu_st32(ctx, cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_gen_qemu_st_tl(cpu_gpr[rD(ctx->opcode)], t0, ctx->mem_idx,
+ DEF_MEMOP(MO_UL | MO_ALIGN));
tcg_temp_free(t0);
}
@@ -6886,7 +7054,7 @@ GEN_HANDLER(stop##u, opc, 0xFF, 0xFF, 0x00000000, type),
#define GEN_STUX(name, stop, opc2, opc3, type) \
GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type),
#define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \
-GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2),
+GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000000, type, type2),
#define GEN_STS(name, stop, op, type) \
GEN_ST(name, stop, op | 0x20, type) \
GEN_STU(name, stop, op | 0x21, type) \
@@ -7314,6 +7482,7 @@ static bool ppc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
DisasContext *ctx = container_of(dcbase, DisasContext, base);
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 cleared -- thus we increment the PC here so that
diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c
index 76d6f3f..7813b1b 100644
--- a/target/ppc/translate_init.inc.c
+++ b/target/ppc/translate_init.inc.c
@@ -10457,6 +10457,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data)
cc->set_pc = ppc_cpu_set_pc;
cc->gdb_read_register = ppc_cpu_gdb_read_register;
cc->gdb_write_register = ppc_cpu_gdb_write_register;
+ cc->do_unaligned_access = ppc_cpu_do_unaligned_access;
#ifdef CONFIG_USER_ONLY
cc->handle_mmu_fault = ppc_cpu_handle_mmu_fault;
#else