aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--MAINTAINERS4
-rw-r--r--Makefile2
-rw-r--r--async.c29
-rw-r--r--backends/baum.c315
-rw-r--r--block.c6
-rw-r--r--block/backup.c17
-rw-r--r--block/block-backend.c30
-rw-r--r--block/commit.c2
-rw-r--r--block/io.c137
-rw-r--r--block/mirror.c70
-rw-r--r--block/nbd.c1
-rw-r--r--block/nfs.c55
-rw-r--r--block/qed-table.c16
-rw-r--r--block/qed.c16
-rw-r--r--block/replication.c27
-rw-r--r--block/sheepdog.c67
-rw-r--r--blockdev-nbd.c3
-rw-r--r--blockjob.c37
-rwxr-xr-xconfigure30
-rw-r--r--default-configs/ppc64-softmmu.mak1
-rw-r--r--docs/multiple-iothreads.txt40
-rw-r--r--docs/specs/ppc-spapr-hotplug.txt55
-rw-r--r--hw/input/adb.c26
-rw-r--r--hw/intc/xics.c99
-rw-r--r--hw/intc/xics_kvm.c13
-rw-r--r--hw/intc/xics_spapr.c53
-rw-r--r--hw/nvram/Makefile.objs1
-rw-r--r--hw/nvram/chrp_nvram.c85
-rw-r--r--hw/nvram/mac_nvram.c49
-rw-r--r--hw/nvram/spapr_nvram.c6
-rw-r--r--hw/ppc/Makefile.objs4
-rw-r--r--hw/ppc/pnv.c819
-rw-r--r--hw/ppc/pnv_core.c232
-rw-r--r--hw/ppc/pnv_lpc.c471
-rw-r--r--hw/ppc/pnv_xscom.c275
-rw-r--r--hw/ppc/spapr.c791
-rw-r--r--hw/ppc/spapr_cpu_core.c2
-rw-r--r--hw/ppc/spapr_drc.c17
-rw-r--r--hw/ppc/spapr_events.c279
-rw-r--r--hw/ppc/spapr_hcall.c70
-rw-r--r--hw/ppc/spapr_ovec.c242
-rw-r--r--hw/ppc/spapr_pci.c8
-rw-r--r--hw/ppc/spapr_rtas.c91
-rw-r--r--hw/ppc/spapr_vio.c40
-rw-r--r--hw/scsi/virtio-scsi-dataplane.c4
-rw-r--r--hw/sparc/sun4m.c35
-rw-r--r--hw/sparc64/sun4u.c35
-rw-r--r--include/block/aio.h24
-rw-r--r--include/block/block.h31
-rw-r--r--include/block/block_int.h27
-rw-r--r--include/block/blockjob.h7
-rw-r--r--include/glib-compat.h11
-rw-r--r--include/hw/nvram/chrp_nvram.h54
-rw-r--r--include/hw/nvram/sun_nvram.h (renamed from include/hw/nvram/openbios_firmware_abi.h)47
-rw-r--r--include/hw/ppc/pnv.h129
-rw-r--r--include/hw/ppc/pnv_core.h50
-rw-r--r--include/hw/ppc/pnv_lpc.h67
-rw-r--r--include/hw/ppc/pnv_xscom.h78
-rw-r--r--include/hw/ppc/spapr.h27
-rw-r--r--include/hw/ppc/spapr_ovec.h67
-rw-r--r--include/hw/ppc/spapr_vio.h6
-rw-r--r--include/hw/ppc/xics.h11
-rw-r--r--include/io/channel.h29
-rw-r--r--include/qemu/rfifolock.h54
-rw-r--r--include/qemu/thread-posix.h6
-rw-r--r--include/qemu/thread-win32.h10
-rw-r--r--include/qemu/thread.h3
-rw-r--r--io/channel-socket.c18
-rw-r--r--io/channel-tls.c4
-rw-r--r--io/channel-websock.c4
-rw-r--r--io/channel.c35
-rw-r--r--iothread.c33
-rw-r--r--main-loop.c2
-rw-r--r--migration/exec.c2
-rw-r--r--migration/fd.c2
-rw-r--r--migration/migration.c1
-rw-r--r--migration/savevm.c3
-rw-r--r--migration/socket.c5
-rw-r--r--migration/tls.c2
-rw-r--r--nbd/client.c1
-rw-r--r--nbd/server.c1
-rw-r--r--net/colo-compare.c21
-rw-r--r--net/trace-events3
-rw-r--r--pc-bios/README7
-rw-r--r--pc-bios/skiboot.lidbin0 -> 983893 bytes
-rw-r--r--pc-bios/slof.binbin923832 -> 898232 bytes
-rw-r--r--qemu-char.c77
-rw-r--r--qemu-img.c6
-rw-r--r--qemu-io-cmds.c6
-rw-r--r--roms/Makefile8
m---------roms/SLOF0
m---------roms/skiboot0
-rw-r--r--stubs/Makefile.objs1
-rw-r--r--stubs/iothread.c8
-rw-r--r--target-m68k/cpu.h6
-rw-r--r--target-m68k/helper.c65
-rw-r--r--target-m68k/helper.h2
-rw-r--r--target-m68k/translate.c679
-rw-r--r--target-ppc/excp_helper.c34
-rw-r--r--target-ppc/fpu_helper.c71
-rw-r--r--target-ppc/helper.h8
-rw-r--r--target-ppc/int_helper.c12
-rw-r--r--target-ppc/translate.c34
-rw-r--r--target-ppc/translate/vmx-impl.inc.c74
-rw-r--r--target-ppc/translate/vmx-ops.inc.c10
-rw-r--r--target-ppc/translate/vsx-impl.inc.c83
-rw-r--r--target-ppc/translate/vsx-ops.inc.c14
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.include5
-rw-r--r--tests/ahci-test.c13
-rw-r--r--tests/e1000e-test.c7
-rw-r--r--tests/ide-test.c177
-rw-r--r--tests/ivshmem-test.c47
-rw-r--r--tests/libqos/ahci.c4
-rw-r--r--tests/libqos/ahci.h7
-rw-r--r--tests/libqos/libqos.c2
-rw-r--r--tests/libqos/pci-pc.c187
-rw-r--r--tests/libqos/pci-spapr.c194
-rw-r--r--tests/libqos/pci.c194
-rw-r--r--tests/libqos/pci.h66
-rw-r--r--tests/libqos/usb.c6
-rw-r--r--tests/libqos/usb.h2
-rw-r--r--tests/libqos/virtio-mmio.c17
-rw-r--r--tests/libqos/virtio-pci.c146
-rw-r--r--tests/libqos/virtio-pci.h2
-rw-r--r--tests/libqos/virtio.c78
-rw-r--r--tests/libqos/virtio.h57
-rw-r--r--tests/libqtest.h10
-rw-r--r--tests/postcopy-test.c8
-rw-r--r--tests/prom-env-test.c20
-rw-r--r--tests/rtas-test.c1
-rw-r--r--tests/rtl8139-test.c10
-rw-r--r--tests/tco-test.c80
-rw-r--r--tests/test-aio.c22
-rw-r--r--tests/test-io-channel-socket.c33
-rw-r--r--tests/test-rfifolock.c91
-rw-r--r--tests/usb-hcd-ehci-test.c5
-rw-r--r--tests/vhost-user-test.c33
-rw-r--r--tests/virtio-9p-test.c73
-rw-r--r--tests/virtio-blk-test.c342
-rw-r--r--tests/virtio-net-test.c106
-rw-r--r--tests/virtio-rng-test.c7
-rw-r--r--tests/virtio-scsi-test.c93
-rw-r--r--ui/curses.c4
-rw-r--r--ui/gtk.c27
-rw-r--r--ui/vnc-auth-vencrypt.c1
-rw-r--r--ui/vnc-ws.c3
-rw-r--r--ui/vnc.c7
-rw-r--r--util/Makefile.objs1
-rw-r--r--util/qemu-thread-posix.c14
-rw-r--r--util/qemu-thread-win32.c25
-rw-r--r--util/rfifolock.c78
153 files changed, 6460 insertions, 2636 deletions
diff --git a/.gitmodules b/.gitmodules
index 9da9ede261..ca323b4d87 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -31,3 +31,6 @@
[submodule "roms/u-boot"]
path = roms/u-boot
url = git://git.qemu-project.org/u-boot.git
+[submodule "roms/skiboot"]
+ path = roms/skiboot
+ url = git://git.qemu.org/skiboot.git
diff --git a/MAINTAINERS b/MAINTAINERS
index b01fec0a7b..82d4d0083b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -133,7 +133,8 @@ F: include/hw/lm32/
F: tests/tcg/lm32/
M68K
-S: Orphan
+M: Laurent Vivier <laurent@vivier.eu>
+S: Maintained
F: target-m68k/
F: hw/m68k/
@@ -656,6 +657,7 @@ F: include/hw/*/xics*
F: pc-bios/spapr-rtas/*
F: pc-bios/spapr-rtas.bin
F: pc-bios/slof.bin
+F: pc-bios/skiboot.lid
F: docs/specs/ppc-spapr-hcalls.txt
F: docs/specs/ppc-spapr-hotplug.txt
F: tests/spapr*
diff --git a/Makefile b/Makefile
index 3bcb0565b6..11f5154c81 100644
--- a/Makefile
+++ b/Makefile
@@ -421,7 +421,7 @@ qemu-icon.bmp qemu_logo_no_text.svg \
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \
s390-ccw.img \
-spapr-rtas.bin slof.bin \
+spapr-rtas.bin slof.bin skiboot.lid \
palcode-clipper \
u-boot.e500
else
diff --git a/async.c b/async.c
index f30d011ebc..b2de360c23 100644
--- a/async.c
+++ b/async.c
@@ -61,6 +61,7 @@ void aio_bh_schedule_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
smp_wmb();
ctx->first_bh = bh;
qemu_mutex_unlock(&ctx->bh_lock);
+ aio_notify(ctx);
}
QEMUBH *aio_bh_new(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
@@ -106,8 +107,8 @@ int aio_bh_poll(AioContext *ctx)
* aio_notify again if necessary.
*/
if (atomic_xchg(&bh->scheduled, 0)) {
- /* Idle BHs and the notify BH don't count as progress */
- if (!bh->idle && bh != ctx->notify_dummy_bh) {
+ /* Idle BHs don't count as progress */
+ if (!bh->idle) {
ret = 1;
}
bh->idle = 0;
@@ -259,7 +260,6 @@ aio_ctx_finalize(GSource *source)
{
AioContext *ctx = (AioContext *) source;
- qemu_bh_delete(ctx->notify_dummy_bh);
thread_pool_free(ctx->thread_pool);
#ifdef CONFIG_LINUX_AIO
@@ -284,7 +284,7 @@ aio_ctx_finalize(GSource *source)
aio_set_event_notifier(ctx, &ctx->notifier, false, NULL);
event_notifier_cleanup(&ctx->notifier);
- rfifolock_destroy(&ctx->lock);
+ qemu_rec_mutex_destroy(&ctx->lock);
qemu_mutex_destroy(&ctx->bh_lock);
timerlistgroup_deinit(&ctx->tlg);
}
@@ -345,19 +345,6 @@ static void aio_timerlist_notify(void *opaque)
aio_notify(opaque);
}
-static void aio_rfifolock_cb(void *opaque)
-{
- AioContext *ctx = opaque;
-
- /* Kick owner thread in case they are blocked in aio_poll() */
- qemu_bh_schedule(ctx->notify_dummy_bh);
-}
-
-static void notify_dummy_bh(void *opaque)
-{
- /* Do nothing, we were invoked just to force the event loop to iterate */
-}
-
static void event_notifier_dummy_cb(EventNotifier *e)
{
}
@@ -385,11 +372,9 @@ AioContext *aio_context_new(Error **errp)
#endif
ctx->thread_pool = NULL;
qemu_mutex_init(&ctx->bh_lock);
- rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx);
+ qemu_rec_mutex_init(&ctx->lock);
timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx);
- ctx->notify_dummy_bh = aio_bh_new(ctx, notify_dummy_bh, NULL);
-
return ctx;
fail:
g_source_destroy(&ctx->source);
@@ -408,10 +393,10 @@ void aio_context_unref(AioContext *ctx)
void aio_context_acquire(AioContext *ctx)
{
- rfifolock_lock(&ctx->lock);
+ qemu_rec_mutex_lock(&ctx->lock);
}
void aio_context_release(AioContext *ctx)
{
- rfifolock_unlock(&ctx->lock);
+ qemu_rec_mutex_unlock(&ctx->lock);
}
diff --git a/backends/baum.c b/backends/baum.c
index 919844e7f0..b92369d840 100644
--- a/backends/baum.c
+++ b/backends/baum.c
@@ -1,7 +1,7 @@
/*
* QEMU Baum Braille Device
*
- * Copyright (c) 2008 Samuel Thibault
+ * Copyright (c) 2008, 2010-2011, 2016 Samuel Thibault
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -92,6 +92,7 @@ typedef struct {
brlapi_handle_t *brlapi;
int brlapi_fd;
unsigned int x, y;
+ bool deferred_init;
uint8_t in_buf[BUF_SIZE];
uint8_t in_buf_used;
@@ -102,8 +103,11 @@ typedef struct {
} BaumDriverState;
/* Let's assume NABCC by default */
-static const uint8_t nabcc_translation[256] = {
- [0] = ' ',
+enum way {
+ DOTS2ASCII,
+ ASCII2DOTS
+};
+static const uint8_t nabcc_translation[2][256] = {
#ifndef BRLAPI_DOTS
#define BRLAPI_DOTS(d1,d2,d3,d4,d5,d6,d7,d8) \
((d1?BRLAPI_DOT1:0)|\
@@ -115,107 +119,154 @@ static const uint8_t nabcc_translation[256] = {
(d7?BRLAPI_DOT7:0)|\
(d8?BRLAPI_DOT8:0))
#endif
- [BRLAPI_DOTS(1,0,0,0,0,0,0,0)] = 'a',
- [BRLAPI_DOTS(1,1,0,0,0,0,0,0)] = 'b',
- [BRLAPI_DOTS(1,0,0,1,0,0,0,0)] = 'c',
- [BRLAPI_DOTS(1,0,0,1,1,0,0,0)] = 'd',
- [BRLAPI_DOTS(1,0,0,0,1,0,0,0)] = 'e',
- [BRLAPI_DOTS(1,1,0,1,0,0,0,0)] = 'f',
- [BRLAPI_DOTS(1,1,0,1,1,0,0,0)] = 'g',
- [BRLAPI_DOTS(1,1,0,0,1,0,0,0)] = 'h',
- [BRLAPI_DOTS(0,1,0,1,0,0,0,0)] = 'i',
- [BRLAPI_DOTS(0,1,0,1,1,0,0,0)] = 'j',
- [BRLAPI_DOTS(1,0,1,0,0,0,0,0)] = 'k',
- [BRLAPI_DOTS(1,1,1,0,0,0,0,0)] = 'l',
- [BRLAPI_DOTS(1,0,1,1,0,0,0,0)] = 'm',
- [BRLAPI_DOTS(1,0,1,1,1,0,0,0)] = 'n',
- [BRLAPI_DOTS(1,0,1,0,1,0,0,0)] = 'o',
- [BRLAPI_DOTS(1,1,1,1,0,0,0,0)] = 'p',
- [BRLAPI_DOTS(1,1,1,1,1,0,0,0)] = 'q',
- [BRLAPI_DOTS(1,1,1,0,1,0,0,0)] = 'r',
- [BRLAPI_DOTS(0,1,1,1,0,0,0,0)] = 's',
- [BRLAPI_DOTS(0,1,1,1,1,0,0,0)] = 't',
- [BRLAPI_DOTS(1,0,1,0,0,1,0,0)] = 'u',
- [BRLAPI_DOTS(1,1,1,0,0,1,0,0)] = 'v',
- [BRLAPI_DOTS(0,1,0,1,1,1,0,0)] = 'w',
- [BRLAPI_DOTS(1,0,1,1,0,1,0,0)] = 'x',
- [BRLAPI_DOTS(1,0,1,1,1,1,0,0)] = 'y',
- [BRLAPI_DOTS(1,0,1,0,1,1,0,0)] = 'z',
-
- [BRLAPI_DOTS(1,0,0,0,0,0,1,0)] = 'A',
- [BRLAPI_DOTS(1,1,0,0,0,0,1,0)] = 'B',
- [BRLAPI_DOTS(1,0,0,1,0,0,1,0)] = 'C',
- [BRLAPI_DOTS(1,0,0,1,1,0,1,0)] = 'D',
- [BRLAPI_DOTS(1,0,0,0,1,0,1,0)] = 'E',
- [BRLAPI_DOTS(1,1,0,1,0,0,1,0)] = 'F',
- [BRLAPI_DOTS(1,1,0,1,1,0,1,0)] = 'G',
- [BRLAPI_DOTS(1,1,0,0,1,0,1,0)] = 'H',
- [BRLAPI_DOTS(0,1,0,1,0,0,1,0)] = 'I',
- [BRLAPI_DOTS(0,1,0,1,1,0,1,0)] = 'J',
- [BRLAPI_DOTS(1,0,1,0,0,0,1,0)] = 'K',
- [BRLAPI_DOTS(1,1,1,0,0,0,1,0)] = 'L',
- [BRLAPI_DOTS(1,0,1,1,0,0,1,0)] = 'M',
- [BRLAPI_DOTS(1,0,1,1,1,0,1,0)] = 'N',
- [BRLAPI_DOTS(1,0,1,0,1,0,1,0)] = 'O',
- [BRLAPI_DOTS(1,1,1,1,0,0,1,0)] = 'P',
- [BRLAPI_DOTS(1,1,1,1,1,0,1,0)] = 'Q',
- [BRLAPI_DOTS(1,1,1,0,1,0,1,0)] = 'R',
- [BRLAPI_DOTS(0,1,1,1,0,0,1,0)] = 'S',
- [BRLAPI_DOTS(0,1,1,1,1,0,1,0)] = 'T',
- [BRLAPI_DOTS(1,0,1,0,0,1,1,0)] = 'U',
- [BRLAPI_DOTS(1,1,1,0,0,1,1,0)] = 'V',
- [BRLAPI_DOTS(0,1,0,1,1,1,1,0)] = 'W',
- [BRLAPI_DOTS(1,0,1,1,0,1,1,0)] = 'X',
- [BRLAPI_DOTS(1,0,1,1,1,1,1,0)] = 'Y',
- [BRLAPI_DOTS(1,0,1,0,1,1,1,0)] = 'Z',
-
- [BRLAPI_DOTS(0,0,1,0,1,1,0,0)] = '0',
- [BRLAPI_DOTS(0,1,0,0,0,0,0,0)] = '1',
- [BRLAPI_DOTS(0,1,1,0,0,0,0,0)] = '2',
- [BRLAPI_DOTS(0,1,0,0,1,0,0,0)] = '3',
- [BRLAPI_DOTS(0,1,0,0,1,1,0,0)] = '4',
- [BRLAPI_DOTS(0,1,0,0,0,1,0,0)] = '5',
- [BRLAPI_DOTS(0,1,1,0,1,0,0,0)] = '6',
- [BRLAPI_DOTS(0,1,1,0,1,1,0,0)] = '7',
- [BRLAPI_DOTS(0,1,1,0,0,1,0,0)] = '8',
- [BRLAPI_DOTS(0,0,1,0,1,0,0,0)] = '9',
-
- [BRLAPI_DOTS(0,0,0,1,0,1,0,0)] = '.',
- [BRLAPI_DOTS(0,0,1,1,0,1,0,0)] = '+',
- [BRLAPI_DOTS(0,0,1,0,0,1,0,0)] = '-',
- [BRLAPI_DOTS(1,0,0,0,0,1,0,0)] = '*',
- [BRLAPI_DOTS(0,0,1,1,0,0,0,0)] = '/',
- [BRLAPI_DOTS(1,1,1,0,1,1,0,0)] = '(',
- [BRLAPI_DOTS(0,1,1,1,1,1,0,0)] = ')',
-
- [BRLAPI_DOTS(1,1,1,1,0,1,0,0)] = '&',
- [BRLAPI_DOTS(0,0,1,1,1,1,0,0)] = '#',
-
- [BRLAPI_DOTS(0,0,0,0,0,1,0,0)] = ',',
- [BRLAPI_DOTS(0,0,0,0,1,1,0,0)] = ';',
- [BRLAPI_DOTS(1,0,0,0,1,1,0,0)] = ':',
- [BRLAPI_DOTS(0,1,1,1,0,1,0,0)] = '!',
- [BRLAPI_DOTS(1,0,0,1,1,1,0,0)] = '?',
- [BRLAPI_DOTS(0,0,0,0,1,0,0,0)] = '"',
- [BRLAPI_DOTS(0,0,1,0,0,0,0,0)] ='\'',
- [BRLAPI_DOTS(0,0,0,1,0,0,0,0)] = '`',
- [BRLAPI_DOTS(0,0,0,1,1,0,1,0)] = '^',
- [BRLAPI_DOTS(0,0,0,1,1,0,0,0)] = '~',
- [BRLAPI_DOTS(0,1,0,1,0,1,1,0)] = '[',
- [BRLAPI_DOTS(1,1,0,1,1,1,1,0)] = ']',
- [BRLAPI_DOTS(0,1,0,1,0,1,0,0)] = '{',
- [BRLAPI_DOTS(1,1,0,1,1,1,0,0)] = '}',
- [BRLAPI_DOTS(1,1,1,1,1,1,0,0)] = '=',
- [BRLAPI_DOTS(1,1,0,0,0,1,0,0)] = '<',
- [BRLAPI_DOTS(0,0,1,1,1,0,0,0)] = '>',
- [BRLAPI_DOTS(1,1,0,1,0,1,0,0)] = '$',
- [BRLAPI_DOTS(1,0,0,1,0,1,0,0)] = '%',
- [BRLAPI_DOTS(0,0,0,1,0,0,1,0)] = '@',
- [BRLAPI_DOTS(1,1,0,0,1,1,0,0)] = '|',
- [BRLAPI_DOTS(1,1,0,0,1,1,1,0)] ='\\',
- [BRLAPI_DOTS(0,0,0,1,1,1,0,0)] = '_',
+#define DO(dots, ascii) \
+ [DOTS2ASCII][dots] = ascii, \
+ [ASCII2DOTS][ascii] = dots
+ DO(0, ' '),
+ DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 0, 0), 'a'),
+ DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 0, 0), 'b'),
+ DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 0, 0), 'c'),
+ DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 0, 0), 'd'),
+ DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 0, 0), 'e'),
+ DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 0, 0), 'f'),
+ DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 0, 0), 'g'),
+ DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 0, 0), 'h'),
+ DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 0, 0), 'i'),
+ DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 0, 0), 'j'),
+ DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 0, 0), 'k'),
+ DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 0, 0), 'l'),
+ DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 0, 0), 'm'),
+ DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 0, 0), 'n'),
+ DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 0, 0), 'o'),
+ DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 0, 0), 'p'),
+ DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 0, 0), 'q'),
+ DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 0, 0), 'r'),
+ DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 0, 0), 's'),
+ DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 0, 0), 't'),
+ DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 0, 0), 'u'),
+ DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 0, 0), 'v'),
+ DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 0, 0), 'w'),
+ DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 0, 0), 'x'),
+ DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 0, 0), 'y'),
+ DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 0, 0), 'z'),
+
+ DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 0, 1, 0), 'A'),
+ DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 0, 1, 0), 'B'),
+ DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 0, 1, 0), 'C'),
+ DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 0, 1, 0), 'D'),
+ DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 0, 1, 0), 'E'),
+ DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 0, 1, 0), 'F'),
+ DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 0, 1, 0), 'G'),
+ DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 0, 1, 0), 'H'),
+ DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 0, 1, 0), 'I'),
+ DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 0, 1, 0), 'J'),
+ DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 0, 1, 0), 'K'),
+ DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 0, 1, 0), 'L'),
+ DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 0, 1, 0), 'M'),
+ DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 0, 1, 0), 'N'),
+ DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 0, 1, 0), 'O'),
+ DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 0, 1, 0), 'P'),
+ DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 0, 1, 0), 'Q'),
+ DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 0, 1, 0), 'R'),
+ DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 0, 1, 0), 'S'),
+ DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 0, 1, 0), 'T'),
+ DO(BRLAPI_DOTS(1, 0, 1, 0, 0, 1, 1, 0), 'U'),
+ DO(BRLAPI_DOTS(1, 1, 1, 0, 0, 1, 1, 0), 'V'),
+ DO(BRLAPI_DOTS(0, 1, 0, 1, 1, 1, 1, 0), 'W'),
+ DO(BRLAPI_DOTS(1, 0, 1, 1, 0, 1, 1, 0), 'X'),
+ DO(BRLAPI_DOTS(1, 0, 1, 1, 1, 1, 1, 0), 'Y'),
+ DO(BRLAPI_DOTS(1, 0, 1, 0, 1, 1, 1, 0), 'Z'),
+
+ DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 1, 0, 0), '0'),
+ DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 0, 0, 0), '1'),
+ DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 0, 0, 0), '2'),
+ DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 0, 0, 0), '3'),
+ DO(BRLAPI_DOTS(0, 1, 0, 0, 1, 1, 0, 0), '4'),
+ DO(BRLAPI_DOTS(0, 1, 0, 0, 0, 1, 0, 0), '5'),
+ DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 0, 0, 0), '6'),
+ DO(BRLAPI_DOTS(0, 1, 1, 0, 1, 1, 0, 0), '7'),
+ DO(BRLAPI_DOTS(0, 1, 1, 0, 0, 1, 0, 0), '8'),
+ DO(BRLAPI_DOTS(0, 0, 1, 0, 1, 0, 0, 0), '9'),
+
+ DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 1, 0, 0), '.'),
+ DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 1, 0, 0), '+'),
+ DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 1, 0, 0), '-'),
+ DO(BRLAPI_DOTS(1, 0, 0, 0, 0, 1, 0, 0), '*'),
+ DO(BRLAPI_DOTS(0, 0, 1, 1, 0, 0, 0, 0), '/'),
+ DO(BRLAPI_DOTS(1, 1, 1, 0, 1, 1, 0, 0), '('),
+ DO(BRLAPI_DOTS(0, 1, 1, 1, 1, 1, 0, 0), ')'),
+
+ DO(BRLAPI_DOTS(1, 1, 1, 1, 0, 1, 0, 0), '&'),
+ DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 1, 0, 0), '#'),
+
+ DO(BRLAPI_DOTS(0, 0, 0, 0, 0, 1, 0, 0), ','),
+ DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 1, 0, 0), ';'),
+ DO(BRLAPI_DOTS(1, 0, 0, 0, 1, 1, 0, 0), ':'),
+ DO(BRLAPI_DOTS(0, 1, 1, 1, 0, 1, 0, 0), '!'),
+ DO(BRLAPI_DOTS(1, 0, 0, 1, 1, 1, 0, 0), '?'),
+ DO(BRLAPI_DOTS(0, 0, 0, 0, 1, 0, 0, 0), '"'),
+ DO(BRLAPI_DOTS(0, 0, 1, 0, 0, 0, 0, 0), '\''),
+ DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 0, 0), '`'),
+ DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 1, 0), '^'),
+ DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 0, 0, 0), '~'),
+ DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 1, 0), '['),
+ DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 1, 0), ']'),
+ DO(BRLAPI_DOTS(0, 1, 0, 1, 0, 1, 0, 0), '{'),
+ DO(BRLAPI_DOTS(1, 1, 0, 1, 1, 1, 0, 0), '}'),
+ DO(BRLAPI_DOTS(1, 1, 1, 1, 1, 1, 0, 0), '='),
+ DO(BRLAPI_DOTS(1, 1, 0, 0, 0, 1, 0, 0), '<'),
+ DO(BRLAPI_DOTS(0, 0, 1, 1, 1, 0, 0, 0), '>'),
+ DO(BRLAPI_DOTS(1, 1, 0, 1, 0, 1, 0, 0), '$'),
+ DO(BRLAPI_DOTS(1, 0, 0, 1, 0, 1, 0, 0), '%'),
+ DO(BRLAPI_DOTS(0, 0, 0, 1, 0, 0, 1, 0), '@'),
+ DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 0, 0), '|'),
+ DO(BRLAPI_DOTS(1, 1, 0, 0, 1, 1, 1, 0), '\\'),
+ DO(BRLAPI_DOTS(0, 0, 0, 1, 1, 1, 0, 0), '_'),
};
+/* The guest OS has started discussing with us, finish initializing BrlAPI */
+static int baum_deferred_init(BaumDriverState *baum)
+{
+#if defined(CONFIG_SDL)
+#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
+ SDL_SysWMinfo info;
+#endif
+#endif
+ int tty;
+
+ if (baum->deferred_init) {
+ return 1;
+ }
+
+ if (brlapi__getDisplaySize(baum->brlapi, &baum->x, &baum->y) == -1) {
+ brlapi_perror("baum: brlapi__getDisplaySize");
+ return 0;
+ }
+
+#if defined(CONFIG_SDL)
+#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
+ memset(&info, 0, sizeof(info));
+ SDL_VERSION(&info.version);
+ if (SDL_GetWMInfo(&info)) {
+ tty = info.info.x11.wmwindow;
+ } else {
+#endif
+#endif
+ tty = BRLAPI_TTY_DEFAULT;
+#if defined(CONFIG_SDL)
+#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
+ }
+#endif
+#endif
+
+ if (brlapi__enterTtyMode(baum->brlapi, tty, NULL) == -1) {
+ brlapi_perror("baum: brlapi__enterTtyMode");
+ return 0;
+ }
+ baum->deferred_init = 1;
+ return 1;
+}
+
/* The serial port can receive more of our data */
static void baum_accept_input(struct CharDriverState *chr)
{
@@ -346,8 +397,10 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
cursor = i + 1;
c &= ~(BRLAPI_DOT7|BRLAPI_DOT8);
}
- if (!(c = nabcc_translation[c]))
+ c = nabcc_translation[DOTS2ASCII][c];
+ if (!c) {
c = '?';
+ }
text[i] = c;
}
timer_del(baum->cellCount_timer);
@@ -440,6 +493,8 @@ static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
return 0;
if (!baum->brlapi)
return len;
+ if (!baum_deferred_init(baum))
+ return len;
while (len) {
/* Complete our buffer as much as possible */
@@ -476,6 +531,13 @@ static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) {
baum_write_packet(baum, packet, sizeof(packet));
}
+static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value,
+ uint8_t value2) {
+ uint8_t packet[] = { type, value, value2 };
+ DPRINTF("writing key %x %x\n", type, value);
+ baum_write_packet(baum, packet, sizeof(packet));
+}
+
/* We got some data on the BrlAPI socket */
static void baum_chr_read(void *opaque)
{
@@ -484,6 +546,8 @@ static void baum_chr_read(void *opaque)
int ret;
if (!baum->brlapi)
return;
+ if (!baum_deferred_init(baum))
+ return;
while ((ret = brlapi__readKey(baum->brlapi, 0, &code)) == 1) {
DPRINTF("got key %"BRLAPI_PRIxKEYCODE"\n", code);
/* Emulate */
@@ -540,7 +604,17 @@ static void baum_chr_read(void *opaque)
}
break;
case BRLAPI_KEY_TYPE_SYM:
- break;
+ {
+ brlapi_keyCode_t keysym = code & BRLAPI_KEY_CODE_MASK;
+ if (keysym < 0x100) {
+ uint8_t dots = nabcc_translation[ASCII2DOTS][keysym];
+ if (dots) {
+ baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, dots);
+ baum_send_key2(baum, BAUM_RSP_EntryKeys, 0, 0);
+ }
+ }
+ break;
+ }
}
}
if (ret == -1 && (brlapi_errno != BRLAPI_ERROR_LIBCERR || errno != EINTR)) {
@@ -573,12 +647,6 @@ static CharDriverState *chr_baum_init(const char *id,
BaumDriverState *baum;
CharDriverState *chr;
brlapi_handle_t *handle;
-#if defined(CONFIG_SDL)
-#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
- SDL_SysWMinfo info;
-#endif
-#endif
- int tty;
chr = qemu_chr_alloc(common, errp);
if (!chr) {
@@ -601,39 +669,14 @@ static CharDriverState *chr_baum_init(const char *id,
brlapi_strerror(brlapi_error_location()));
goto fail_handle;
}
+ baum->deferred_init = 0;
baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum);
- if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) {
- error_setg(errp, "brlapi__getDisplaySize: %s",
- brlapi_strerror(brlapi_error_location()));
- goto fail;
- }
-
-#if defined(CONFIG_SDL)
-#if SDL_COMPILEDVERSION < SDL_VERSIONNUM(2, 0, 0)
- memset(&info, 0, sizeof(info));
- SDL_VERSION(&info.version);
- if (SDL_GetWMInfo(&info))
- tty = info.info.x11.wmwindow;
- else
-#endif
-#endif
- tty = BRLAPI_TTY_DEFAULT;
-
- if (brlapi__enterTtyMode(handle, tty, NULL) == -1) {
- error_setg(errp, "brlapi__enterTtyMode: %s",
- brlapi_strerror(brlapi_error_location()));
- goto fail;
- }
-
qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
return chr;
-fail:
- timer_free(baum->cellCount_timer);
- brlapi__closeConnection(handle);
fail_handle:
g_free(handle);
g_free(chr);
diff --git a/block.c b/block.c
index 7f3e7bcdc3..a17baab1d0 100644
--- a/block.c
+++ b/block.c
@@ -2082,7 +2082,7 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
* to all devices.
*
*/
-int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
+int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
{
int ret = -1;
BlockReopenQueueEntry *bs_entry, *next;
@@ -2090,7 +2090,9 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
assert(bs_queue != NULL);
+ aio_context_release(ctx);
bdrv_drain_all();
+ aio_context_acquire(ctx);
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
@@ -2131,7 +2133,7 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
Error *local_err = NULL;
BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
- ret = bdrv_reopen_multiple(queue, &local_err);
+ ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
}
diff --git a/block/backup.c b/block/backup.c
index 02dbe48035..81d4042ae8 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -300,6 +300,21 @@ void backup_cow_request_end(CowRequest *req)
cow_request_end(req);
}
+static void backup_drain(BlockJob *job)
+{
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+
+ /* Need to keep a reference in case blk_drain triggers execution
+ * of backup_complete...
+ */
+ if (s->target) {
+ BlockBackend *target = s->target;
+ blk_ref(target);
+ blk_drain(target);
+ blk_unref(target);
+ }
+}
+
static const BlockJobDriver backup_job_driver = {
.instance_size = sizeof(BackupBlockJob),
.job_type = BLOCK_JOB_TYPE_BACKUP,
@@ -307,6 +322,7 @@ static const BlockJobDriver backup_job_driver = {
.commit = backup_commit,
.abort = backup_abort,
.attached_aio_context = backup_attached_aio_context,
+ .drain = backup_drain,
};
static BlockErrorAction backup_error_action(BackupBlockJob *job,
@@ -331,6 +347,7 @@ static void backup_complete(BlockJob *job, void *opaque)
BackupCompleteData *data = opaque;
blk_unref(s->target);
+ s->target = NULL;
block_job_completed(job, data->ret);
g_free(data);
diff --git a/block/block-backend.c b/block/block-backend.c
index c53ca30000..27a7f6f523 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -799,20 +799,25 @@ int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
BdrvRequestFlags flags)
{
int ret;
+ BlockDriverState *bs = blk_bs(blk);
- trace_blk_co_preadv(blk, blk_bs(blk), offset, bytes, flags);
+ trace_blk_co_preadv(blk, bs, offset, bytes, flags);
ret = blk_check_byte_request(blk, offset, bytes);
if (ret < 0) {
return ret;
}
+ bdrv_inc_in_flight(bs);
+
/* throttling disk I/O */
if (blk->public.throttle_state) {
throttle_group_co_io_limits_intercept(blk, bytes, false);
}
- return bdrv_co_preadv(blk->root, offset, bytes, qiov, flags);
+ ret = bdrv_co_preadv(blk->root, offset, bytes, qiov, flags);
+ bdrv_dec_in_flight(bs);
+ return ret;
}
int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
@@ -820,14 +825,17 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
BdrvRequestFlags flags)
{
int ret;
+ BlockDriverState *bs = blk_bs(blk);
- trace_blk_co_pwritev(blk, blk_bs(blk), offset, bytes, flags);
+ trace_blk_co_pwritev(blk, bs, offset, bytes, flags);
ret = blk_check_byte_request(blk, offset, bytes);
if (ret < 0) {
return ret;
}
+ bdrv_inc_in_flight(bs);
+
/* throttling disk I/O */
if (blk->public.throttle_state) {
throttle_group_co_io_limits_intercept(blk, bytes, true);
@@ -837,7 +845,9 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
flags |= BDRV_REQ_FUA;
}
- return bdrv_co_pwritev(blk->root, offset, bytes, qiov, flags);
+ ret = bdrv_co_pwritev(blk->root, offset, bytes, qiov, flags);
+ bdrv_dec_in_flight(bs);
+ return ret;
}
typedef struct BlkRwCo {
@@ -868,7 +878,6 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
int64_t bytes, CoroutineEntry co_entry,
BdrvRequestFlags flags)
{
- AioContext *aio_context;
QEMUIOVector qiov;
struct iovec iov;
Coroutine *co;
@@ -890,11 +899,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
co = qemu_coroutine_create(co_entry, &rwco);
qemu_coroutine_enter(co);
-
- aio_context = blk_get_aio_context(blk);
- while (rwco.ret == NOT_DONE) {
- aio_poll(aio_context, true);
- }
+ BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE);
return rwco.ret;
}
@@ -930,6 +935,8 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
static void error_callback_bh(void *opaque)
{
struct BlockBackendAIOCB *acb = opaque;
+
+ bdrv_dec_in_flight(acb->common.bs);
acb->common.cb(acb->common.opaque, acb->ret);
qemu_aio_unref(acb);
}
@@ -940,6 +947,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk,
{
struct BlockBackendAIOCB *acb;
+ bdrv_inc_in_flight(blk_bs(blk));
acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque);
acb->blk = blk;
acb->ret = ret;
@@ -962,6 +970,7 @@ static const AIOCBInfo blk_aio_em_aiocb_info = {
static void blk_aio_complete(BlkAioEmAIOCB *acb)
{
if (acb->has_returned) {
+ bdrv_dec_in_flight(acb->common.bs);
acb->common.cb(acb->common.opaque, acb->rwco.ret);
qemu_aio_unref(acb);
}
@@ -983,6 +992,7 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
BlkAioEmAIOCB *acb;
Coroutine *co;
+ bdrv_inc_in_flight(blk_bs(blk));
acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque);
acb->rwco = (BlkRwCo) {
.blk = blk,
diff --git a/block/commit.c b/block/commit.c
index 9f67a8b121..499eccaeee 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -251,7 +251,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
orig_overlay_flags | BDRV_O_RDWR);
}
if (reopen_queue) {
- bdrv_reopen_multiple(reopen_queue, &local_err);
+ bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
block_job_unref(&s->common);
diff --git a/block/io.c b/block/io.c
index 79cbbdf769..be0d862ca6 100644
--- a/block/io.c
+++ b/block/io.c
@@ -143,7 +143,7 @@ bool bdrv_requests_pending(BlockDriverState *bs)
{
BdrvChild *child;
- if (!QLIST_EMPTY(&bs->tracked_requests)) {
+ if (atomic_read(&bs->in_flight)) {
return true;
}
@@ -156,16 +156,22 @@ bool bdrv_requests_pending(BlockDriverState *bs)
return false;
}
-static void bdrv_drain_recurse(BlockDriverState *bs)
+static bool bdrv_drain_recurse(BlockDriverState *bs)
{
BdrvChild *child;
+ bool waited;
+
+ waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
if (bs->drv && bs->drv->bdrv_drain) {
bs->drv->bdrv_drain(bs);
}
+
QLIST_FOREACH(child, &bs->children, next) {
- bdrv_drain_recurse(child->bs);
+ waited |= bdrv_drain_recurse(child->bs);
}
+
+ return waited;
}
typedef struct {
@@ -174,23 +180,14 @@ typedef struct {
bool done;
} BdrvCoDrainData;
-static void bdrv_drain_poll(BlockDriverState *bs)
-{
- bool busy = true;
-
- while (busy) {
- /* Keep iterating */
- busy = bdrv_requests_pending(bs);
- busy |= aio_poll(bdrv_get_aio_context(bs), busy);
- }
-}
-
static void bdrv_co_drain_bh_cb(void *opaque)
{
BdrvCoDrainData *data = opaque;
Coroutine *co = data->co;
+ BlockDriverState *bs = data->bs;
- bdrv_drain_poll(data->bs);
+ bdrv_dec_in_flight(bs);
+ bdrv_drained_begin(bs);
data->done = true;
qemu_coroutine_enter(co);
}
@@ -209,6 +206,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs)
.bs = bs,
.done = false,
};
+ bdrv_inc_in_flight(bs);
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
bdrv_co_drain_bh_cb, &data);
@@ -220,6 +218,11 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs)
void bdrv_drained_begin(BlockDriverState *bs)
{
+ if (qemu_in_coroutine()) {
+ bdrv_co_yield_to_drain(bs);
+ return;
+ }
+
if (!bs->quiesce_counter++) {
aio_disable_external(bdrv_get_aio_context(bs));
bdrv_parent_drained_begin(bs);
@@ -227,11 +230,6 @@ void bdrv_drained_begin(BlockDriverState *bs)
bdrv_io_unplugged_begin(bs);
bdrv_drain_recurse(bs);
- if (qemu_in_coroutine()) {
- bdrv_co_yield_to_drain(bs);
- } else {
- bdrv_drain_poll(bs);
- }
bdrv_io_unplugged_end(bs);
}
@@ -279,7 +277,7 @@ void bdrv_drain(BlockDriverState *bs)
void bdrv_drain_all(void)
{
/* Always run first iteration so any pending completion BHs run */
- bool busy = true;
+ bool waited = true;
BlockDriverState *bs;
BdrvNextIterator it;
BlockJob *job = NULL;
@@ -299,7 +297,6 @@ void bdrv_drain_all(void)
aio_context_acquire(aio_context);
bdrv_parent_drained_begin(bs);
bdrv_io_unplugged_begin(bs);
- bdrv_drain_recurse(bs);
aio_context_release(aio_context);
if (!g_slist_find(aio_ctxs, aio_context)) {
@@ -313,8 +310,8 @@ void bdrv_drain_all(void)
* request completion. Therefore we must keep looping until there was no
* more activity rather than simply draining each device independently.
*/
- while (busy) {
- busy = false;
+ while (waited) {
+ waited = false;
for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
AioContext *aio_context = ctx->data;
@@ -322,13 +319,9 @@ void bdrv_drain_all(void)
aio_context_acquire(aio_context);
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (aio_context == bdrv_get_aio_context(bs)) {
- if (bdrv_requests_pending(bs)) {
- busy = true;
- aio_poll(aio_context, busy);
- }
+ waited |= bdrv_drain_recurse(bs);
}
}
- busy |= aio_poll(aio_context, false);
aio_context_release(aio_context);
}
}
@@ -476,6 +469,28 @@ static bool tracked_request_overlaps(BdrvTrackedRequest *req,
return true;
}
+void bdrv_inc_in_flight(BlockDriverState *bs)
+{
+ atomic_inc(&bs->in_flight);
+}
+
+static void dummy_bh_cb(void *opaque)
+{
+}
+
+void bdrv_wakeup(BlockDriverState *bs)
+{
+ if (bs->wakeup) {
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL);
+ }
+}
+
+void bdrv_dec_in_flight(BlockDriverState *bs)
+{
+ atomic_dec(&bs->in_flight);
+ bdrv_wakeup(bs);
+}
+
static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
{
BlockDriverState *bs = self->bs;
@@ -583,13 +598,9 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset,
/* Fast-path if already in coroutine context */
bdrv_rw_co_entry(&rwco);
} else {
- AioContext *aio_context = bdrv_get_aio_context(child->bs);
-
co = qemu_coroutine_create(bdrv_rw_co_entry, &rwco);
qemu_coroutine_enter(co);
- while (rwco.ret == NOT_DONE) {
- aio_poll(aio_context, true);
- }
+ BDRV_POLL_WHILE(child->bs, rwco.ret == NOT_DONE);
}
return rwco.ret;
}
@@ -1097,6 +1108,8 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
return ret;
}
+ bdrv_inc_in_flight(bs);
+
/* Don't do copy-on-read if we read data before write operation */
if (bs->copy_on_read && !(flags & BDRV_REQ_NO_SERIALISING)) {
flags |= BDRV_REQ_COPY_ON_READ;
@@ -1132,6 +1145,7 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child,
use_local_qiov ? &local_qiov : qiov,
flags);
tracked_request_end(&req);
+ bdrv_dec_in_flight(bs);
if (use_local_qiov) {
qemu_iovec_destroy(&local_qiov);
@@ -1480,6 +1494,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
return ret;
}
+ bdrv_inc_in_flight(bs);
/*
* Align write if necessary by performing a read-modify-write cycle.
* Pad qiov with the read parts and be sure to have a tracked request not
@@ -1581,6 +1596,7 @@ fail:
qemu_vfree(tail_buf);
out:
tracked_request_end(&req);
+ bdrv_dec_in_flight(bs);
return ret;
}
@@ -1705,17 +1721,19 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
}
*file = NULL;
+ bdrv_inc_in_flight(bs);
ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum,
file);
if (ret < 0) {
*pnum = 0;
- return ret;
+ goto out;
}
if (ret & BDRV_BLOCK_RAW) {
assert(ret & BDRV_BLOCK_OFFSET_VALID);
- return bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS,
- *pnum, pnum, file);
+ ret = bdrv_get_block_status(bs->file->bs, ret >> BDRV_SECTOR_BITS,
+ *pnum, pnum, file);
+ goto out;
}
if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) {
@@ -1757,6 +1775,8 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
}
}
+out:
+ bdrv_dec_in_flight(bs);
return ret;
}
@@ -1822,14 +1842,10 @@ int64_t bdrv_get_block_status_above(BlockDriverState *bs,
/* Fast-path if already in coroutine context */
bdrv_get_block_status_above_co_entry(&data);
} else {
- AioContext *aio_context = bdrv_get_aio_context(bs);
-
co = qemu_coroutine_create(bdrv_get_block_status_above_co_entry,
&data);
qemu_coroutine_enter(co);
- while (!data.done) {
- aio_poll(aio_context, true);
- }
+ BDRV_POLL_WHILE(bs, !data.done);
}
return data.ret;
}
@@ -2102,6 +2118,7 @@ static const AIOCBInfo bdrv_em_co_aiocb_info = {
static void bdrv_co_complete(BlockAIOCBCoroutine *acb)
{
if (!acb->need_bh) {
+ bdrv_dec_in_flight(acb->common.bs);
acb->common.cb(acb->common.opaque, acb->req.error);
qemu_aio_unref(acb);
}
@@ -2152,6 +2169,9 @@ static BlockAIOCB *bdrv_co_aio_prw_vector(BdrvChild *child,
Coroutine *co;
BlockAIOCBCoroutine *acb;
+ /* Matched by bdrv_co_complete's bdrv_dec_in_flight. */
+ bdrv_inc_in_flight(child->bs);
+
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, child->bs, cb, opaque);
acb->child = child;
acb->need_bh = true;
@@ -2185,6 +2205,9 @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
Coroutine *co;
BlockAIOCBCoroutine *acb;
+ /* Matched by bdrv_co_complete's bdrv_dec_in_flight. */
+ bdrv_inc_in_flight(bs);
+
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
acb->need_bh = true;
acb->req.error = -EINPROGRESS;
@@ -2244,23 +2267,22 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque)
int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
{
int ret;
- BdrvTrackedRequest req;
if (!bs || !bdrv_is_inserted(bs) || bdrv_is_read_only(bs) ||
bdrv_is_sg(bs)) {
return 0;
}
- tracked_request_begin(&req, bs, 0, 0, BDRV_TRACKED_FLUSH);
+ bdrv_inc_in_flight(bs);
int current_gen = bs->write_gen;
/* Wait until any previous flushes are completed */
- while (bs->active_flush_req != NULL) {
+ while (bs->active_flush_req) {
qemu_co_queue_wait(&bs->flush_queue);
}
- bs->active_flush_req = &req;
+ bs->active_flush_req = true;
/* Write back all layers by calling one driver function */
if (bs->drv->bdrv_co_flush) {
@@ -2330,11 +2352,11 @@ flush_parent:
out:
/* Notify any pending flushes that we have completed */
bs->flushed_gen = current_gen;
- bs->active_flush_req = NULL;
+ bs->active_flush_req = false;
/* Return value is ignored - it's ok if wait queue is empty */
qemu_co_queue_next(&bs->flush_queue);
- tracked_request_end(&req);
+ bdrv_dec_in_flight(bs);
return ret;
}
@@ -2350,13 +2372,9 @@ int bdrv_flush(BlockDriverState *bs)
/* Fast-path if already in coroutine context */
bdrv_flush_co_entry(&flush_co);
} else {
- AioContext *aio_context = bdrv_get_aio_context(bs);
-
co = qemu_coroutine_create(bdrv_flush_co_entry, &flush_co);
qemu_coroutine_enter(co);
- while (flush_co.ret == NOT_DONE) {
- aio_poll(aio_context, true);
- }
+ BDRV_POLL_WHILE(bs, flush_co.ret == NOT_DONE);
}
return flush_co.ret;
@@ -2417,6 +2435,7 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset,
return 0;
}
+ bdrv_inc_in_flight(bs);
tracked_request_begin(&req, bs, offset, count, BDRV_TRACKED_DISCARD);
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req);
@@ -2463,6 +2482,7 @@ out:
bdrv_set_dirty(bs, req.offset >> BDRV_SECTOR_BITS,
req.bytes >> BDRV_SECTOR_BITS);
tracked_request_end(&req);
+ bdrv_dec_in_flight(bs);
return ret;
}
@@ -2480,13 +2500,9 @@ int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count)
/* Fast-path if already in coroutine context */
bdrv_pdiscard_co_entry(&rwco);
} else {
- AioContext *aio_context = bdrv_get_aio_context(bs);
-
co = qemu_coroutine_create(bdrv_pdiscard_co_entry, &rwco);
qemu_coroutine_enter(co);
- while (rwco.ret == NOT_DONE) {
- aio_poll(aio_context, true);
- }
+ BDRV_POLL_WHILE(bs, rwco.ret == NOT_DONE);
}
return rwco.ret;
@@ -2495,13 +2511,12 @@ int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count)
int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf)
{
BlockDriver *drv = bs->drv;
- BdrvTrackedRequest tracked_req;
CoroutineIOCompletion co = {
.coroutine = qemu_coroutine_self(),
};
BlockAIOCB *acb;
- tracked_request_begin(&tracked_req, bs, 0, 0, BDRV_TRACKED_IOCTL);
+ bdrv_inc_in_flight(bs);
if (!drv || (!drv->bdrv_aio_ioctl && !drv->bdrv_co_ioctl)) {
co.ret = -ENOTSUP;
goto out;
@@ -2518,7 +2533,7 @@ int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf)
qemu_coroutine_yield();
}
out:
- tracked_request_end(&tracked_req);
+ bdrv_dec_in_flight(bs);
return co.ret;
}
diff --git a/block/mirror.c b/block/mirror.c
index a433e6848c..3a0788ede3 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -469,7 +469,11 @@ static void mirror_free_init(MirrorBlockJob *s)
}
}
-static void mirror_drain(MirrorBlockJob *s)
+/* This is also used for the .pause callback. There is no matching
+ * mirror_resume() because mirror_run() will begin iterating again
+ * when the job is resumed.
+ */
+static void mirror_wait_for_all_io(MirrorBlockJob *s)
{
while (s->in_flight > 0) {
mirror_wait_for_io(s);
@@ -528,6 +532,7 @@ static void mirror_exit(BlockJob *job, void *opaque)
g_free(s->replaces);
bdrv_op_unblock_all(target_bs, s->common.blocker);
blk_unref(s->target);
+ s->target = NULL;
block_job_completed(&s->common, data->ret);
g_free(data);
bdrv_drained_end(src);
@@ -582,7 +587,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
sector_num += nb_sectors;
}
- mirror_drain(s);
+ mirror_wait_for_all_io(s);
}
/* First part, loop on the sectors and initialize the dirty bitmap. */
@@ -617,6 +622,7 @@ static void coroutine_fn mirror_run(void *opaque)
MirrorExitData *data;
BlockDriverState *bs = blk_bs(s->common.blk);
BlockDriverState *target_bs = blk_bs(s->target);
+ bool need_drain = true;
int64_t length;
BlockDriverInfo bdi;
char backing_filename[2]; /* we only need 2 characters because we are only
@@ -752,11 +758,26 @@ static void coroutine_fn mirror_run(void *opaque)
* source has dirty data to copy!
*
* Note that I/O can be submitted by the guest while
- * mirror_populate runs.
+ * mirror_populate runs, so pause it now. Before deciding
+ * whether to switch to target check one last time if I/O has
+ * come in the meanwhile, and if not flush the data to disk.
*/
trace_mirror_before_drain(s, cnt);
- bdrv_co_drain(bs);
+
+ bdrv_drained_begin(bs);
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
+ if (cnt > 0) {
+ bdrv_drained_end(bs);
+ continue;
+ }
+
+ /* The two disks are in sync. Exit and report successful
+ * completion.
+ */
+ assert(QLIST_EMPTY(&bs->tracked_requests));
+ s->common.cancelled = false;
+ need_drain = false;
+ break;
}
ret = 0;
@@ -769,13 +790,6 @@ static void coroutine_fn mirror_run(void *opaque)
} else if (!should_complete) {
delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0);
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns);
- } else if (cnt == 0) {
- /* The two disks are in sync. Exit and report successful
- * completion.
- */
- assert(QLIST_EMPTY(&bs->tracked_requests));
- s->common.cancelled = false;
- break;
}
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
}
@@ -787,7 +801,8 @@ immediate_exit:
* the target is a copy of the source.
*/
assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
- mirror_drain(s);
+ assert(need_drain);
+ mirror_wait_for_all_io(s);
}
assert(s->in_flight == 0);
@@ -799,9 +814,10 @@ immediate_exit:
data = g_malloc(sizeof(*data));
data->ret = ret;
- /* Before we switch to target in mirror_exit, make sure data doesn't
- * change. */
- bdrv_drained_begin(bs);
+
+ if (need_drain) {
+ bdrv_drained_begin(bs);
+ }
block_job_defer_to_main_loop(&s->common, mirror_exit, data);
}
@@ -872,14 +888,11 @@ static void mirror_complete(BlockJob *job, Error **errp)
block_job_enter(&s->common);
}
-/* There is no matching mirror_resume() because mirror_run() will begin
- * iterating again when the job is resumed.
- */
-static void coroutine_fn mirror_pause(BlockJob *job)
+static void mirror_pause(BlockJob *job)
{
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
- mirror_drain(s);
+ mirror_wait_for_all_io(s);
}
static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context)
@@ -889,6 +902,21 @@ static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context)
blk_set_aio_context(s->target, new_context);
}
+static void mirror_drain(BlockJob *job)
+{
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
+
+ /* Need to keep a reference in case blk_drain triggers execution
+ * of mirror_complete...
+ */
+ if (s->target) {
+ BlockBackend *target = s->target;
+ blk_ref(target);
+ blk_drain(target);
+ blk_unref(target);
+ }
+}
+
static const BlockJobDriver mirror_job_driver = {
.instance_size = sizeof(MirrorBlockJob),
.job_type = BLOCK_JOB_TYPE_MIRROR,
@@ -896,6 +924,7 @@ static const BlockJobDriver mirror_job_driver = {
.complete = mirror_complete,
.pause = mirror_pause,
.attached_aio_context = mirror_attached_aio_context,
+ .drain = mirror_drain,
};
static const BlockJobDriver commit_active_job_driver = {
@@ -905,6 +934,7 @@ static const BlockJobDriver commit_active_job_driver = {
.complete = mirror_complete,
.pause = mirror_pause,
.attached_aio_context = mirror_attached_aio_context,
+ .drain = mirror_drain,
};
static void mirror_start_job(const char *job_id, BlockDriverState *bs,
diff --git a/block/nbd.c b/block/nbd.c
index 8ef143870f..6e837f80c9 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -307,6 +307,7 @@ static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr,
Error *local_err = NULL;
sioc = qio_channel_socket_new();
+ qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client");
qio_channel_socket_connect_sync(sioc,
saddr,
diff --git a/block/nfs.c b/block/nfs.c
index c3db2ec58d..88c60a9118 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -52,6 +52,7 @@ typedef struct NFSClient {
} NFSClient;
typedef struct NFSRPC {
+ BlockDriverState *bs;
int ret;
int complete;
QEMUIOVector *iov;
@@ -90,11 +91,12 @@ static void nfs_process_write(void *arg)
nfs_set_events(client);
}
-static void nfs_co_init_task(NFSClient *client, NFSRPC *task)
+static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
{
*task = (NFSRPC) {
.co = qemu_coroutine_self(),
- .client = client,
+ .bs = bs,
+ .client = bs->opaque,
};
}
@@ -111,6 +113,7 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
{
NFSRPC *task = private_data;
task->ret = ret;
+ assert(!task->st);
if (task->ret > 0 && task->iov) {
if (task->ret <= task->iov->size) {
qemu_iovec_from_buf(task->iov, 0, data, task->ret);
@@ -118,18 +121,11 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
task->ret = -EIO;
}
}
- if (task->ret == 0 && task->st) {
- memcpy(task->st, data, sizeof(struct stat));
- }
if (task->ret < 0) {
error_report("NFS Error: %s", nfs_get_error(nfs));
}
- if (task->co) {
- aio_bh_schedule_oneshot(task->client->aio_context,
- nfs_co_generic_bh_cb, task);
- } else {
- task->complete = 1;
- }
+ aio_bh_schedule_oneshot(task->client->aio_context,
+ nfs_co_generic_bh_cb, task);
}
static int coroutine_fn nfs_co_readv(BlockDriverState *bs,
@@ -139,7 +135,7 @@ static int coroutine_fn nfs_co_readv(BlockDriverState *bs,
NFSClient *client = bs->opaque;
NFSRPC task;
- nfs_co_init_task(client, &task);
+ nfs_co_init_task(bs, &task);
task.iov = iov;
if (nfs_pread_async(client->context, client->fh,
@@ -149,8 +145,8 @@ static int coroutine_fn nfs_co_readv(BlockDriverState *bs,
return -ENOMEM;
}
+ nfs_set_events(client);
while (!task.complete) {
- nfs_set_events(client);
qemu_coroutine_yield();
}
@@ -174,7 +170,7 @@ static int coroutine_fn nfs_co_writev(BlockDriverState *bs,
NFSRPC task;
char *buf = NULL;
- nfs_co_init_task(client, &task);
+ nfs_co_init_task(bs, &task);
buf = g_try_malloc(nb_sectors * BDRV_SECTOR_SIZE);
if (nb_sectors && buf == NULL) {
@@ -191,8 +187,8 @@ static int coroutine_fn nfs_co_writev(BlockDriverState *bs,
return -ENOMEM;
}
+ nfs_set_events(client);
while (!task.complete) {
- nfs_set_events(client);
qemu_coroutine_yield();
}
@@ -210,15 +206,15 @@ static int coroutine_fn nfs_co_flush(BlockDriverState *bs)
NFSClient *client = bs->opaque;
NFSRPC task;
- nfs_co_init_task(client, &task);
+ nfs_co_init_task(bs, &task);
if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb,
&task) != 0) {
return -ENOMEM;
}
+ nfs_set_events(client);
while (!task.complete) {
- nfs_set_events(client);
qemu_coroutine_yield();
}
@@ -496,6 +492,22 @@ static int nfs_has_zero_init(BlockDriverState *bs)
return client->has_zero_init;
}
+static void
+nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
+ void *private_data)
+{
+ NFSRPC *task = private_data;
+ task->ret = ret;
+ if (task->ret == 0) {
+ memcpy(task->st, data, sizeof(struct stat));
+ }
+ if (task->ret < 0) {
+ error_report("NFS Error: %s", nfs_get_error(nfs));
+ }
+ task->complete = 1;
+ bdrv_wakeup(task->bs);
+}
+
static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
{
NFSClient *client = bs->opaque;
@@ -507,16 +519,15 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
return client->st_blocks * 512;
}
+ task.bs = bs;
task.st = &st;
- if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb,
+ if (nfs_fstat_async(client->context, client->fh, nfs_get_allocated_file_size_cb,
&task) != 0) {
return -ENOMEM;
}
- while (!task.complete) {
- nfs_set_events(client);
- aio_poll(client->aio_context, true);
- }
+ nfs_set_events(client);
+ BDRV_POLL_WHILE(bs, !task.complete);
return (task.ret < 0 ? task.ret : st.st_blocks * 512);
}
diff --git a/block/qed-table.c b/block/qed-table.c
index 1a731dff51..ed443e2b70 100644
--- a/block/qed-table.c
+++ b/block/qed-table.c
@@ -174,9 +174,7 @@ int qed_read_l1_table_sync(BDRVQEDState *s)
qed_read_table(s, s->header.l1_table_offset,
s->l1_table, qed_sync_cb, &ret);
- while (ret == -EINPROGRESS) {
- aio_poll(bdrv_get_aio_context(s->bs), true);
- }
+ BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS);
return ret;
}
@@ -195,9 +193,7 @@ int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
int ret = -EINPROGRESS;
qed_write_l1_table(s, index, n, qed_sync_cb, &ret);
- while (ret == -EINPROGRESS) {
- aio_poll(bdrv_get_aio_context(s->bs), true);
- }
+ BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS);
return ret;
}
@@ -268,9 +264,7 @@ int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset
int ret = -EINPROGRESS;
qed_read_l2_table(s, request, offset, qed_sync_cb, &ret);
- while (ret == -EINPROGRESS) {
- aio_poll(bdrv_get_aio_context(s->bs), true);
- }
+ BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS);
return ret;
}
@@ -290,9 +284,7 @@ int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
int ret = -EINPROGRESS;
qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret);
- while (ret == -EINPROGRESS) {
- aio_poll(bdrv_get_aio_context(s->bs), true);
- }
+ BDRV_POLL_WHILE(s->bs, ret == -EINPROGRESS);
return ret;
}
diff --git a/block/qed.c b/block/qed.c
index 3ee879b52e..1a7ef0a9ce 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -336,7 +336,7 @@ static void qed_need_check_timer_cb(void *opaque)
qed_plug_allocating_write_reqs(s);
/* Ensure writes are on disk before clearing flag */
- bdrv_aio_flush(s->bs, qed_clear_need_check, s);
+ bdrv_aio_flush(s->bs->file->bs, qed_clear_need_check, s);
}
static void qed_start_need_check_timer(BDRVQEDState *s)
@@ -378,6 +378,19 @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs,
}
}
+static void bdrv_qed_drain(BlockDriverState *bs)
+{
+ BDRVQEDState *s = bs->opaque;
+
+ /* Fire the timer immediately in order to start doing I/O as soon as the
+ * header is flushed.
+ */
+ if (s->need_check_timer && timer_pending(s->need_check_timer)) {
+ qed_cancel_need_check_timer(s);
+ qed_need_check_timer_cb(s);
+ }
+}
+
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
@@ -1668,6 +1681,7 @@ static BlockDriver bdrv_qed = {
.bdrv_check = bdrv_qed_check,
.bdrv_detach_aio_context = bdrv_qed_detach_aio_context,
.bdrv_attach_aio_context = bdrv_qed_attach_aio_context,
+ .bdrv_drain = bdrv_qed_drain,
};
static void bdrv_qed_init(void)
diff --git a/block/replication.c b/block/replication.c
index 8bbfc8f870..02aeaaf7d0 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -138,6 +138,9 @@ static void replication_close(BlockDriverState *bs)
if (s->replication_state == BLOCK_REPLICATION_RUNNING) {
replication_stop(s->rs, false, NULL);
}
+ if (s->replication_state == BLOCK_REPLICATION_FAILOVER) {
+ block_job_cancel_sync(s->active_disk->bs->job);
+ }
if (s->mode == REPLICATION_MODE_SECONDARY) {
g_free(s->top_id);
@@ -319,9 +322,10 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
}
}
-static void reopen_backing_file(BDRVReplicationState *s, bool writable,
+static void reopen_backing_file(BlockDriverState *bs, bool writable,
Error **errp)
{
+ BDRVReplicationState *s = bs->opaque;
BlockReopenQueue *reopen_queue = NULL;
int orig_hidden_flags, orig_secondary_flags;
int new_hidden_flags, new_secondary_flags;
@@ -356,13 +360,15 @@ static void reopen_backing_file(BDRVReplicationState *s, bool writable,
}
if (reopen_queue) {
- bdrv_reopen_multiple(reopen_queue, &local_err);
+ bdrv_reopen_multiple(bdrv_get_aio_context(bs),
+ reopen_queue, &local_err);
error_propagate(errp, local_err);
}
}
-static void backup_job_cleanup(BDRVReplicationState *s)
+static void backup_job_cleanup(BlockDriverState *bs)
{
+ BDRVReplicationState *s = bs->opaque;
BlockDriverState *top_bs;
top_bs = bdrv_lookup_bs(s->top_id, s->top_id, NULL);
@@ -371,19 +377,20 @@ static void backup_job_cleanup(BDRVReplicationState *s)
}
bdrv_op_unblock_all(top_bs, s->blocker);
error_free(s->blocker);
- reopen_backing_file(s, false, NULL);
+ reopen_backing_file(bs, false, NULL);
}
static void backup_job_completed(void *opaque, int ret)
{
- BDRVReplicationState *s = opaque;
+ BlockDriverState *bs = opaque;
+ BDRVReplicationState *s = bs->opaque;
if (s->replication_state != BLOCK_REPLICATION_FAILOVER) {
/* The backup job is cancelled unexpectedly */
s->error = -EIO;
}
- backup_job_cleanup(s);
+ backup_job_cleanup(bs);
}
static bool check_top_bs(BlockDriverState *top_bs, BlockDriverState *bs)
@@ -479,7 +486,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
}
/* reopen the backing file in r/w mode */
- reopen_backing_file(s, true, &local_err);
+ reopen_backing_file(bs, true, &local_err);
if (local_err) {
error_propagate(errp, local_err);
aio_context_release(aio_context);
@@ -494,7 +501,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
if (!top_bs || !bdrv_is_root_node(top_bs) ||
!check_top_bs(top_bs, bs)) {
error_setg(errp, "No top_bs or it is invalid");
- reopen_backing_file(s, false, NULL);
+ reopen_backing_file(bs, false, NULL);
aio_context_release(aio_context);
return;
}
@@ -504,10 +511,10 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
backup_start("replication-backup", s->secondary_disk->bs,
s->hidden_disk->bs, 0, MIRROR_SYNC_MODE_NONE, NULL, false,
BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
- backup_job_completed, s, NULL, &local_err);
+ backup_job_completed, bs, NULL, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- backup_job_cleanup(s);
+ backup_job_cleanup(bs);
aio_context_release(aio_context);
return;
}
diff --git a/block/sheepdog.c b/block/sheepdog.c
index ccbf7e1fa6..1fb917343a 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -641,6 +641,7 @@ static void restart_co_req(void *opaque)
typedef struct SheepdogReqCo {
int sockfd;
+ BlockDriverState *bs;
AioContext *aio_context;
SheepdogReq *hdr;
void *data;
@@ -701,6 +702,9 @@ out:
srco->ret = ret;
srco->finished = true;
+ if (srco->bs) {
+ bdrv_wakeup(srco->bs);
+ }
}
/*
@@ -708,13 +712,14 @@ out:
*
* Return 0 on success, -errno in case of error.
*/
-static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr,
+static int do_req(int sockfd, BlockDriverState *bs, SheepdogReq *hdr,
void *data, unsigned int *wlen, unsigned int *rlen)
{
Coroutine *co;
SheepdogReqCo srco = {
.sockfd = sockfd,
- .aio_context = aio_context,
+ .aio_context = bs ? bdrv_get_aio_context(bs) : qemu_get_aio_context(),
+ .bs = bs,
.hdr = hdr,
.data = data,
.wlen = wlen,
@@ -727,9 +732,14 @@ static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr,
do_co_req(&srco);
} else {
co = qemu_coroutine_create(do_co_req, &srco);
- qemu_coroutine_enter(co);
- while (!srco.finished) {
- aio_poll(aio_context, true);
+ if (bs) {
+ qemu_coroutine_enter(co);
+ BDRV_POLL_WHILE(bs, !srco.finished);
+ } else {
+ qemu_coroutine_enter(co);
+ while (!srco.finished) {
+ aio_poll(qemu_get_aio_context(), true);
+ }
}
}
@@ -1125,7 +1135,7 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename,
hdr.snapid = snapid;
hdr.flags = SD_FLAG_CMD_WRITE;
- ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
+ ret = do_req(fd, s->bs, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
if (ret) {
error_setg_errno(errp, -ret, "cannot get vdi info");
goto out;
@@ -1240,7 +1250,7 @@ out:
qemu_co_mutex_unlock(&s->lock);
}
-static int read_write_object(int fd, AioContext *aio_context, char *buf,
+static int read_write_object(int fd, BlockDriverState *bs, char *buf,
uint64_t oid, uint8_t copies,
unsigned int datalen, uint64_t offset,
bool write, bool create, uint32_t cache_flags)
@@ -1274,7 +1284,7 @@ static int read_write_object(int fd, AioContext *aio_context, char *buf,
hdr.offset = offset;
hdr.copies = copies;
- ret = do_req(fd, aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
+ ret = do_req(fd, bs, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
if (ret) {
error_report("failed to send a request to the sheep");
return ret;
@@ -1289,22 +1299,22 @@ static int read_write_object(int fd, AioContext *aio_context, char *buf,
}
}
-static int read_object(int fd, AioContext *aio_context, char *buf,
+static int read_object(int fd, BlockDriverState *bs, char *buf,
uint64_t oid, uint8_t copies,
unsigned int datalen, uint64_t offset,
uint32_t cache_flags)
{
- return read_write_object(fd, aio_context, buf, oid, copies,
+ return read_write_object(fd, bs, buf, oid, copies,
datalen, offset, false,
false, cache_flags);
}
-static int write_object(int fd, AioContext *aio_context, char *buf,
+static int write_object(int fd, BlockDriverState *bs, char *buf,
uint64_t oid, uint8_t copies,
unsigned int datalen, uint64_t offset, bool create,
uint32_t cache_flags)
{
- return read_write_object(fd, aio_context, buf, oid, copies,
+ return read_write_object(fd, bs, buf, oid, copies,
datalen, offset, true,
create, cache_flags);
}
@@ -1331,7 +1341,7 @@ static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag)
goto out;
}
- ret = read_object(fd, s->aio_context, (char *)inode, vid_to_vdi_oid(vid),
+ ret = read_object(fd, s->bs, (char *)inode, vid_to_vdi_oid(vid),
s->inode.nr_copies, SD_INODE_HEADER_SIZE, 0,
s->cache_flags);
if (ret < 0) {
@@ -1489,7 +1499,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
}
buf = g_malloc(SD_INODE_SIZE);
- ret = read_object(fd, s->aio_context, buf, vid_to_vdi_oid(vid),
+ ret = read_object(fd, s->bs, buf, vid_to_vdi_oid(vid),
0, SD_INODE_SIZE, 0, s->cache_flags);
closesocket(fd);
@@ -1618,7 +1628,7 @@ static int do_sd_create(BDRVSheepdogState *s, uint32_t *vdi_id, int snapshot,
hdr.copies = s->inode.nr_copies;
hdr.block_size_shift = s->inode.block_size_shift;
- ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
+ ret = do_req(fd, NULL, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
closesocket(fd);
@@ -1886,7 +1896,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
hdr.opcode = SD_OP_GET_CLUSTER_DEFAULT;
hdr.proto_ver = SD_PROTO_VER;
- ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr,
+ ret = do_req(fd, NULL, (SheepdogReq *)&hdr,
NULL, &wlen, &rlen);
closesocket(fd);
if (ret) {
@@ -1951,7 +1961,7 @@ static void sd_close(BlockDriverState *bs)
hdr.data_length = wlen;
hdr.flags = SD_FLAG_CMD_WRITE;
- ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr,
+ ret = do_req(fd, s->bs, (SheepdogReq *)&hdr,
s->name, &wlen, &rlen);
closesocket(fd);
@@ -2000,7 +2010,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
/* we don't need to update entire object */
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
s->inode.vdi_size = offset;
- ret = write_object(fd, s->aio_context, (char *)&s->inode,
+ ret = write_object(fd, s->bs, (char *)&s->inode,
vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies,
datalen, 0, false, s->cache_flags);
close(fd);
@@ -2070,7 +2080,7 @@ static bool sd_delete(BDRVSheepdogState *s)
return false;
}
- ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr,
+ ret = do_req(fd, s->bs, (SheepdogReq *)&hdr,
s->name, &wlen, &rlen);
closesocket(fd);
if (ret) {
@@ -2126,7 +2136,7 @@ static int sd_create_branch(BDRVSheepdogState *s)
goto out;
}
- ret = read_object(fd, s->aio_context, buf, vid_to_vdi_oid(vid),
+ ret = read_object(fd, s->bs, buf, vid_to_vdi_oid(vid),
s->inode.nr_copies, SD_INODE_SIZE, 0, s->cache_flags);
closesocket(fd);
@@ -2411,7 +2421,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
goto cleanup;
}
- ret = write_object(fd, s->aio_context, (char *)&s->inode,
+ ret = write_object(fd, s->bs, (char *)&s->inode,
vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies,
datalen, 0, false, s->cache_flags);
if (ret < 0) {
@@ -2426,7 +2436,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
goto cleanup;
}
- ret = read_object(fd, s->aio_context, (char *)inode,
+ ret = read_object(fd, s->bs, (char *)inode,
vid_to_vdi_oid(new_vid), s->inode.nr_copies, datalen, 0,
s->cache_flags);
@@ -2528,7 +2538,7 @@ static bool remove_objects(BDRVSheepdogState *s)
i++;
}
- ret = write_object(fd, s->aio_context,
+ ret = write_object(fd, s->bs,
(char *)&inode->data_vdi_id[start_idx],
vid_to_vdi_oid(s->inode.vdi_id), inode->nr_copies,
(i - start_idx) * sizeof(uint32_t),
@@ -2600,7 +2610,7 @@ static int sd_snapshot_delete(BlockDriverState *bs,
return -1;
}
- ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr,
+ ret = do_req(fd, s->bs, (SheepdogReq *)&hdr,
buf, &wlen, &rlen);
closesocket(fd);
if (ret) {
@@ -2652,8 +2662,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
req.opcode = SD_OP_READ_VDIS;
req.data_length = max;
- ret = do_req(fd, s->aio_context, &req,
- vdi_inuse, &wlen, &rlen);
+ ret = do_req(fd, s->bs, &req, vdi_inuse, &wlen, &rlen);
closesocket(fd);
if (ret) {
@@ -2679,7 +2688,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
}
/* we don't need to read entire object */
- ret = read_object(fd, s->aio_context, (char *)&inode,
+ ret = read_object(fd, s->bs, (char *)&inode,
vid_to_vdi_oid(vid),
0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0,
s->cache_flags);
@@ -2745,11 +2754,11 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
create = (offset == 0);
if (load) {
- ret = read_object(fd, s->aio_context, (char *)data, vmstate_oid,
+ ret = read_object(fd, s->bs, (char *)data, vmstate_oid,
s->inode.nr_copies, data_len, offset,
s->cache_flags);
} else {
- ret = write_object(fd, s->aio_context, (char *)data, vmstate_oid,
+ ret = write_object(fd, s->bs, (char *)data, vmstate_oid,
s->inode.nr_copies, data_len, offset, create,
s->cache_flags);
}
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index ca41cc6fdd..81bca1760f 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -44,6 +44,7 @@ static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
return TRUE;
}
+ qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
nbd_client_new(NULL, cioc,
nbd_server->tlscreds, NULL,
nbd_client_put);
@@ -111,6 +112,8 @@ void qmp_nbd_server_start(SocketAddress *addr,
nbd_server = g_new0(NBDServerData, 1);
nbd_server->watch = -1;
nbd_server->listen_ioc = qio_channel_socket_new();
+ qio_channel_set_name(QIO_CHANNEL(nbd_server->listen_ioc),
+ "nbd-listener");
if (qio_channel_socket_listen_sync(
nbd_server->listen_ioc, addr, errp) < 0) {
goto error;
diff --git a/blockjob.c b/blockjob.c
index 43fecbe13e..7c88b30074 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -74,17 +74,6 @@ BlockJob *block_job_get(const char *id)
return NULL;
}
-/* Normally the job runs in its BlockBackend's AioContext. The exception is
- * block_job_defer_to_main_loop() where it runs in the QEMU main loop. Code
- * that supports both cases uses this helper function.
- */
-static AioContext *block_job_get_aio_context(BlockJob *job)
-{
- return job->deferred_to_main_loop ?
- qemu_get_aio_context() :
- blk_get_aio_context(job->blk);
-}
-
static void block_job_attached_aio_context(AioContext *new_context,
void *opaque)
{
@@ -97,6 +86,17 @@ static void block_job_attached_aio_context(AioContext *new_context,
block_job_resume(job);
}
+static void block_job_drain(BlockJob *job)
+{
+ /* If job is !job->busy this kicks it into the next pause point. */
+ block_job_enter(job);
+
+ blk_drain(job->blk);
+ if (job->driver->drain) {
+ job->driver->drain(job);
+ }
+}
+
static void block_job_detach_aio_context(void *opaque)
{
BlockJob *job = opaque;
@@ -106,12 +106,8 @@ static void block_job_detach_aio_context(void *opaque)
block_job_pause(job);
- if (!job->paused) {
- /* If job is !job->busy this kicks it into the next pause point. */
- block_job_enter(job);
- }
while (!job->paused && !job->completed) {
- aio_poll(block_job_get_aio_context(job), true);
+ block_job_drain(job);
}
block_job_unref(job);
@@ -413,14 +409,21 @@ static int block_job_finish_sync(BlockJob *job,
assert(blk_bs(job->blk)->job == job);
block_job_ref(job);
+
finish(job, &local_err);
if (local_err) {
error_propagate(errp, local_err);
block_job_unref(job);
return -EBUSY;
}
+ /* block_job_drain calls block_job_enter, and it should be enough to
+ * induce progress until the job completes or moves to the main thread.
+ */
+ while (!job->deferred_to_main_loop && !job->completed) {
+ block_job_drain(job);
+ }
while (!job->completed) {
- aio_poll(block_job_get_aio_context(job), true);
+ aio_poll(qemu_get_aio_context(), true);
}
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
block_job_unref(job);
diff --git a/configure b/configure
index 8e10059607..f83cdf8876 100755
--- a/configure
+++ b/configure
@@ -2917,27 +2917,38 @@ fi
# curses probe
if test "$curses" != "no" ; then
if test "$mingw32" = "yes" ; then
- curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lpdcurses"
+ curses_inc_list="$($pkg_config --cflags ncurses 2>/dev/null):"
+ curses_lib_list="$($pkg_config --libs ncurses 2>/dev/null):-lpdcurses"
else
- curses_list="$($pkg_config --libs ncurses 2>/dev/null):-lncurses:-lcurses"
+ curses_inc_list="$($pkg_config --cflags ncursesw 2>/dev/null):"
+ curses_lib_list="$($pkg_config --libs ncursesw 2>/dev/null):-lncursesw:-lcursesw"
fi
curses_found=no
cat > $TMPC << EOF
+#include <locale.h>
#include <curses.h>
+#include <wchar.h>
int main(void) {
const char *s = curses_version();
+ wchar_t wch = L'w';
+ setlocale(LC_ALL, "");
resize_term(0, 0);
+ addwstr(L"wide chars\n");
+ addnwstr(&wch, 1);
return s != 0;
}
EOF
IFS=:
- for curses_lib in $curses_list; do
- unset IFS
- if compile_prog "" "$curses_lib" ; then
- curses_found=yes
- libs_softmmu="$curses_lib $libs_softmmu"
- break
- fi
+ for curses_inc in $curses_inc_list; do
+ for curses_lib in $curses_lib_list; do
+ unset IFS
+ if compile_prog "$curses_inc" "$curses_lib" ; then
+ curses_found=yes
+ QEMU_CFLAGS="$curses_inc $QEMU_CFLAGS"
+ libs_softmmu="$curses_lib $libs_softmmu"
+ break
+ fi
+ done
done
unset IFS
if test "$curses_found" = "yes" ; then
@@ -6131,6 +6142,7 @@ FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
FILES="$FILES pc-bios/qemu-icon.bmp"
for bios_file in \
$source_path/pc-bios/*.bin \
+ $source_path/pc-bios/*.lid \
$source_path/pc-bios/*.aml \
$source_path/pc-bios/*.rom \
$source_path/pc-bios/*.dtb \
diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
index db5a4d6f5e..67a9bcaa67 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -39,6 +39,7 @@ CONFIG_I8259=y
CONFIG_XILINX=y
CONFIG_XILINX_ETHLITE=y
CONFIG_PSERIES=y
+CONFIG_POWERNV=y
CONFIG_PREP=y
CONFIG_MAC=y
CONFIG_E500=y
diff --git a/docs/multiple-iothreads.txt b/docs/multiple-iothreads.txt
index 40b8419916..0e7cdb2c28 100644
--- a/docs/multiple-iothreads.txt
+++ b/docs/multiple-iothreads.txt
@@ -105,13 +105,10 @@ a BH in the target AioContext beforehand and then call qemu_bh_schedule(). No
acquire/release or locking is needed for the qemu_bh_schedule() call. But be
sure to acquire the AioContext for aio_bh_new() if necessary.
-The relationship between AioContext and the block layer
--------------------------------------------------------
-The AioContext originates from the QEMU block layer because it provides a
-scoped way of running event loop iterations until all work is done. This
-feature is used to complete all in-flight block I/O requests (see
-bdrv_drain_all()). Nowadays AioContext is a generic event loop that can be
-used by any QEMU subsystem.
+AioContext and the block layer
+------------------------------
+The AioContext originates from the QEMU block layer, even though nowadays
+AioContext is a generic event loop that can be used by any QEMU subsystem.
The block layer has support for AioContext integrated. Each BlockDriverState
is associated with an AioContext using bdrv_set_aio_context() and
@@ -122,13 +119,22 @@ Block layer code must therefore expect to run in an IOThread and avoid using
old APIs that implicitly use the main loop. See the "How to program for
IOThreads" above for information on how to do that.
-If main loop code such as a QMP function wishes to access a BlockDriverState it
-must first call aio_context_acquire(bdrv_get_aio_context(bs)) to ensure the
-IOThread does not run in parallel.
-
-Long-running jobs (usually in the form of coroutines) are best scheduled in the
-BlockDriverState's AioContext to avoid the need to acquire/release around each
-bdrv_*() call. Be aware that there is currently no mechanism to get notified
-when bdrv_set_aio_context() moves this BlockDriverState to a different
-AioContext (see bdrv_detach_aio_context()/bdrv_attach_aio_context()), so you
-may need to add this if you want to support long-running jobs.
+If main loop code such as a QMP function wishes to access a BlockDriverState
+it must first call aio_context_acquire(bdrv_get_aio_context(bs)) to ensure
+that callbacks in the IOThread do not run in parallel.
+
+Code running in the monitor typically needs to ensure that past
+requests from the guest are completed. When a block device is running
+in an IOThread, the IOThread can also process requests from the guest
+(via ioeventfd). To achieve both objects, wrap the code between
+bdrv_drained_begin() and bdrv_drained_end(), thus creating a "drained
+section". The functions must be called between aio_context_acquire()
+and aio_context_release(). You can freely release and re-acquire the
+AioContext within a drained section.
+
+Long-running jobs (usually in the form of coroutines) are best scheduled in
+the BlockDriverState's AioContext to avoid the need to acquire/release around
+each bdrv_*() call. The functions bdrv_add/remove_aio_context_notifier,
+or alternatively blk_add/remove_aio_context_notifier if you use BlockBackends,
+can be used to get a notification whenever bdrv_set_aio_context() moves a
+BlockDriverState to a different AioContext.
diff --git a/docs/specs/ppc-spapr-hotplug.txt b/docs/specs/ppc-spapr-hotplug.txt
index 631b0cadae..f57e2a09c6 100644
--- a/docs/specs/ppc-spapr-hotplug.txt
+++ b/docs/specs/ppc-spapr-hotplug.txt
@@ -233,12 +233,27 @@ tools by host-level management such as an HMC. This level of management is not
applicable to PowerKVM, hence the reason for extending the notification
framework to support hotplug events.
-Note that these events are not yet formally part of the PAPR+ specification,
-but support for this format has already been implemented in DR-related
-guest tools such as powerpc-utils/librtas, as well as kernel patches that have
-been submitted to handle in-kernel processing of memory/cpu-related hotplug
-events[1], and is planned for formal inclusion is PAPR+ specification. The
-hotplug-specific payload is QEMU implemented as follows (with all values
+The format for these EPOW-signalled events is described below under
+"hotplug/unplug event structure". Note that these events are not
+formally part of the PAPR+ specification, and have been superseded by a
+newer format, also described below under "hotplug/unplug event structure",
+and so are now deemed a "legacy" format. The formats are similar, but the
+"modern" format contains additional fields/flags, which are denoted for the
+purposes of this documentation with "#ifdef GUEST_SUPPORTS_MODERN" guards.
+
+QEMU should assume support only for "legacy" fields/flags unless the guest
+advertises support for the "modern" format via ibm,client-architecture-support
+hcall by setting byte 5, bit 6 of it's ibm,architecture-vec-5 option vector
+structure (as described by LoPAPR v11, B.6.2.3). As with "legacy" format events,
+"modern" format events are surfaced to the guest via check-exception RTAS calls,
+but use a dedicated event source to signal the guest. This event source is
+advertised to the guest by the addition of a "hot-plug-events" node under
+"/event-sources" node of the guest's device tree using the standard format
+described in LoPAPR v11, B.6.12.1.
+
+== hotplug/unplug event structure ==
+
+The hotplug-specific payload in QEMU is implemented as follows (with all values
encoded in big-endian format):
struct rtas_event_log_v6_hp {
@@ -263,14 +278,23 @@ struct rtas_event_log_v6_hp {
#define RTAS_LOG_V6_HP_ACTION_ADD 1
#define RTAS_LOG_V6_HP_ACTION_REMOVE 2
uint8_t hotplug_action; /* action (add/remove) */
-#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
-#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
-#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
+#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
+#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
+#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
+#ifdef GUEST_SUPPORTS_MODERN
+#define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4
+#endif
uint8_t hotplug_identifier; /* type of the resource identifier,
* which serves as the discriminator
* for the 'drc' union field below
*/
+#ifdef GUEST_SUPPORTS_MODERN
+ uint8_t capabilities; /* capability flags, currently unused
+ * by QEMU
+ */
+#else
uint8_t reserved;
+#endif
union {
uint32_t index; /* DRC index of resource to take action
* on
@@ -278,6 +302,19 @@ struct rtas_event_log_v6_hp {
uint32_t count; /* number of DR resources to take
* action on (guest chooses which)
*/
+#ifdef GUEST_SUPPORTS_MODERN
+ struct {
+ uint32_t count; /* number of DR resources to take
+ * action on
+ */
+ uint32_t index; /* DRC index of first resource to take
+ * action on. guest will take action
+ * on DRC index <index> through
+ * DRC index <index + count - 1> in
+ * sequential order
+ */
+ } count_indexed;
+#endif
char name[1]; /* string representing the name of the
* DRC to take action on
*/
diff --git a/hw/input/adb.c b/hw/input/adb.c
index 3d39368909..43d3205472 100644
--- a/hw/input/adb.c
+++ b/hw/input/adb.c
@@ -396,9 +396,15 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
d->devaddr = buf[1] & 0xf;
break;
default:
- /* XXX: check this */
d->devaddr = buf[1] & 0xf;
- d->handler = buf[2];
+ /* we support handlers:
+ * 1: Apple Standard Keyboard
+ * 2: Apple Extended Keyboard (LShift = RShift)
+ * 3: Apple Extended Keyboard (LShift != RShift)
+ */
+ if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) {
+ d->handler = buf[2];
+ }
break;
}
}
@@ -437,6 +443,7 @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src,
if (qcode >= ARRAY_SIZE(qcode_to_adb_keycode)) {
return;
}
+ /* FIXME: take handler into account when translating qcode */
keycode = qcode_to_adb_keycode[qcode];
if (keycode == NO_KEY) { /* We don't want to send this to the guest */
ADB_DPRINTF("Ignoring NO_KEY\n");
@@ -631,8 +638,21 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
d->devaddr = buf[1] & 0xf;
break;
default:
- /* XXX: check this */
d->devaddr = buf[1] & 0xf;
+ /* we support handlers:
+ * 0x01: Classic Apple Mouse Protocol / 100 cpi operations
+ * 0x02: Classic Apple Mouse Protocol / 200 cpi operations
+ * we don't support handlers (at least):
+ * 0x03: Mouse systems A3 trackball
+ * 0x04: Extended Apple Mouse Protocol
+ * 0x2f: Microspeed mouse
+ * 0x42: Macally
+ * 0x5f: Microspeed mouse
+ * 0x66: Microspeed mouse
+ */
+ if (buf[2] == 1 || buf[2] == 2) {
+ d->handler = buf[2];
+ }
break;
}
}
diff --git a/hw/intc/xics.c b/hw/intc/xics.c
index f40b00003a..095c16a300 100644
--- a/hw/intc/xics.c
+++ b/hw/intc/xics.c
@@ -35,6 +35,8 @@
#include "hw/ppc/xics.h"
#include "qemu/error-report.h"
#include "qapi/visitor.h"
+#include "monitor/monitor.h"
+#include "hw/intc/intc.h"
int xics_get_cpu_index_by_dt_id(int cpu_dt_id)
{
@@ -90,6 +92,47 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu)
}
}
+static void xics_common_pic_print_info(InterruptStatsProvider *obj,
+ Monitor *mon)
+{
+ XICSState *xics = XICS_COMMON(obj);
+ ICSState *ics;
+ uint32_t i;
+
+ for (i = 0; i < xics->nr_servers; i++) {
+ ICPState *icp = &xics->ss[i];
+
+ if (!icp->output) {
+ continue;
+ }
+ monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n",
+ i, icp->xirr, icp->xirr_owner,
+ icp->pending_priority, icp->mfrr);
+ }
+
+ QLIST_FOREACH(ics, &xics->ics, list) {
+ monitor_printf(mon, "ICS %4x..%4x %p\n",
+ ics->offset, ics->offset + ics->nr_irqs - 1, ics);
+
+ if (!ics->irqs) {
+ continue;
+ }
+
+ for (i = 0; i < ics->nr_irqs; i++) {
+ ICSIRQState *irq = ics->irqs + i;
+
+ if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) {
+ continue;
+ }
+ monitor_printf(mon, " %4x %s %02x %02x\n",
+ ics->offset + i,
+ (irq->flags & XICS_FLAGS_IRQ_LSI) ?
+ "LSI" : "MSI",
+ irq->priority, irq->status);
+ }
+ }
+}
+
/*
* XICS Common class - parent for emulated XICS and KVM-XICS
*/
@@ -140,6 +183,25 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name,
info->set_nr_irqs(xics, value, errp);
}
+void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers,
+ const char *typename, Error **errp)
+{
+ int i;
+
+ xics->nr_servers = nr_servers;
+
+ xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState));
+ for (i = 0; i < xics->nr_servers; i++) {
+ char name[32];
+ ICPState *icp = &xics->ss[i];
+
+ object_initialize(icp, sizeof(*icp), typename);
+ snprintf(name, sizeof(name), "icp[%d]", i);
+ object_property_add_child(OBJECT(xics), name, OBJECT(icp), errp);
+ icp->xics = xics;
+ }
+}
+
static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
@@ -155,7 +217,7 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
Error **errp)
{
XICSState *xics = XICS_COMMON(obj);
- XICSStateClass *info = XICS_COMMON_GET_CLASS(xics);
+ XICSStateClass *xsc = XICS_COMMON_GET_CLASS(xics);
Error *error = NULL;
int64_t value;
@@ -170,8 +232,8 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
return;
}
- assert(info->set_nr_servers);
- info->set_nr_servers(xics, value, errp);
+ assert(xsc->set_nr_servers);
+ xsc->set_nr_servers(xics, value, errp);
}
static void xics_common_initfn(Object *obj)
@@ -190,8 +252,10 @@ static void xics_common_initfn(Object *obj)
static void xics_common_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc);
dc->reset = xics_common_reset;
+ ic->print_info = xics_common_pic_print_info;
}
static const TypeInfo xics_common_info = {
@@ -201,6 +265,10 @@ static const TypeInfo xics_common_info = {
.class_size = sizeof(XICSStateClass),
.instance_init = xics_common_initfn,
.class_init = xics_common_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_INTERRUPT_STATS_PROVIDER },
+ { }
+ },
};
/*
@@ -258,22 +326,20 @@ static void icp_check_ipi(ICPState *ss)
qemu_irq_raise(ss->output);
}
-static void icp_resend(XICSState *xics, int server)
+static void icp_resend(ICPState *ss)
{
- ICPState *ss = xics->ss + server;
ICSState *ics;
if (ss->mfrr < CPPR(ss)) {
icp_check_ipi(ss);
}
- QLIST_FOREACH(ics, &xics->ics, list) {
+ QLIST_FOREACH(ics, &ss->xics->ics, list) {
ics_resend(ics);
}
}
-void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
+void icp_set_cppr(ICPState *ss, uint8_t cppr)
{
- ICPState *ss = xics->ss + server;
uint8_t old_cppr;
uint32_t old_xisr;
@@ -293,15 +359,13 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr)
}
} else {
if (!XISR(ss)) {
- icp_resend(xics, server);
+ icp_resend(ss);
}
}
}
-void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr)
+void icp_set_mfrr(ICPState *ss, uint8_t mfrr)
{
- ICPState *ss = xics->ss + server;
-
ss->mfrr = mfrr;
if (mfrr < CPPR(ss)) {
icp_check_ipi(ss);
@@ -330,23 +394,22 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr)
return ss->xirr;
}
-void icp_eoi(XICSState *xics, int server, uint32_t xirr)
+void icp_eoi(ICPState *ss, uint32_t xirr)
{
- ICPState *ss = xics->ss + server;
ICSState *ics;
uint32_t irq;
/* Send EOI -> ICS */
ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK);
- trace_xics_icp_eoi(server, xirr, ss->xirr);
+ trace_xics_icp_eoi(ss->cs->cpu_index, xirr, ss->xirr);
irq = xirr & XISR_MASK;
- QLIST_FOREACH(ics, &xics->ics, list) {
+ QLIST_FOREACH(ics, &ss->xics->ics, list) {
if (ics_valid_irq(ics, irq)) {
ics_eoi(ics, irq);
}
}
if (!XISR(ss)) {
- icp_resend(xics, server);
+ icp_resend(ss);
}
}
@@ -605,7 +668,7 @@ static int ics_simple_post_load(ICSState *ics, int version_id)
int i;
for (i = 0; i < ics->xics->nr_servers; i++) {
- icp_resend(ics->xics, i);
+ icp_resend(&ics->xics->ss[i]);
}
return 0;
diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c
index 9c2f198fd1..17694eaa87 100644
--- a/hw/intc/xics_kvm.c
+++ b/hw/intc/xics_kvm.c
@@ -373,18 +373,7 @@ static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
static void xics_kvm_set_nr_servers(XICSState *xics, uint32_t nr_servers,
Error **errp)
{
- int i;
-
- xics->nr_servers = nr_servers;
-
- xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState));
- for (i = 0; i < xics->nr_servers; i++) {
- char buffer[32];
- object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_KVM_ICP);
- snprintf(buffer, sizeof(buffer), "icp[%d]", i);
- object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]),
- errp);
- }
+ xics_set_nr_servers(xics, nr_servers, TYPE_KVM_ICP, errp);
}
static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr,
diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c
index e8d0623c2c..2e3f1c5e95 100644
--- a/hw/intc/xics_spapr.c
+++ b/hw/intc/xics_spapr.c
@@ -32,6 +32,7 @@
#include "qemu/timer.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/xics.h"
+#include "hw/ppc/fdt.h"
#include "qapi/visitor.h"
#include "qapi/error.h"
@@ -43,9 +44,10 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
+ ICPState *icp = &spapr->xics->ss[cs->cpu_index];
target_ulong cppr = args[0];
- icp_set_cppr(spapr->xics, cs->cpu_index, cppr);
+ icp_set_cppr(icp, cppr);
return H_SUCCESS;
}
@@ -59,7 +61,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return H_PARAMETER;
}
- icp_set_mfrr(spapr->xics, server, mfrr);
+ icp_set_mfrr(spapr->xics->ss + server, mfrr);
return H_SUCCESS;
}
@@ -67,7 +69,8 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
- uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index);
+ ICPState *icp = &spapr->xics->ss[cs->cpu_index];
+ uint32_t xirr = icp_accept(icp);
args[0] = xirr;
return H_SUCCESS;
@@ -77,8 +80,8 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
- ICPState *ss = &spapr->xics->ss[cs->cpu_index];
- uint32_t xirr = icp_accept(ss);
+ ICPState *icp = &spapr->xics->ss[cs->cpu_index];
+ uint32_t xirr = icp_accept(icp);
args[0] = xirr;
args[1] = cpu_get_host_ticks();
@@ -89,9 +92,10 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
+ ICPState *icp = &spapr->xics->ss[cs->cpu_index];
target_ulong xirr = args[0];
- icp_eoi(spapr->xics, cs->cpu_index, xirr);
+ icp_eoi(icp, xirr);
return H_SUCCESS;
}
@@ -99,8 +103,9 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
+ ICPState *icp = &spapr->xics->ss[cs->cpu_index];
uint32_t mfrr;
- uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr);
+ uint32_t xirr = icp_ipoll(icp, &mfrr);
args[0] = xirr;
args[1] = mfrr;
@@ -249,18 +254,7 @@ static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs,
static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers,
Error **errp)
{
- int i;
-
- xics->nr_servers = nr_servers;
-
- xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState));
- for (i = 0; i < xics->nr_servers; i++) {
- char buffer[32];
- object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_ICP);
- snprintf(buffer, sizeof(buffer), "icp[%d]", i);
- object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]),
- errp);
- }
+ xics_set_nr_servers(xics, nr_servers, TYPE_ICP, errp);
}
static void xics_spapr_realize(DeviceState *dev, Error **errp)
@@ -456,6 +450,27 @@ void xics_spapr_free(XICSState *xics, int irq, int num)
}
}
+void spapr_dt_xics(XICSState *xics, void *fdt, uint32_t phandle)
+{
+ uint32_t interrupt_server_ranges_prop[] = {
+ 0, cpu_to_be32(xics->nr_servers),
+ };
+ int node;
+
+ _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller"));
+
+ _FDT(fdt_setprop_string(fdt, node, "device_type",
+ "PowerPC-External-Interrupt-Presentation"));
+ _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,ppc-xicp"));
+ _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0));
+ _FDT(fdt_setprop(fdt, node, "ibm,interrupt-server-ranges",
+ interrupt_server_ranges_prop,
+ sizeof(interrupt_server_ranges_prop)));
+ _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2));
+ _FDT(fdt_setprop_cell(fdt, node, "linux,phandle", phandle));
+ _FDT(fdt_setprop_cell(fdt, node, "phandle", phandle));
+}
+
static void xics_spapr_register_types(void)
{
type_register_static(&xics_spapr_info);
diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs
index e9a66940e0..c018f6b2ff 100644
--- a/hw/nvram/Makefile.objs
+++ b/hw/nvram/Makefile.objs
@@ -1,5 +1,6 @@
common-obj-$(CONFIG_DS1225Y) += ds1225y.o
common-obj-y += eeprom93xx.o
common-obj-y += fw_cfg.o
+common-obj-y += chrp_nvram.o
common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
obj-$(CONFIG_PSERIES) += spapr_nvram.o
diff --git a/hw/nvram/chrp_nvram.c b/hw/nvram/chrp_nvram.c
new file mode 100644
index 0000000000..3837510dd2
--- /dev/null
+++ b/hw/nvram/chrp_nvram.c
@@ -0,0 +1,85 @@
+/*
+ * Common Hardware Reference Platform NVRAM helper functions.
+ *
+ * The CHRP NVRAM layout is used by OpenBIOS and SLOF. See CHRP
+ * specification, chapter 8, or the LoPAPR specification for details
+ * about the NVRAM layout.
+ *
+ * This code 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/cutils.h"
+#include "hw/hw.h"
+#include "hw/nvram/chrp_nvram.h"
+#include "sysemu/sysemu.h"
+
+static int chrp_nvram_set_var(uint8_t *nvram, int addr, const char *str)
+{
+ int len;
+
+ len = strlen(str) + 1;
+ memcpy(&nvram[addr], str, len);
+
+ return addr + len;
+}
+
+/**
+ * Create a "system partition", used for the Open Firmware
+ * environment variables.
+ */
+int chrp_nvram_create_system_partition(uint8_t *data, int min_len)
+{
+ ChrpNvramPartHdr *part_header;
+ unsigned int i;
+ int end;
+
+ part_header = (ChrpNvramPartHdr *)data;
+ part_header->signature = CHRP_NVPART_SYSTEM;
+ pstrcpy(part_header->name, sizeof(part_header->name), "system");
+
+ end = sizeof(ChrpNvramPartHdr);
+ for (i = 0; i < nb_prom_envs; i++) {
+ end = chrp_nvram_set_var(data, end, prom_envs[i]);
+ }
+
+ /* End marker */
+ data[end++] = '\0';
+
+ end = (end + 15) & ~15;
+ /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for
+ new variables. */
+ if (end < min_len) {
+ end = min_len;
+ }
+ chrp_nvram_finish_partition(part_header, end);
+
+ return end;
+}
+
+/**
+ * Create a "free space" partition
+ */
+int chrp_nvram_create_free_partition(uint8_t *data, int len)
+{
+ ChrpNvramPartHdr *part_header;
+
+ part_header = (ChrpNvramPartHdr *)data;
+ part_header->signature = CHRP_NVPART_FREE;
+ pstrcpy(part_header->name, sizeof(part_header->name), "free");
+
+ chrp_nvram_finish_partition(part_header, len);
+
+ return len;
+}
diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c
index 24f61212ba..63f9ed1d82 100644
--- a/hw/nvram/mac_nvram.c
+++ b/hw/nvram/mac_nvram.c
@@ -24,8 +24,7 @@
*/
#include "qemu/osdep.h"
#include "hw/hw.h"
-#include "hw/nvram/openbios_firmware_abi.h"
-#include "sysemu/sysemu.h"
+#include "hw/nvram/chrp_nvram.h"
#include "hw/ppc/mac.h"
#include "qemu/cutils.h"
#include <zlib.h>
@@ -146,38 +145,14 @@ static void macio_nvram_register_types(void)
static void pmac_format_nvram_partition_of(MacIONVRAMState *nvr, int off,
int len)
{
- unsigned int i;
- uint32_t start = off, end;
- struct OpenBIOS_nvpart_v1 *part_header;
-
- // OpenBIOS nvram variables
- // Variable partition
- part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start];
- part_header->signature = OPENBIOS_PART_SYSTEM;
- pstrcpy(part_header->name, sizeof(part_header->name), "system");
-
- end = start + sizeof(struct OpenBIOS_nvpart_v1);
- for (i = 0; i < nb_prom_envs; i++)
- end = OpenBIOS_set_var(nvr->data, end, prom_envs[i]);
-
- // End marker
- nvr->data[end++] = '\0';
-
- end = start + ((end - start + 15) & ~15);
- /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for
- new variables. */
- if (end < DEF_SYSTEM_SIZE)
- end = DEF_SYSTEM_SIZE;
- OpenBIOS_finish_partition(part_header, end - start);
-
- // free partition
- start = end;
- part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start];
- part_header->signature = OPENBIOS_PART_FREE;
- pstrcpy(part_header->name, sizeof(part_header->name), "free");
-
- end = len;
- OpenBIOS_finish_partition(part_header, end - start);
+ int sysp_end;
+
+ /* OpenBIOS nvram variables partition */
+ sysp_end = chrp_nvram_create_system_partition(&nvr->data[off],
+ DEF_SYSTEM_SIZE) + off;
+
+ /* Free space partition */
+ chrp_nvram_create_free_partition(&nvr->data[sysp_end], len - sysp_end);
}
#define OSX_NVRAM_SIGNATURE (0x5A)
@@ -187,15 +162,15 @@ static void pmac_format_nvram_partition_osx(MacIONVRAMState *nvr, int off,
int len)
{
uint32_t start = off;
- struct OpenBIOS_nvpart_v1 *part_header;
+ ChrpNvramPartHdr *part_header;
unsigned char *data = &nvr->data[start];
/* empty partition */
- part_header = (struct OpenBIOS_nvpart_v1 *)data;
+ part_header = (ChrpNvramPartHdr *)data;
part_header->signature = OSX_NVRAM_SIGNATURE;
pstrcpy(part_header->name, sizeof(part_header->name), "wwwwwwwwwwww");
- OpenBIOS_finish_partition(part_header, len);
+ chrp_nvram_finish_partition(part_header, len);
/* Generation */
stl_be_p(&data[20], 2);
diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c
index 4de5f705d8..eb42ea323f 100644
--- a/hw/nvram/spapr_nvram.c
+++ b/hw/nvram/spapr_nvram.c
@@ -31,6 +31,7 @@
#include "sysemu/block-backend.h"
#include "sysemu/device_tree.h"
#include "hw/sysbus.h"
+#include "hw/nvram/chrp_nvram.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
@@ -162,6 +163,11 @@ static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp)
error_setg(errp, "can't read spapr-nvram contents");
return;
}
+ } else if (nb_prom_envs > 0) {
+ /* Create a system partition to pass the -prom-env variables */
+ chrp_nvram_create_system_partition(nvram->buf, MIN_NVRAM_SIZE / 4);
+ chrp_nvram_create_free_partition(&nvram->buf[MIN_NVRAM_SIZE / 4],
+ nvram->size - MIN_NVRAM_SIZE / 4);
}
spapr_rtas_register(RTAS_NVRAM_FETCH, "nvram-fetch", rtas_nvram_fetch);
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs
index 99a0d4e581..8025129377 100644
--- a/hw/ppc/Makefile.objs
+++ b/hw/ppc/Makefile.objs
@@ -4,7 +4,9 @@ obj-y += ppc.o ppc_booke.o fdt.o
obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o
obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
-obj-$(CONFIG_PSERIES) += spapr_cpu_core.o
+obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
+# IBM PowerNV
+obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o
ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o
endif
diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c
new file mode 100644
index 0000000000..82276e0857
--- /dev/null
+++ b/hw/ppc/pnv.c
@@ -0,0 +1,819 @@
+/*
+ * QEMU PowerPC PowerNV machine model
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * 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 "qapi/error.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/numa.h"
+#include "hw/hw.h"
+#include "target-ppc/cpu.h"
+#include "qemu/log.h"
+#include "hw/ppc/fdt.h"
+#include "hw/ppc/ppc.h"
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_core.h"
+#include "hw/loader.h"
+#include "exec/address-spaces.h"
+#include "qemu/cutils.h"
+#include "qapi/visitor.h"
+
+#include "hw/ppc/pnv_xscom.h"
+
+#include "hw/isa/isa.h"
+#include "hw/char/serial.h"
+#include "hw/timer/mc146818rtc.h"
+
+#include <libfdt.h>
+
+#define FDT_MAX_SIZE 0x00100000
+
+#define FW_FILE_NAME "skiboot.lid"
+#define FW_LOAD_ADDR 0x0
+#define FW_MAX_SIZE 0x00400000
+
+#define KERNEL_LOAD_ADDR 0x20000000
+#define INITRD_LOAD_ADDR 0x40000000
+
+/*
+ * On Power Systems E880 (POWER8), the max cpus (threads) should be :
+ * 4 * 4 sockets * 12 cores * 8 threads = 1536
+ * Let's make it 2^11
+ */
+#define MAX_CPUS 2048
+
+/*
+ * Memory nodes are created by hostboot, one for each range of memory
+ * that has a different "affinity". In practice, it means one range
+ * per chip.
+ */
+static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
+ hwaddr size)
+{
+ char *mem_name;
+ uint64_t mem_reg_property[2];
+ int off;
+
+ mem_reg_property[0] = cpu_to_be64(start);
+ mem_reg_property[1] = cpu_to_be64(size);
+
+ mem_name = g_strdup_printf("memory@%"HWADDR_PRIx, start);
+ off = fdt_add_subnode(fdt, 0, mem_name);
+ g_free(mem_name);
+
+ _FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
+ _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
+ sizeof(mem_reg_property))));
+ _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id)));
+}
+
+static int get_cpus_node(void *fdt)
+{
+ int cpus_offset = fdt_path_offset(fdt, "/cpus");
+
+ if (cpus_offset < 0) {
+ cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
+ "cpus");
+ if (cpus_offset) {
+ _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1)));
+ _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0)));
+ }
+ }
+ _FDT(cpus_offset);
+ return cpus_offset;
+}
+
+/*
+ * The PowerNV cores (and threads) need to use real HW ids and not an
+ * incremental index like it has been done on other platforms. This HW
+ * id is stored in the CPU PIR, it is used to create cpu nodes in the
+ * device tree, used in XSCOM to address cores and in interrupt
+ * servers.
+ */
+static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
+{
+ CPUState *cs = CPU(DEVICE(pc->threads));
+ DeviceClass *dc = DEVICE_GET_CLASS(cs);
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ int smt_threads = ppc_get_compat_smt_threads(cpu);
+ CPUPPCState *env = &cpu->env;
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
+ uint32_t servers_prop[smt_threads];
+ int i;
+ uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
+ 0xffffffff, 0xffffffff};
+ uint32_t tbfreq = PNV_TIMEBASE_FREQ;
+ uint32_t cpufreq = 1000000000;
+ uint32_t page_sizes_prop[64];
+ size_t page_sizes_prop_size;
+ const uint8_t pa_features[] = { 24, 0,
+ 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 };
+ int offset;
+ char *nodename;
+ int cpus_offset = get_cpus_node(fdt);
+
+ nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir);
+ offset = fdt_add_subnode(fdt, cpus_offset, nodename);
+ _FDT(offset);
+ g_free(nodename);
+
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id)));
+
+ _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir)));
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir)));
+ _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu")));
+
+ _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR])));
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size",
+ env->dcache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size",
+ env->dcache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size",
+ env->icache_line_size)));
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size",
+ env->icache_line_size)));
+
+ if (pcc->l1_dcache_size) {
+ _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size",
+ pcc->l1_dcache_size)));
+ } else {
+ error_report("Warning: Unknown L1 dcache size for cpu");
+ }
+ if (pcc->l1_icache_size) {
+ _FDT((fdt_setprop_cell(fdt, offset, "i-cache-size",
+ pcc->l1_icache_size)));
+ } else {
+ error_report("Warning: Unknown L1 icache size for cpu");
+ }
+
+ _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
+ _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr)));
+ _FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
+ _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
+
+ if (env->spr_cb[SPR_PURR].oea_read) {
+ _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0)));
+ }
+
+ if (env->mmu_model & POWERPC_MMU_1TSEG) {
+ _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes",
+ segs, sizeof(segs))));
+ }
+
+ /* Advertise VMX/VSX (vector extensions) if available
+ * 0 / no property == no vector extensions
+ * 1 == VMX / Altivec available
+ * 2 == VSX available */
+ if (env->insns_flags & PPC_ALTIVEC) {
+ uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
+
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx)));
+ }
+
+ /* Advertise DFP (Decimal Floating Point) if available
+ * 0 / no property == no DFP
+ * 1 == DFP available */
+ if (env->insns_flags2 & PPC2_DFP) {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
+ }
+
+ page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop,
+ sizeof(page_sizes_prop));
+ if (page_sizes_prop_size) {
+ _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes",
+ page_sizes_prop, page_sizes_prop_size)));
+ }
+
+ _FDT((fdt_setprop(fdt, offset, "ibm,pa-features",
+ pa_features, sizeof(pa_features))));
+
+ if (cpu->cpu_version) {
+ _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version)));
+ }
+
+ /* Build interrupt servers properties */
+ for (i = 0; i < smt_threads; i++) {
+ servers_prop[i] = cpu_to_be32(pc->pir + i);
+ }
+ _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s",
+ servers_prop, sizeof(servers_prop))));
+}
+
+static void powernv_populate_chip(PnvChip *chip, void *fdt)
+{
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+ char *typename = pnv_core_typename(pcc->cpu_model);
+ size_t typesize = object_type_get_instance_size(typename);
+ int i;
+
+ pnv_xscom_populate(chip, fdt, 0);
+
+ for (i = 0; i < chip->nr_cores; i++) {
+ PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
+
+ powernv_create_core_node(chip, pnv_core, fdt);
+ }
+
+ if (chip->ram_size) {
+ powernv_populate_memory_node(fdt, chip->chip_id, chip->ram_start,
+ chip->ram_size);
+ }
+ g_free(typename);
+}
+
+static void *powernv_create_fdt(MachineState *machine)
+{
+ const char plat_compat[] = "qemu,powernv\0ibm,powernv";
+ PnvMachineState *pnv = POWERNV_MACHINE(machine);
+ void *fdt;
+ char *buf;
+ int off;
+ int i;
+
+ fdt = g_malloc0(FDT_MAX_SIZE);
+ _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
+
+ /* Root node */
+ _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2)));
+ _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2)));
+ _FDT((fdt_setprop_string(fdt, 0, "model",
+ "IBM PowerNV (emulated by qemu)")));
+ _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat,
+ sizeof(plat_compat))));
+
+ buf = qemu_uuid_unparse_strdup(&qemu_uuid);
+ _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf)));
+ if (qemu_uuid_set) {
+ _FDT((fdt_property_string(fdt, "system-id", buf)));
+ }
+ g_free(buf);
+
+ off = fdt_add_subnode(fdt, 0, "chosen");
+ if (machine->kernel_cmdline) {
+ _FDT((fdt_setprop_string(fdt, off, "bootargs",
+ machine->kernel_cmdline)));
+ }
+
+ if (pnv->initrd_size) {
+ uint32_t start_prop = cpu_to_be32(pnv->initrd_base);
+ uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size);
+
+ _FDT((fdt_setprop(fdt, off, "linux,initrd-start",
+ &start_prop, sizeof(start_prop))));
+ _FDT((fdt_setprop(fdt, off, "linux,initrd-end",
+ &end_prop, sizeof(end_prop))));
+ }
+
+ /* Populate device tree for each chip */
+ for (i = 0; i < pnv->num_chips; i++) {
+ powernv_populate_chip(pnv->chips[i], fdt);
+ }
+ return fdt;
+}
+
+static void ppc_powernv_reset(void)
+{
+ MachineState *machine = MACHINE(qdev_get_machine());
+ void *fdt;
+
+ qemu_devices_reset();
+
+ fdt = powernv_create_fdt(machine);
+
+ /* Pack resulting tree */
+ _FDT((fdt_pack(fdt)));
+
+ cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
+}
+
+/* If we don't use the built-in LPC interrupt deserializer, we need
+ * to provide a set of qirqs for the ISA bus or things will go bad.
+ *
+ * Most machines using pre-Naples chips (without said deserializer)
+ * have a CPLD that will collect the SerIRQ and shoot them as a
+ * single level interrupt to the P8 chip. So let's setup a hook
+ * for doing just that.
+ *
+ * Note: The actual interrupt input isn't emulated yet, this will
+ * come with the PSI bridge model.
+ */
+static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
+{
+ /* We don't yet emulate the PSI bridge which provides the external
+ * interrupt, so just drop interrupts on the floor
+ */
+}
+
+static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
+{
+ /* XXX TODO */
+}
+
+static ISABus *pnv_isa_create(PnvChip *chip)
+{
+ PnvLpcController *lpc = &chip->lpc;
+ ISABus *isa_bus;
+ qemu_irq *irqs;
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+ /* let isa_bus_new() create its own bridge on SysBus otherwise
+ * devices speficied on the command line won't find the bus and
+ * will fail to create.
+ */
+ isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
+ &error_fatal);
+
+ /* Not all variants have a working serial irq decoder. If not,
+ * handling of LPC interrupts becomes a platform issue (some
+ * platforms have a CPLD to do it).
+ */
+ if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
+ irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS);
+ } else {
+ irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip,
+ ISA_NUM_IRQS);
+ }
+
+ isa_bus_irqs(isa_bus, irqs);
+ return isa_bus;
+}
+
+static void ppc_powernv_init(MachineState *machine)
+{
+ PnvMachineState *pnv = POWERNV_MACHINE(machine);
+ MemoryRegion *ram;
+ char *fw_filename;
+ long fw_size;
+ int i;
+ char *chip_typename;
+
+ /* allocate RAM */
+ if (machine->ram_size < (1 * G_BYTE)) {
+ error_report("Warning: skiboot may not work with < 1GB of RAM");
+ }
+
+ ram = g_new(MemoryRegion, 1);
+ memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram",
+ machine->ram_size);
+ memory_region_add_subregion(get_system_memory(), 0, ram);
+
+ /* load skiboot firmware */
+ if (bios_name == NULL) {
+ bios_name = FW_FILE_NAME;
+ }
+
+ fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+
+ fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
+ if (fw_size < 0) {
+ hw_error("qemu: could not load OPAL '%s'\n", fw_filename);
+ exit(1);
+ }
+ g_free(fw_filename);
+
+ /* load kernel */
+ if (machine->kernel_filename) {
+ long kernel_size;
+
+ kernel_size = load_image_targphys(machine->kernel_filename,
+ KERNEL_LOAD_ADDR, 0x2000000);
+ if (kernel_size < 0) {
+ hw_error("qemu: could not load kernel'%s'\n",
+ machine->kernel_filename);
+ exit(1);
+ }
+ }
+
+ /* load initrd */
+ if (machine->initrd_filename) {
+ pnv->initrd_base = INITRD_LOAD_ADDR;
+ pnv->initrd_size = load_image_targphys(machine->initrd_filename,
+ pnv->initrd_base, 0x10000000); /* 128MB max */
+ if (pnv->initrd_size < 0) {
+ error_report("qemu: could not load initial ram disk '%s'",
+ machine->initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* We need some cpu model to instantiate the PnvChip class */
+ if (machine->cpu_model == NULL) {
+ machine->cpu_model = "POWER8";
+ }
+
+ /* Create the processor chips */
+ chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
+ if (!object_class_by_name(chip_typename)) {
+ error_report("qemu: invalid CPU model '%s' for %s machine",
+ machine->cpu_model, MACHINE_GET_CLASS(machine)->name);
+ exit(1);
+ }
+
+ pnv->chips = g_new0(PnvChip *, pnv->num_chips);
+ for (i = 0; i < pnv->num_chips; i++) {
+ char chip_name[32];
+ Object *chip = object_new(chip_typename);
+
+ pnv->chips[i] = PNV_CHIP(chip);
+
+ /* TODO: put all the memory in one node on chip 0 until we find a
+ * way to specify different ranges for each chip
+ */
+ if (i == 0) {
+ object_property_set_int(chip, machine->ram_size, "ram-size",
+ &error_fatal);
+ }
+
+ snprintf(chip_name, sizeof(chip_name), "chip[%d]", PNV_CHIP_HWID(i));
+ object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal);
+ object_property_set_int(chip, PNV_CHIP_HWID(i), "chip-id",
+ &error_fatal);
+ object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal);
+ object_property_set_bool(chip, true, "realized", &error_fatal);
+ }
+ g_free(chip_typename);
+
+ /* Instantiate ISA bus on chip 0 */
+ pnv->isa_bus = pnv_isa_create(pnv->chips[0]);
+
+ /* Create serial port */
+ serial_hds_isa_init(pnv->isa_bus, 0, MAX_SERIAL_PORTS);
+
+ /* Create an RTC ISA device too */
+ rtc_init(pnv->isa_bus, 2000, NULL);
+}
+
+/*
+ * 0:21 Reserved - Read as zeros
+ * 22:24 Chip ID
+ * 25:28 Core number
+ * 29:31 Thread ID
+ */
+static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id)
+{
+ return (chip->chip_id << 7) | (core_id << 3);
+}
+
+/*
+ * 0:48 Reserved - Read as zeroes
+ * 49:52 Node ID
+ * 53:55 Chip ID
+ * 56 Reserved - Read as zero
+ * 57:61 Core number
+ * 62:63 Thread ID
+ *
+ * We only care about the lower bits. uint32_t is fine for the moment.
+ */
+static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id)
+{
+ return (chip->chip_id << 8) | (core_id << 2);
+}
+
+/* Allowed core identifiers on a POWER8 Processor Chip :
+ *
+ * <EX0 reserved>
+ * EX1 - Venice only
+ * EX2 - Venice only
+ * EX3 - Venice only
+ * EX4
+ * EX5
+ * EX6
+ * <EX7,8 reserved> <reserved>
+ * EX9 - Venice only
+ * EX10 - Venice only
+ * EX11 - Venice only
+ * EX12
+ * EX13
+ * EX14
+ * <EX15 reserved>
+ */
+#define POWER8E_CORE_MASK (0x7070ull)
+#define POWER8_CORE_MASK (0x7e7eull)
+
+/*
+ * POWER9 has 24 cores, ids starting at 0x20
+ */
+#define POWER9_CORE_MASK (0xffffff00000000ull)
+
+static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PnvChipClass *k = PNV_CHIP_CLASS(klass);
+
+ k->cpu_model = "POWER8E";
+ k->chip_type = PNV_CHIP_POWER8E;
+ k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */
+ k->cores_mask = POWER8E_CORE_MASK;
+ k->core_pir = pnv_chip_core_pir_p8;
+ k->xscom_base = 0x003fc0000000000ull;
+ dc->desc = "PowerNV Chip POWER8E";
+}
+
+static const TypeInfo pnv_chip_power8e_info = {
+ .name = TYPE_PNV_CHIP_POWER8E,
+ .parent = TYPE_PNV_CHIP,
+ .instance_size = sizeof(PnvChip),
+ .class_init = pnv_chip_power8e_class_init,
+};
+
+static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PnvChipClass *k = PNV_CHIP_CLASS(klass);
+
+ k->cpu_model = "POWER8";
+ k->chip_type = PNV_CHIP_POWER8;
+ k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */
+ k->cores_mask = POWER8_CORE_MASK;
+ k->core_pir = pnv_chip_core_pir_p8;
+ k->xscom_base = 0x003fc0000000000ull;
+ dc->desc = "PowerNV Chip POWER8";
+}
+
+static const TypeInfo pnv_chip_power8_info = {
+ .name = TYPE_PNV_CHIP_POWER8,
+ .parent = TYPE_PNV_CHIP,
+ .instance_size = sizeof(PnvChip),
+ .class_init = pnv_chip_power8_class_init,
+};
+
+static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PnvChipClass *k = PNV_CHIP_CLASS(klass);
+
+ k->cpu_model = "POWER8NVL";
+ k->chip_type = PNV_CHIP_POWER8NVL;
+ k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */
+ k->cores_mask = POWER8_CORE_MASK;
+ k->core_pir = pnv_chip_core_pir_p8;
+ k->xscom_base = 0x003fc0000000000ull;
+ dc->desc = "PowerNV Chip POWER8NVL";
+}
+
+static const TypeInfo pnv_chip_power8nvl_info = {
+ .name = TYPE_PNV_CHIP_POWER8NVL,
+ .parent = TYPE_PNV_CHIP,
+ .instance_size = sizeof(PnvChip),
+ .class_init = pnv_chip_power8nvl_class_init,
+};
+
+static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PnvChipClass *k = PNV_CHIP_CLASS(klass);
+
+ k->cpu_model = "POWER9";
+ k->chip_type = PNV_CHIP_POWER9;
+ k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
+ k->cores_mask = POWER9_CORE_MASK;
+ k->core_pir = pnv_chip_core_pir_p9;
+ k->xscom_base = 0x00603fc00000000ull;
+ dc->desc = "PowerNV Chip POWER9";
+}
+
+static const TypeInfo pnv_chip_power9_info = {
+ .name = TYPE_PNV_CHIP_POWER9,
+ .parent = TYPE_PNV_CHIP,
+ .instance_size = sizeof(PnvChip),
+ .class_init = pnv_chip_power9_class_init,
+};
+
+static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
+{
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+ int cores_max;
+
+ /*
+ * No custom mask for this chip, let's use the default one from *
+ * the chip class
+ */
+ if (!chip->cores_mask) {
+ chip->cores_mask = pcc->cores_mask;
+ }
+
+ /* filter alien core ids ! some are reserved */
+ if ((chip->cores_mask & pcc->cores_mask) != chip->cores_mask) {
+ error_setg(errp, "warning: invalid core mask for chip Ox%"PRIx64" !",
+ chip->cores_mask);
+ return;
+ }
+ chip->cores_mask &= pcc->cores_mask;
+
+ /* now that we have a sane layout, let check the number of cores */
+ cores_max = hweight_long(chip->cores_mask);
+ if (chip->nr_cores > cores_max) {
+ error_setg(errp, "warning: too many cores for chip ! Limit is %d",
+ cores_max);
+ return;
+ }
+}
+
+static void pnv_chip_init(Object *obj)
+{
+ PnvChip *chip = PNV_CHIP(obj);
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+ chip->xscom_base = pcc->xscom_base;
+
+ object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
+ object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
+}
+
+static void pnv_chip_realize(DeviceState *dev, Error **errp)
+{
+ PnvChip *chip = PNV_CHIP(dev);
+ Error *error = NULL;
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+ char *typename = pnv_core_typename(pcc->cpu_model);
+ size_t typesize = object_type_get_instance_size(typename);
+ int i, core_hwid;
+
+ if (!object_class_by_name(typename)) {
+ error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename);
+ return;
+ }
+
+ /* XSCOM bridge */
+ pnv_xscom_realize(chip, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip));
+
+ /* Cores */
+ pnv_chip_core_sanitize(chip, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+
+ chip->cores = g_malloc0(typesize * chip->nr_cores);
+
+ for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8)
+ && (i < chip->nr_cores); core_hwid++) {
+ char core_name[32];
+ void *pnv_core = chip->cores + i * typesize;
+
+ if (!(chip->cores_mask & (1ull << core_hwid))) {
+ continue;
+ }
+
+ object_initialize(pnv_core, typesize, typename);
+ snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid);
+ object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core),
+ &error_fatal);
+ object_property_set_int(OBJECT(pnv_core), smp_threads, "nr-threads",
+ &error_fatal);
+ object_property_set_int(OBJECT(pnv_core), core_hwid,
+ CPU_CORE_PROP_CORE_ID, &error_fatal);
+ object_property_set_int(OBJECT(pnv_core),
+ pcc->core_pir(chip, core_hwid),
+ "pir", &error_fatal);
+ object_property_set_bool(OBJECT(pnv_core), true, "realized",
+ &error_fatal);
+ object_unref(OBJECT(pnv_core));
+
+ /* Each core has an XSCOM MMIO region */
+ pnv_xscom_add_subregion(chip, PNV_XSCOM_EX_CORE_BASE(core_hwid),
+ &PNV_CORE(pnv_core)->xscom_regs);
+ i++;
+ }
+ g_free(typename);
+
+ /* Create LPC controller */
+ object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
+ &error_fatal);
+ pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs);
+}
+
+static Property pnv_chip_properties[] = {
+ DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0),
+ DEFINE_PROP_UINT64("ram-start", PnvChip, ram_start, 0),
+ DEFINE_PROP_UINT64("ram-size", PnvChip, ram_size, 0),
+ DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1),
+ DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pnv_chip_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = pnv_chip_realize;
+ dc->props = pnv_chip_properties;
+ dc->desc = "PowerNV Chip";
+}
+
+static const TypeInfo pnv_chip_info = {
+ .name = TYPE_PNV_CHIP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .class_init = pnv_chip_class_init,
+ .instance_init = pnv_chip_init,
+ .class_size = sizeof(PnvChipClass),
+ .abstract = true,
+};
+
+static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ visit_type_uint32(v, name, &POWERNV_MACHINE(obj)->num_chips, errp);
+}
+
+static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ PnvMachineState *pnv = POWERNV_MACHINE(obj);
+ uint32_t num_chips;
+ Error *local_err = NULL;
+
+ visit_type_uint32(v, name, &num_chips, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ /*
+ * TODO: should we decide on how many chips we can create based
+ * on #cores and Venice vs. Murano vs. Naples chip type etc...,
+ */
+ if (!is_power_of_2(num_chips) || num_chips > 4) {
+ error_setg(errp, "invalid number of chips: '%d'", num_chips);
+ return;
+ }
+
+ pnv->num_chips = num_chips;
+}
+
+static void powernv_machine_initfn(Object *obj)
+{
+ PnvMachineState *pnv = POWERNV_MACHINE(obj);
+ pnv->num_chips = 1;
+}
+
+static void powernv_machine_class_props_init(ObjectClass *oc)
+{
+ object_class_property_add(oc, "num-chips", "uint32_t",
+ pnv_get_num_chips, pnv_set_num_chips,
+ NULL, NULL, NULL);
+ object_class_property_set_description(oc, "num-chips",
+ "Specifies the number of processor chips",
+ NULL);
+}
+
+static void powernv_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "IBM PowerNV (Non-Virtualized)";
+ mc->init = ppc_powernv_init;
+ mc->reset = ppc_powernv_reset;
+ mc->max_cpus = MAX_CPUS;
+ mc->block_default_type = IF_IDE; /* Pnv provides a AHCI device for
+ * storage */
+ mc->no_parallel = 1;
+ mc->default_boot_order = NULL;
+ mc->default_ram_size = 1 * G_BYTE;
+
+ powernv_machine_class_props_init(oc);
+}
+
+static const TypeInfo powernv_machine_info = {
+ .name = TYPE_POWERNV_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(PnvMachineState),
+ .instance_init = powernv_machine_initfn,
+ .class_init = powernv_machine_class_init,
+};
+
+static void powernv_machine_register_types(void)
+{
+ type_register_static(&powernv_machine_info);
+ type_register_static(&pnv_chip_info);
+ type_register_static(&pnv_chip_power8e_info);
+ type_register_static(&pnv_chip_power8_info);
+ type_register_static(&pnv_chip_power8nvl_info);
+ type_register_static(&pnv_chip_power9_info);
+}
+
+type_init(powernv_machine_register_types)
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
new file mode 100644
index 0000000000..2acda9637d
--- /dev/null
+++ b/hw/ppc/pnv_core.c
@@ -0,0 +1,232 @@
+/*
+ * QEMU PowerPC PowerNV CPU Core model
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * 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 "sysemu/sysemu.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "target-ppc/cpu.h"
+#include "hw/ppc/ppc.h"
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/pnv_core.h"
+
+static void powernv_cpu_reset(void *opaque)
+{
+ PowerPCCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+
+ cpu_reset(cs);
+
+ /*
+ * the skiboot firmware elects a primary thread to initialize the
+ * system and it can be any.
+ */
+ env->gpr[3] = PNV_FDT_ADDR;
+ env->nip = 0x10;
+ env->msr |= MSR_HVB; /* Hypervisor mode */
+}
+
+static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp)
+{
+ CPUPPCState *env = &cpu->env;
+ int core_pir;
+ int thread_index = 0; /* TODO: TCG supports only one thread */
+ ppc_spr_t *pir = &env->spr_cb[SPR_PIR];
+
+ core_pir = object_property_get_int(OBJECT(cpu), "core-pir", &error_abort);
+
+ /*
+ * The PIR of a thread is the core PIR + the thread index. We will
+ * need to find a way to get the thread index when TCG supports
+ * more than 1. We could use the object name ?
+ */
+ pir->default_value = core_pir + thread_index;
+
+ /* Set time-base frequency to 512 MHz */
+ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ);
+
+ qemu_register_reset(powernv_cpu_reset, cpu);
+}
+
+/*
+ * These values are read by the PowerNV HW monitors under Linux
+ */
+#define PNV_XSCOM_EX_DTS_RESULT0 0x50000
+#define PNV_XSCOM_EX_DTS_RESULT1 0x50001
+
+static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr,
+ unsigned int width)
+{
+ uint32_t offset = addr >> 3;
+ uint64_t val = 0;
+
+ /* The result should be 38 C */
+ switch (offset) {
+ case PNV_XSCOM_EX_DTS_RESULT0:
+ val = 0x26f024f023f0000ull;
+ break;
+ case PNV_XSCOM_EX_DTS_RESULT1:
+ val = 0x24f000000000000ull;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx,
+ addr);
+ }
+
+ return val;
+}
+
+static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned int width)
+{
+ qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx,
+ addr);
+}
+
+static const MemoryRegionOps pnv_core_xscom_ops = {
+ .read = pnv_core_xscom_read,
+ .write = pnv_core_xscom_write,
+ .valid.min_access_size = 8,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static void pnv_core_realize_child(Object *child, Error **errp)
+{
+ Error *local_err = NULL;
+ CPUState *cs = CPU(child);
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+
+ object_property_set_bool(child, true, "realized", &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ powernv_cpu_init(cpu, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+}
+
+static void pnv_core_realize(DeviceState *dev, Error **errp)
+{
+ PnvCore *pc = PNV_CORE(OBJECT(dev));
+ CPUCore *cc = CPU_CORE(OBJECT(dev));
+ PnvCoreClass *pcc = PNV_CORE_GET_CLASS(OBJECT(dev));
+ const char *typename = object_class_get_name(pcc->cpu_oc);
+ size_t size = object_type_get_instance_size(typename);
+ Error *local_err = NULL;
+ void *obj;
+ int i, j;
+ char name[32];
+
+ pc->threads = g_malloc0(size * cc->nr_threads);
+ for (i = 0; i < cc->nr_threads; i++) {
+ obj = pc->threads + i * size;
+
+ object_initialize(obj, size, typename);
+
+ snprintf(name, sizeof(name), "thread[%d]", i);
+ object_property_add_child(OBJECT(pc), name, obj, &local_err);
+ object_property_add_alias(obj, "core-pir", OBJECT(pc),
+ "pir", &local_err);
+ if (local_err) {
+ goto err;
+ }
+ object_unref(obj);
+ }
+
+ for (j = 0; j < cc->nr_threads; j++) {
+ obj = pc->threads + j * size;
+
+ pnv_core_realize_child(obj, &local_err);
+ if (local_err) {
+ goto err;
+ }
+ }
+
+ snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id);
+ pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops,
+ pc, name, PNV_XSCOM_EX_CORE_SIZE);
+ return;
+
+err:
+ while (--i >= 0) {
+ obj = pc->threads + i * size;
+ object_unparent(obj);
+ }
+ g_free(pc->threads);
+ error_propagate(errp, local_err);
+}
+
+static Property pnv_core_properties[] = {
+ DEFINE_PROP_UINT32("pir", PnvCore, pir, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pnv_core_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ PnvCoreClass *pcc = PNV_CORE_CLASS(oc);
+
+ dc->realize = pnv_core_realize;
+ dc->props = pnv_core_properties;
+ pcc->cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, data);
+}
+
+static const TypeInfo pnv_core_info = {
+ .name = TYPE_PNV_CORE,
+ .parent = TYPE_CPU_CORE,
+ .instance_size = sizeof(PnvCore),
+ .class_size = sizeof(PnvCoreClass),
+ .abstract = true,
+};
+
+static const char *pnv_core_models[] = {
+ "POWER8E", "POWER8", "POWER8NVL", "POWER9"
+};
+
+static void pnv_core_register_types(void)
+{
+ int i ;
+
+ type_register_static(&pnv_core_info);
+ for (i = 0; i < ARRAY_SIZE(pnv_core_models); ++i) {
+ TypeInfo ti = {
+ .parent = TYPE_PNV_CORE,
+ .instance_size = sizeof(PnvCore),
+ .class_init = pnv_core_class_init,
+ .class_data = (void *) pnv_core_models[i],
+ };
+ ti.name = pnv_core_typename(pnv_core_models[i]);
+ type_register(&ti);
+ g_free((void *)ti.name);
+ }
+}
+
+type_init(pnv_core_register_types)
+
+char *pnv_core_typename(const char *model)
+{
+ return g_strdup_printf(TYPE_PNV_CORE "-%s", model);
+}
diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c
new file mode 100644
index 0000000000..00dbd8b07b
--- /dev/null
+++ b/hw/ppc/pnv_lpc.c
@@ -0,0 +1,471 @@
+/*
+ * QEMU PowerPC PowerNV LPC controller
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * 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 "sysemu/sysemu.h"
+#include "target-ppc/cpu.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+
+#include "hw/ppc/pnv_lpc.h"
+#include "hw/ppc/pnv.h"
+#include "hw/ppc/fdt.h"
+
+#include <libfdt.h>
+
+enum {
+ ECCB_CTL = 0,
+ ECCB_RESET = 1,
+ ECCB_STAT = 2,
+ ECCB_DATA = 3,
+};
+
+/* OPB Master LS registers */
+#define OPB_MASTER_LS_IRQ_STAT 0x50
+#define OPB_MASTER_IRQ_LPC 0x00000800
+#define OPB_MASTER_LS_IRQ_MASK 0x54
+#define OPB_MASTER_LS_IRQ_POL 0x58
+#define OPB_MASTER_LS_IRQ_INPUT 0x5c
+
+/* LPC HC registers */
+#define LPC_HC_FW_SEG_IDSEL 0x24
+#define LPC_HC_FW_RD_ACC_SIZE 0x28
+#define LPC_HC_FW_RD_1B 0x00000000
+#define LPC_HC_FW_RD_2B 0x01000000
+#define LPC_HC_FW_RD_4B 0x02000000
+#define LPC_HC_FW_RD_16B 0x04000000
+#define LPC_HC_FW_RD_128B 0x07000000
+#define LPC_HC_IRQSER_CTRL 0x30
+#define LPC_HC_IRQSER_EN 0x80000000
+#define LPC_HC_IRQSER_QMODE 0x40000000
+#define LPC_HC_IRQSER_START_MASK 0x03000000
+#define LPC_HC_IRQSER_START_4CLK 0x00000000
+#define LPC_HC_IRQSER_START_6CLK 0x01000000
+#define LPC_HC_IRQSER_START_8CLK 0x02000000
+#define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */
+#define LPC_HC_IRQSTAT 0x38
+#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */
+#define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */
+#define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000
+#define LPC_HC_IRQ_LRESET 0x00000400
+#define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080
+#define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040
+#define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020
+#define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010
+#define LPC_HC_IRQ_SYNC_TARG_TAR_ERR 0x00000008
+#define LPC_HC_IRQ_SYNC_BM_TAR_ERR 0x00000004
+#define LPC_HC_IRQ_SYNC_BM0_REQ 0x00000002
+#define LPC_HC_IRQ_SYNC_BM1_REQ 0x00000001
+#define LPC_HC_ERROR_ADDRESS 0x40
+
+#define LPC_OPB_SIZE 0x100000000ull
+
+#define ISA_IO_SIZE 0x00010000
+#define ISA_MEM_SIZE 0x10000000
+#define LPC_IO_OPB_ADDR 0xd0010000
+#define LPC_IO_OPB_SIZE 0x00010000
+#define LPC_MEM_OPB_ADDR 0xe0010000
+#define LPC_MEM_OPB_SIZE 0x10000000
+#define LPC_FW_OPB_ADDR 0xf0000000
+#define LPC_FW_OPB_SIZE 0x10000000
+
+#define LPC_OPB_REGS_OPB_ADDR 0xc0010000
+#define LPC_OPB_REGS_OPB_SIZE 0x00002000
+#define LPC_HC_REGS_OPB_ADDR 0xc0012000
+#define LPC_HC_REGS_OPB_SIZE 0x00001000
+
+
+/*
+ * TODO: the "primary" cell should only be added on chip 0. This is
+ * how skiboot chooses the default LPC controller on multichip
+ * systems.
+ *
+ * It would be easly done if we can change the populate() interface to
+ * replace the PnvXScomInterface parameter by a PnvChip one
+ */
+static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
+{
+ const char compat[] = "ibm,power8-lpc\0ibm,lpc";
+ char *name;
+ int offset;
+ uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE;
+ uint32_t reg[] = {
+ cpu_to_be32(lpc_pcba),
+ cpu_to_be32(PNV_XSCOM_LPC_SIZE)
+ };
+
+ name = g_strdup_printf("isa@%x", lpc_pcba);
+ offset = fdt_add_subnode(fdt, xscom_offset, name);
+ _FDT(offset);
+ g_free(name);
+
+ _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
+ _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
+ _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1)));
+ _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0)));
+ _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
+ return 0;
+}
+
+/*
+ * These read/write handlers of the OPB address space should be common
+ * with the P9 LPC Controller which uses direct MMIOs.
+ *
+ * TODO: rework to use address_space_stq() and address_space_ldq()
+ * instead.
+ */
+static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
+ int sz)
+{
+ bool success;
+
+ /* XXX Handle access size limits and FW read caching here */
+ success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
+ data, sz, false);
+
+ return success;
+}
+
+static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data,
+ int sz)
+{
+ bool success;
+
+ /* XXX Handle access size limits here */
+ success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED,
+ data, sz, true);
+
+ return success;
+}
+
+#define ECCB_CTL_READ (1ull << (63 - 15))
+#define ECCB_CTL_SZ_LSH (63 - 7)
+#define ECCB_CTL_SZ_MASK (0xfull << ECCB_CTL_SZ_LSH)
+#define ECCB_CTL_ADDR_MASK 0xffffffffu;
+
+#define ECCB_STAT_OP_DONE (1ull << (63 - 52))
+#define ECCB_STAT_OP_ERR (1ull << (63 - 52))
+#define ECCB_STAT_RD_DATA_LSH (63 - 37)
+#define ECCB_STAT_RD_DATA_MASK (0xffffffff << ECCB_STAT_RD_DATA_LSH)
+
+static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd)
+{
+ /* XXX Check for magic bits at the top, addr size etc... */
+ unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH;
+ uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK;
+ uint8_t data[4];
+ bool success;
+
+ if (cmd & ECCB_CTL_READ) {
+ success = opb_read(lpc, opb_addr, data, sz);
+ if (success) {
+ lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
+ (((uint64_t)data[0]) << 24 |
+ ((uint64_t)data[1]) << 16 |
+ ((uint64_t)data[2]) << 8 |
+ ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH;
+ } else {
+ lpc->eccb_stat_reg = ECCB_STAT_OP_DONE |
+ (0xffffffffull << ECCB_STAT_RD_DATA_LSH);
+ }
+ } else {
+ data[0] = lpc->eccb_data_reg >> 24;
+ data[1] = lpc->eccb_data_reg >> 16;
+ data[2] = lpc->eccb_data_reg >> 8;
+ data[3] = lpc->eccb_data_reg;
+
+ success = opb_write(lpc, opb_addr, data, sz);
+ lpc->eccb_stat_reg = ECCB_STAT_OP_DONE;
+ }
+ /* XXX Which error bit (if any) to signal OPB error ? */
+}
+
+static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned size)
+{
+ PnvLpcController *lpc = PNV_LPC(opaque);
+ uint32_t offset = addr >> 3;
+ uint64_t val = 0;
+
+ switch (offset & 3) {
+ case ECCB_CTL:
+ case ECCB_RESET:
+ val = 0;
+ break;
+ case ECCB_STAT:
+ val = lpc->eccb_stat_reg;
+ lpc->eccb_stat_reg = 0;
+ break;
+ case ECCB_DATA:
+ val = ((uint64_t)lpc->eccb_data_reg) << 32;
+ break;
+ }
+ return val;
+}
+
+static void pnv_lpc_xscom_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PnvLpcController *lpc = PNV_LPC(opaque);
+ uint32_t offset = addr >> 3;
+
+ switch (offset & 3) {
+ case ECCB_CTL:
+ pnv_lpc_do_eccb(lpc, val);
+ break;
+ case ECCB_RESET:
+ /* XXXX */
+ break;
+ case ECCB_STAT:
+ break;
+ case ECCB_DATA:
+ lpc->eccb_data_reg = val >> 32;
+ break;
+ }
+}
+
+static const MemoryRegionOps pnv_lpc_xscom_ops = {
+ .read = pnv_lpc_xscom_read,
+ .write = pnv_lpc_xscom_write,
+ .valid.min_access_size = 8,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
+{
+ PnvLpcController *lpc = opaque;
+ uint64_t val = 0xfffffffffffffffful;
+
+ switch (addr) {
+ case LPC_HC_FW_SEG_IDSEL:
+ val = lpc->lpc_hc_fw_seg_idsel;
+ break;
+ case LPC_HC_FW_RD_ACC_SIZE:
+ val = lpc->lpc_hc_fw_rd_acc_size;
+ break;
+ case LPC_HC_IRQSER_CTRL:
+ val = lpc->lpc_hc_irqser_ctrl;
+ break;
+ case LPC_HC_IRQMASK:
+ val = lpc->lpc_hc_irqmask;
+ break;
+ case LPC_HC_IRQSTAT:
+ val = lpc->lpc_hc_irqstat;
+ break;
+ case LPC_HC_ERROR_ADDRESS:
+ val = lpc->lpc_hc_error_addr;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%"
+ HWADDR_PRIx "\n", addr);
+ }
+ return val;
+}
+
+static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ PnvLpcController *lpc = opaque;
+
+ /* XXX Filter out reserved bits */
+
+ switch (addr) {
+ case LPC_HC_FW_SEG_IDSEL:
+ /* XXX Actually figure out how that works as this impact
+ * memory regions/aliases
+ */
+ lpc->lpc_hc_fw_seg_idsel = val;
+ break;
+ case LPC_HC_FW_RD_ACC_SIZE:
+ lpc->lpc_hc_fw_rd_acc_size = val;
+ break;
+ case LPC_HC_IRQSER_CTRL:
+ lpc->lpc_hc_irqser_ctrl = val;
+ break;
+ case LPC_HC_IRQMASK:
+ lpc->lpc_hc_irqmask = val;
+ break;
+ case LPC_HC_IRQSTAT:
+ lpc->lpc_hc_irqstat &= ~val;
+ break;
+ case LPC_HC_ERROR_ADDRESS:
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%"
+ HWADDR_PRIx "\n", addr);
+ }
+}
+
+static const MemoryRegionOps lpc_hc_ops = {
+ .read = lpc_hc_read,
+ .write = lpc_hc_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size)
+{
+ PnvLpcController *lpc = opaque;
+ uint64_t val = 0xfffffffffffffffful;
+
+ switch (addr) {
+ case OPB_MASTER_LS_IRQ_STAT:
+ val = lpc->opb_irq_stat;
+ break;
+ case OPB_MASTER_LS_IRQ_MASK:
+ val = lpc->opb_irq_mask;
+ break;
+ case OPB_MASTER_LS_IRQ_POL:
+ val = lpc->opb_irq_pol;
+ break;
+ case OPB_MASTER_LS_IRQ_INPUT:
+ val = lpc->opb_irq_input;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%"
+ HWADDR_PRIx "\n", addr);
+ }
+
+ return val;
+}
+
+static void opb_master_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PnvLpcController *lpc = opaque;
+
+ switch (addr) {
+ case OPB_MASTER_LS_IRQ_STAT:
+ lpc->opb_irq_stat &= ~val;
+ break;
+ case OPB_MASTER_LS_IRQ_MASK:
+ /* XXX Filter out reserved bits */
+ lpc->opb_irq_mask = val;
+ break;
+ case OPB_MASTER_LS_IRQ_POL:
+ /* XXX Filter out reserved bits */
+ lpc->opb_irq_pol = val;
+ break;
+ case OPB_MASTER_LS_IRQ_INPUT:
+ /* Read only */
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%"
+ HWADDR_PRIx "\n", addr);
+ }
+}
+
+static const MemoryRegionOps opb_master_ops = {
+ .read = opb_master_read,
+ .write = opb_master_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void pnv_lpc_realize(DeviceState *dev, Error **errp)
+{
+ PnvLpcController *lpc = PNV_LPC(dev);
+
+ /* Reg inits */
+ lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B;
+
+ /* Create address space and backing MR for the OPB bus */
+ memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull);
+ address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb");
+
+ /* Create ISA IO and Mem space regions which are the root of
+ * the ISA bus (ie, ISA address spaces). We don't create a
+ * separate one for FW which we alias to memory.
+ */
+ memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE);
+ memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE);
+
+ /* Create windows from the OPB space to the ISA space */
+ memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io",
+ &lpc->isa_io, 0, LPC_IO_OPB_SIZE);
+ memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR,
+ &lpc->opb_isa_io);
+ memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem",
+ &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE);
+ memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR,
+ &lpc->opb_isa_mem);
+ memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw",
+ &lpc->isa_mem, 0, LPC_FW_OPB_SIZE);
+ memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR,
+ &lpc->opb_isa_fw);
+
+ /* Create MMIO regions for LPC HC and OPB registers */
+ memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops,
+ lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE);
+ memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR,
+ &lpc->opb_master_regs);
+ memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc,
+ "lpc-hc", LPC_HC_REGS_OPB_SIZE);
+ memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR,
+ &lpc->lpc_hc_regs);
+
+ /* XScom region for LPC registers */
+ pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev),
+ &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
+ PNV_XSCOM_LPC_SIZE);
+}
+
+static void pnv_lpc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
+
+ xdc->populate = pnv_lpc_populate;
+
+ dc->realize = pnv_lpc_realize;
+}
+
+static const TypeInfo pnv_lpc_info = {
+ .name = TYPE_PNV_LPC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(PnvLpcController),
+ .class_init = pnv_lpc_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_PNV_XSCOM_INTERFACE },
+ { }
+ }
+};
+
+static void pnv_lpc_register_types(void)
+{
+ type_register_static(&pnv_lpc_info);
+}
+
+type_init(pnv_lpc_register_types)
diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c
new file mode 100644
index 0000000000..5aaa264bd7
--- /dev/null
+++ b/hw/ppc/pnv_xscom.c
@@ -0,0 +1,275 @@
+/*
+ * QEMU PowerPC PowerNV XSCOM bus
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * 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 "qapi/error.h"
+#include "hw/hw.h"
+#include "qemu/log.h"
+#include "sysemu/kvm.h"
+#include "target-ppc/cpu.h"
+#include "hw/sysbus.h"
+
+#include "hw/ppc/fdt.h"
+#include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/pnv.h"
+
+#include <libfdt.h>
+
+static void xscom_complete(CPUState *cs, uint64_t hmer_bits)
+{
+ /*
+ * TODO: When the read/write comes from the monitor, NULL is
+ * passed for the cpu, and no CPU completion is generated.
+ */
+ if (cs) {
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+
+ /*
+ * TODO: Need a CPU helper to set HMER, also handle generation
+ * of HMIs
+ */
+ cpu_synchronize_state(cs);
+ env->spr[SPR_HMER] |= hmer_bits;
+ }
+}
+
+static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr)
+{
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+ addr &= (PNV_XSCOM_SIZE - 1);
+ if (pcc->chip_type == PNV_CHIP_POWER9) {
+ return addr >> 3;
+ } else {
+ return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf);
+ }
+}
+
+static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba)
+{
+ switch (pcba) {
+ case 0xf000f:
+ return PNV_CHIP_GET_CLASS(chip)->chip_cfam_id;
+ case 0x1010c00: /* PIBAM FIR */
+ case 0x1010c03: /* PIBAM FIR MASK */
+ case 0x2020007: /* ADU stuff */
+ case 0x2020009: /* ADU stuff */
+ case 0x202000f: /* ADU stuff */
+ return 0;
+ case 0x2013f00: /* PBA stuff */
+ case 0x2013f01: /* PBA stuff */
+ case 0x2013f02: /* PBA stuff */
+ case 0x2013f03: /* PBA stuff */
+ case 0x2013f04: /* PBA stuff */
+ case 0x2013f05: /* PBA stuff */
+ case 0x2013f06: /* PBA stuff */
+ case 0x2013f07: /* PBA stuff */
+ return 0;
+ case 0x2013028: /* CAPP stuff */
+ case 0x201302a: /* CAPP stuff */
+ case 0x2013801: /* CAPP stuff */
+ case 0x2013802: /* CAPP stuff */
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val)
+{
+ /* We ignore writes to these */
+ switch (pcba) {
+ case 0xf000f: /* chip id is RO */
+ case 0x1010c00: /* PIBAM FIR */
+ case 0x1010c01: /* PIBAM FIR */
+ case 0x1010c02: /* PIBAM FIR */
+ case 0x1010c03: /* PIBAM FIR MASK */
+ case 0x1010c04: /* PIBAM FIR MASK */
+ case 0x1010c05: /* PIBAM FIR MASK */
+ case 0x2020007: /* ADU stuff */
+ case 0x2020009: /* ADU stuff */
+ case 0x202000f: /* ADU stuff */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width)
+{
+ PnvChip *chip = opaque;
+ uint32_t pcba = pnv_xscom_pcba(chip, addr);
+ uint64_t val = 0;
+ MemTxResult result;
+
+ /* Handle some SCOMs here before dispatch */
+ val = xscom_read_default(chip, pcba);
+ if (val != -1) {
+ goto complete;
+ }
+
+ val = address_space_ldq(&chip->xscom_as, pcba << 3, MEMTXATTRS_UNSPECIFIED,
+ &result);
+ if (result != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XSCOM read failed at @0x%"
+ HWADDR_PRIx " pcba=0x%08x\n", addr, pcba);
+ xscom_complete(current_cpu, HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
+ return 0;
+ }
+
+complete:
+ xscom_complete(current_cpu, HMER_XSCOM_DONE);
+ return val;
+}
+
+static void xscom_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned width)
+{
+ PnvChip *chip = opaque;
+ uint32_t pcba = pnv_xscom_pcba(chip, addr);
+ MemTxResult result;
+
+ /* Handle some SCOMs here before dispatch */
+ if (xscom_write_default(chip, pcba, val)) {
+ goto complete;
+ }
+
+ address_space_stq(&chip->xscom_as, pcba << 3, val, MEMTXATTRS_UNSPECIFIED,
+ &result);
+ if (result != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR, "XSCOM write failed at @0x%"
+ HWADDR_PRIx " pcba=0x%08x data=0x%" PRIx64 "\n",
+ addr, pcba, val);
+ xscom_complete(current_cpu, HMER_XSCOM_FAIL | HMER_XSCOM_DONE);
+ return;
+ }
+
+complete:
+ xscom_complete(current_cpu, HMER_XSCOM_DONE);
+}
+
+const MemoryRegionOps pnv_xscom_ops = {
+ .read = xscom_read,
+ .write = xscom_write,
+ .valid.min_access_size = 8,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+void pnv_xscom_realize(PnvChip *chip, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(chip);
+ char *name;
+
+ name = g_strdup_printf("xscom-%x", chip->chip_id);
+ memory_region_init_io(&chip->xscom_mmio, OBJECT(chip), &pnv_xscom_ops,
+ chip, name, PNV_XSCOM_SIZE);
+ sysbus_init_mmio(sbd, &chip->xscom_mmio);
+
+ memory_region_init(&chip->xscom, OBJECT(chip), name, PNV_XSCOM_SIZE);
+ address_space_init(&chip->xscom_as, &chip->xscom, name);
+ g_free(name);
+}
+
+static const TypeInfo pnv_xscom_interface_info = {
+ .name = TYPE_PNV_XSCOM_INTERFACE,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(PnvXScomInterfaceClass),
+};
+
+static void pnv_xscom_register_types(void)
+{
+ type_register_static(&pnv_xscom_interface_info);
+}
+
+type_init(pnv_xscom_register_types)
+
+typedef struct ForeachPopulateArgs {
+ void *fdt;
+ int xscom_offset;
+} ForeachPopulateArgs;
+
+static int xscom_populate_child(Object *child, void *opaque)
+{
+ if (object_dynamic_cast(child, TYPE_PNV_XSCOM_INTERFACE)) {
+ ForeachPopulateArgs *args = opaque;
+ PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child);
+ PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd);
+
+ if (xc->populate) {
+ _FDT((xc->populate(xd, args->fdt, args->xscom_offset)));
+ }
+ }
+ return 0;
+}
+
+static const char compat_p8[] = "ibm,power8-xscom\0ibm,xscom";
+static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom";
+
+int pnv_xscom_populate(PnvChip *chip, void *fdt, int root_offset)
+{
+ uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(chip)),
+ cpu_to_be64(PNV_XSCOM_SIZE) };
+ int xscom_offset;
+ ForeachPopulateArgs args;
+ char *name;
+ PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
+
+ name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0]));
+ xscom_offset = fdt_add_subnode(fdt, root_offset, name);
+ _FDT(xscom_offset);
+ g_free(name);
+ _FDT((fdt_setprop_cell(fdt, xscom_offset, "ibm,chip-id", chip->chip_id)));
+ _FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1)));
+ _FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1)));
+ _FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg))));
+
+ if (pcc->chip_type == PNV_CHIP_POWER9) {
+ _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9,
+ sizeof(compat_p9))));
+ } else {
+ _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p8,
+ sizeof(compat_p8))));
+ }
+
+ _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0)));
+
+ args.fdt = fdt;
+ args.xscom_offset = xscom_offset;
+
+ object_child_foreach(OBJECT(chip), xscom_populate_child, &args);
+ return 0;
+}
+
+void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, MemoryRegion *mr)
+{
+ memory_region_add_subregion(&chip->xscom, offset << 3, mr);
+}
+
+void pnv_xscom_region_init(MemoryRegion *mr,
+ struct Object *owner,
+ const MemoryRegionOps *ops,
+ void *opaque,
+ const char *name,
+ uint64_t size)
+{
+ memory_region_init_io(mr, owner, ops, opaque, name, size << 3);
+}
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 486f57d6f6..c8e29212cb 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -271,205 +271,6 @@ static void add_str(GString *s, const gchar *s1)
g_string_append_len(s, s1, strlen(s1) + 1);
}
-static void *spapr_create_fdt_skel(hwaddr initrd_base,
- hwaddr initrd_size,
- hwaddr kernel_size,
- bool little_endian,
- const char *kernel_cmdline,
- uint32_t epow_irq)
-{
- void *fdt;
- uint32_t start_prop = cpu_to_be32(initrd_base);
- uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
- GString *hypertas = g_string_sized_new(256);
- GString *qemu_hypertas = g_string_sized_new(256);
- uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
- uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(max_cpus)};
- unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80};
- char *buf;
-
- add_str(hypertas, "hcall-pft");
- add_str(hypertas, "hcall-term");
- add_str(hypertas, "hcall-dabr");
- add_str(hypertas, "hcall-interrupt");
- add_str(hypertas, "hcall-tce");
- add_str(hypertas, "hcall-vio");
- add_str(hypertas, "hcall-splpar");
- add_str(hypertas, "hcall-bulk");
- add_str(hypertas, "hcall-set-mode");
- add_str(hypertas, "hcall-sprg0");
- add_str(hypertas, "hcall-copy");
- add_str(hypertas, "hcall-debug");
- add_str(qemu_hypertas, "hcall-memop1");
-
- fdt = g_malloc0(FDT_MAX_SIZE);
- _FDT((fdt_create(fdt, FDT_MAX_SIZE)));
-
- if (kernel_size) {
- _FDT((fdt_add_reservemap_entry(fdt, KERNEL_LOAD_ADDR, kernel_size)));
- }
- if (initrd_size) {
- _FDT((fdt_add_reservemap_entry(fdt, initrd_base, initrd_size)));
- }
- _FDT((fdt_finish_reservemap(fdt)));
-
- /* Root node */
- _FDT((fdt_begin_node(fdt, "")));
- _FDT((fdt_property_string(fdt, "device_type", "chrp")));
- _FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)")));
- _FDT((fdt_property_string(fdt, "compatible", "qemu,pseries")));
-
- /*
- * Add info to guest to indentify which host is it being run on
- * and what is the uuid of the guest
- */
- if (kvmppc_get_host_model(&buf)) {
- _FDT((fdt_property_string(fdt, "host-model", buf)));
- g_free(buf);
- }
- if (kvmppc_get_host_serial(&buf)) {
- _FDT((fdt_property_string(fdt, "host-serial", buf)));
- g_free(buf);
- }
-
- buf = qemu_uuid_unparse_strdup(&qemu_uuid);
-
- _FDT((fdt_property_string(fdt, "vm,uuid", buf)));
- if (qemu_uuid_set) {
- _FDT((fdt_property_string(fdt, "system-id", buf)));
- }
- g_free(buf);
-
- if (qemu_get_vm_name()) {
- _FDT((fdt_property_string(fdt, "ibm,partition-name",
- qemu_get_vm_name())));
- }
-
- _FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
- _FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
-
- /* /chosen */
- _FDT((fdt_begin_node(fdt, "chosen")));
-
- /* Set Form1_affinity */
- _FDT((fdt_property(fdt, "ibm,architecture-vec-5", vec5, sizeof(vec5))));
-
- _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline)));
- _FDT((fdt_property(fdt, "linux,initrd-start",
- &start_prop, sizeof(start_prop))));
- _FDT((fdt_property(fdt, "linux,initrd-end",
- &end_prop, sizeof(end_prop))));
- if (kernel_size) {
- uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR),
- cpu_to_be64(kernel_size) };
-
- _FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop))));
- if (little_endian) {
- _FDT((fdt_property(fdt, "qemu,boot-kernel-le", NULL, 0)));
- }
- }
- if (boot_menu) {
- _FDT((fdt_property_cell(fdt, "qemu,boot-menu", boot_menu)));
- }
- _FDT((fdt_property_cell(fdt, "qemu,graphic-width", graphic_width)));
- _FDT((fdt_property_cell(fdt, "qemu,graphic-height", graphic_height)));
- _FDT((fdt_property_cell(fdt, "qemu,graphic-depth", graphic_depth)));
-
- _FDT((fdt_end_node(fdt)));
-
- /* RTAS */
- _FDT((fdt_begin_node(fdt, "rtas")));
-
- if (!kvm_enabled() || kvmppc_spapr_use_multitce()) {
- add_str(hypertas, "hcall-multi-tce");
- }
- _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas->str,
- hypertas->len)));
- g_string_free(hypertas, TRUE);
- _FDT((fdt_property(fdt, "qemu,hypertas-functions", qemu_hypertas->str,
- qemu_hypertas->len)));
- g_string_free(qemu_hypertas, TRUE);
-
- _FDT((fdt_property(fdt, "ibm,associativity-reference-points",
- refpoints, sizeof(refpoints))));
-
- _FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)));
- _FDT((fdt_property_cell(fdt, "rtas-event-scan-rate",
- RTAS_EVENT_SCAN_RATE)));
-
- if (msi_nonbroken) {
- _FDT((fdt_property(fdt, "ibm,change-msix-capable", NULL, 0)));
- }
-
- /*
- * According to PAPR, rtas ibm,os-term does not guarantee a return
- * back to the guest cpu.
- *
- * While an additional ibm,extended-os-term property indicates that
- * rtas call return will always occur. Set this property.
- */
- _FDT((fdt_property(fdt, "ibm,extended-os-term", NULL, 0)));
-
- _FDT((fdt_end_node(fdt)));
-
- /* interrupt controller */
- _FDT((fdt_begin_node(fdt, "interrupt-controller")));
-
- _FDT((fdt_property_string(fdt, "device_type",
- "PowerPC-External-Interrupt-Presentation")));
- _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp")));
- _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
- _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges",
- interrupt_server_ranges_prop,
- sizeof(interrupt_server_ranges_prop))));
- _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
- _FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP)));
- _FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP)));
-
- _FDT((fdt_end_node(fdt)));
-
- /* vdevice */
- _FDT((fdt_begin_node(fdt, "vdevice")));
-
- _FDT((fdt_property_string(fdt, "device_type", "vdevice")));
- _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice")));
- _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
- _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
- _FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2)));
- _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
-
- _FDT((fdt_end_node(fdt)));
-
- /* event-sources */
- spapr_events_fdt_skel(fdt, epow_irq);
-
- /* /hypervisor node */
- if (kvm_enabled()) {
- uint8_t hypercall[16];
-
- /* indicate KVM hypercall interface */
- _FDT((fdt_begin_node(fdt, "hypervisor")));
- _FDT((fdt_property_string(fdt, "compatible", "linux,kvm")));
- if (kvmppc_has_cap_fixup_hcalls()) {
- /*
- * Older KVM versions with older guest kernels were broken with the
- * magic page, don't allow the guest to map it.
- */
- if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall,
- sizeof(hypercall))) {
- _FDT((fdt_property(fdt, "hcall-instructions", hypercall,
- sizeof(hypercall))));
- }
- }
- _FDT((fdt_end_node(fdt)));
- }
-
- _FDT((fdt_end_node(fdt))); /* close root node */
- _FDT((fdt_finish(fdt)));
-
- return fdt;
-}
-
static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start,
hwaddr size)
{
@@ -854,13 +655,42 @@ out:
return ret;
}
+static int spapr_dt_cas_updates(sPAPRMachineState *spapr, void *fdt,
+ sPAPROptionVector *ov5_updates)
+{
+ sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
+ int ret = 0, offset;
+
+ /* Generate ibm,dynamic-reconfiguration-memory node if required */
+ if (spapr_ovec_test(ov5_updates, OV5_DRCONF_MEMORY)) {
+ g_assert(smc->dr_lmb_enabled);
+ ret = spapr_populate_drconf_memory(spapr, fdt);
+ if (ret) {
+ goto out;
+ }
+ }
+
+ offset = fdt_path_offset(fdt, "/chosen");
+ if (offset < 0) {
+ offset = fdt_add_subnode(fdt, 0, "chosen");
+ if (offset < 0) {
+ return offset;
+ }
+ }
+ ret = spapr_ovec_populate_dt(fdt, offset, spapr->ov5_cas,
+ "ibm,architecture-vec-5");
+
+out:
+ return ret;
+}
+
int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
target_ulong addr, target_ulong size,
- bool cpu_update, bool memory_update)
+ bool cpu_update,
+ sPAPROptionVector *ov5_updates)
{
void *fdt, *fdt_skel;
sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 };
- sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine());
size -= sizeof(hdr);
@@ -879,9 +709,8 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
_FDT((spapr_fixup_cpu_dt(fdt, spapr)));
}
- /* Generate ibm,dynamic-reconfiguration-memory node if required */
- if (memory_update && smc->dr_lmb_enabled) {
- _FDT((spapr_populate_drconf_memory(spapr, fdt)));
+ if (spapr_dt_cas_updates(spapr, fdt, ov5_updates)) {
+ return -1;
}
/* Pack resulting tree */
@@ -900,25 +729,206 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr,
return 0;
}
-static void spapr_finalize_fdt(sPAPRMachineState *spapr,
- hwaddr fdt_addr,
- hwaddr rtas_addr,
- hwaddr rtas_size)
+static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt)
+{
+ int rtas;
+ GString *hypertas = g_string_sized_new(256);
+ GString *qemu_hypertas = g_string_sized_new(256);
+ uint32_t refpoints[] = { cpu_to_be32(0x4), cpu_to_be32(0x4) };
+ uint64_t max_hotplug_addr = spapr->hotplug_memory.base +
+ memory_region_size(&spapr->hotplug_memory.mr);
+ uint32_t lrdr_capacity[] = {
+ cpu_to_be32(max_hotplug_addr >> 32),
+ cpu_to_be32(max_hotplug_addr & 0xffffffff),
+ 0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE),
+ cpu_to_be32(max_cpus / smp_threads),
+ };
+
+ _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas"));
+
+ /* hypertas */
+ add_str(hypertas, "hcall-pft");
+ add_str(hypertas, "hcall-term");
+ add_str(hypertas, "hcall-dabr");
+ add_str(hypertas, "hcall-interrupt");
+ add_str(hypertas, "hcall-tce");
+ add_str(hypertas, "hcall-vio");
+ add_str(hypertas, "hcall-splpar");
+ add_str(hypertas, "hcall-bulk");
+ add_str(hypertas, "hcall-set-mode");
+ add_str(hypertas, "hcall-sprg0");
+ add_str(hypertas, "hcall-copy");
+ add_str(hypertas, "hcall-debug");
+ add_str(qemu_hypertas, "hcall-memop1");
+
+ if (!kvm_enabled() || kvmppc_spapr_use_multitce()) {
+ add_str(hypertas, "hcall-multi-tce");
+ }
+ _FDT(fdt_setprop(fdt, rtas, "ibm,hypertas-functions",
+ hypertas->str, hypertas->len));
+ g_string_free(hypertas, TRUE);
+ _FDT(fdt_setprop(fdt, rtas, "qemu,hypertas-functions",
+ qemu_hypertas->str, qemu_hypertas->len));
+ g_string_free(qemu_hypertas, TRUE);
+
+ _FDT(fdt_setprop(fdt, rtas, "ibm,associativity-reference-points",
+ refpoints, sizeof(refpoints)));
+
+ _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max",
+ RTAS_ERROR_LOG_MAX));
+ _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate",
+ RTAS_EVENT_SCAN_RATE));
+
+ if (msi_nonbroken) {
+ _FDT(fdt_setprop(fdt, rtas, "ibm,change-msix-capable", NULL, 0));
+ }
+
+ /*
+ * According to PAPR, rtas ibm,os-term does not guarantee a return
+ * back to the guest cpu.
+ *
+ * While an additional ibm,extended-os-term property indicates
+ * that rtas call return will always occur. Set this property.
+ */
+ _FDT(fdt_setprop(fdt, rtas, "ibm,extended-os-term", NULL, 0));
+
+ _FDT(fdt_setprop(fdt, rtas, "ibm,lrdr-capacity",
+ lrdr_capacity, sizeof(lrdr_capacity)));
+
+ spapr_dt_rtas_tokens(fdt, rtas);
+}
+
+static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt)
+{
+ MachineState *machine = MACHINE(spapr);
+ int chosen;
+ const char *boot_device = machine->boot_order;
+ char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus);
+ size_t cb = 0;
+ char *bootlist = get_boot_devices_list(&cb, true);
+
+ _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen"));
+
+ _FDT(fdt_setprop_string(fdt, chosen, "bootargs", machine->kernel_cmdline));
+ _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start",
+ spapr->initrd_base));
+ _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end",
+ spapr->initrd_base + spapr->initrd_size));
+
+ if (spapr->kernel_size) {
+ uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR),
+ cpu_to_be64(spapr->kernel_size) };
+
+ _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel",
+ &kprop, sizeof(kprop)));
+ if (spapr->kernel_le) {
+ _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0));
+ }
+ }
+ if (boot_menu) {
+ _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu)));
+ }
+ _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width));
+ _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height));
+ _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth));
+
+ if (cb && bootlist) {
+ int i;
+
+ for (i = 0; i < cb; i++) {
+ if (bootlist[i] == '\n') {
+ bootlist[i] = ' ';
+ }
+ }
+ _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist));
+ }
+
+ if (boot_device && strlen(boot_device)) {
+ _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device));
+ }
+
+ if (!spapr->has_graphics && stdout_path) {
+ _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path));
+ }
+
+ g_free(stdout_path);
+ g_free(bootlist);
+}
+
+static void spapr_dt_hypervisor(sPAPRMachineState *spapr, void *fdt)
+{
+ /* The /hypervisor node isn't in PAPR - this is a hack to allow PR
+ * KVM to work under pHyp with some guest co-operation */
+ int hypervisor;
+ uint8_t hypercall[16];
+
+ _FDT(hypervisor = fdt_add_subnode(fdt, 0, "hypervisor"));
+ /* indicate KVM hypercall interface */
+ _FDT(fdt_setprop_string(fdt, hypervisor, "compatible", "linux,kvm"));
+ if (kvmppc_has_cap_fixup_hcalls()) {
+ /*
+ * Older KVM versions with older guest kernels were broken
+ * with the magic page, don't allow the guest to map it.
+ */
+ if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall,
+ sizeof(hypercall))) {
+ _FDT(fdt_setprop(fdt, hypervisor, "hcall-instructions",
+ hypercall, sizeof(hypercall)));
+ }
+ }
+}
+
+static void *spapr_build_fdt(sPAPRMachineState *spapr,
+ hwaddr rtas_addr,
+ hwaddr rtas_size)
{
MachineState *machine = MACHINE(qdev_get_machine());
MachineClass *mc = MACHINE_GET_CLASS(machine);
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
- const char *boot_device = machine->boot_order;
- int ret, i;
- size_t cb = 0;
- char *bootlist;
+ int ret;
void *fdt;
sPAPRPHBState *phb;
+ char *buf;
+
+ fdt = g_malloc0(FDT_MAX_SIZE);
+ _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
+
+ /* Root node */
+ _FDT(fdt_setprop_string(fdt, 0, "device_type", "chrp"));
+ _FDT(fdt_setprop_string(fdt, 0, "model", "IBM pSeries (emulated by qemu)"));
+ _FDT(fdt_setprop_string(fdt, 0, "compatible", "qemu,pseries"));
+
+ /*
+ * Add info to guest to indentify which host is it being run on
+ * and what is the uuid of the guest
+ */
+ if (kvmppc_get_host_model(&buf)) {
+ _FDT(fdt_setprop_string(fdt, 0, "host-model", buf));
+ g_free(buf);
+ }
+ if (kvmppc_get_host_serial(&buf)) {
+ _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf));
+ g_free(buf);
+ }
+
+ buf = qemu_uuid_unparse_strdup(&qemu_uuid);
+
+ _FDT(fdt_setprop_string(fdt, 0, "vm,uuid", buf));
+ if (qemu_uuid_set) {
+ _FDT(fdt_setprop_string(fdt, 0, "system-id", buf));
+ }
+ g_free(buf);
+
+ if (qemu_get_vm_name()) {
+ _FDT(fdt_setprop_string(fdt, 0, "ibm,partition-name",
+ qemu_get_vm_name()));
+ }
- fdt = g_malloc(FDT_MAX_SIZE);
+ _FDT(fdt_setprop_cell(fdt, 0, "#address-cells", 2));
+ _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2));
- /* open out the base tree into a temp buffer for the final tweaks */
- _FDT((fdt_open_into(spapr->fdt_skel, fdt, FDT_MAX_SIZE)));
+ /* /interrupt controller */
+ spapr_dt_xics(spapr->xics, fdt, PHANDLE_XICP);
ret = spapr_populate_memory(spapr, fdt);
if (ret < 0) {
@@ -926,11 +936,8 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
exit(1);
}
- ret = spapr_populate_vdevice(spapr->vio_bus, fdt);
- if (ret < 0) {
- error_report("couldn't setup vio devices in fdt");
- exit(1);
- }
+ /* /vdevice */
+ spapr_dt_vdevice(spapr->vio_bus, fdt);
if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) {
ret = spapr_rng_populate_dt(fdt);
@@ -948,43 +955,9 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
}
}
- /* RTAS */
- ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size);
- if (ret < 0) {
- error_report("Couldn't set up RTAS device tree properties");
- }
-
/* cpus */
spapr_populate_cpus_dt_node(fdt, spapr);
- bootlist = get_boot_devices_list(&cb, true);
- if (cb && bootlist) {
- int offset = fdt_path_offset(fdt, "/chosen");
- if (offset < 0) {
- exit(1);
- }
- for (i = 0; i < cb; i++) {
- if (bootlist[i] == '\n') {
- bootlist[i] = ' ';
- }
-
- }
- ret = fdt_setprop_string(fdt, offset, "qemu,boot-list", bootlist);
- }
-
- if (boot_device && strlen(boot_device)) {
- int offset = fdt_path_offset(fdt, "/chosen");
-
- if (offset < 0) {
- exit(1);
- }
- fdt_setprop_string(fdt, offset, "qemu,boot-device", boot_device);
- }
-
- if (!spapr->has_graphics) {
- spapr_populate_chosen_stdout(fdt, spapr->vio_bus);
- }
-
if (smc->dr_lmb_enabled) {
_FDT(spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB));
}
@@ -999,19 +972,36 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr,
}
}
- _FDT((fdt_pack(fdt)));
+ /* /event-sources */
+ spapr_dt_events(spapr, fdt);
- if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
- error_report("FDT too big ! 0x%x bytes (max is 0x%x)",
- fdt_totalsize(fdt), FDT_MAX_SIZE);
- exit(1);
+ /* /rtas */
+ spapr_dt_rtas(spapr, fdt);
+
+ /* /chosen */
+ spapr_dt_chosen(spapr, fdt);
+
+ /* /hypervisor */
+ if (kvm_enabled()) {
+ spapr_dt_hypervisor(spapr, fdt);
}
- qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt));
- cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));
+ /* Build memory reserve map */
+ if (spapr->kernel_size) {
+ _FDT((fdt_add_mem_rsv(fdt, KERNEL_LOAD_ADDR, spapr->kernel_size)));
+ }
+ if (spapr->initrd_size) {
+ _FDT((fdt_add_mem_rsv(fdt, spapr->initrd_base, spapr->initrd_size)));
+ }
- g_free(bootlist);
- g_free(fdt);
+ /* ibm,client-architecture-support updates */
+ ret = spapr_dt_cas_updates(spapr, fdt, spapr->ov5_cas);
+ if (ret < 0) {
+ error_report("couldn't setup CAS properties fdt");
+ exit(1);
+ }
+
+ return fdt;
}
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
@@ -1147,6 +1137,9 @@ static void ppc_spapr_reset(void)
sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
PowerPCCPU *first_ppc_cpu;
uint32_t rtas_limit;
+ hwaddr rtas_addr, fdt_addr;
+ void *fdt;
+ int rc;
/* Check for unknown sysbus devices */
foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL);
@@ -1170,24 +1163,44 @@ static void ppc_spapr_reset(void)
* processed with 32-bit real mode code if necessary
*/
rtas_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR);
- spapr->rtas_addr = rtas_limit - RTAS_MAX_SIZE;
- spapr->fdt_addr = spapr->rtas_addr - FDT_MAX_SIZE;
+ rtas_addr = rtas_limit - RTAS_MAX_SIZE;
+ fdt_addr = rtas_addr - FDT_MAX_SIZE;
- /* Load the fdt */
- spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr,
- spapr->rtas_size);
+ /* if this reset wasn't generated by CAS, we should reset our
+ * negotiated options and start from scratch */
+ if (!spapr->cas_reboot) {
+ spapr_ovec_cleanup(spapr->ov5_cas);
+ spapr->ov5_cas = spapr_ovec_new();
+ }
+
+ fdt = spapr_build_fdt(spapr, rtas_addr, spapr->rtas_size);
+
+ spapr_load_rtas(spapr, fdt, rtas_addr);
- /* Copy RTAS over */
- cpu_physical_memory_write(spapr->rtas_addr, spapr->rtas_blob,
- spapr->rtas_size);
+ rc = fdt_pack(fdt);
+
+ /* Should only fail if we've built a corrupted tree */
+ assert(rc == 0);
+
+ if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
+ error_report("FDT too big ! 0x%x bytes (max is 0x%x)",
+ fdt_totalsize(fdt), FDT_MAX_SIZE);
+ exit(1);
+ }
+
+ /* Load the fdt */
+ qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt));
+ cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));
+ g_free(fdt);
/* Set up the entry state */
first_ppc_cpu = POWERPC_CPU(first_cpu);
- first_ppc_cpu->env.gpr[3] = spapr->fdt_addr;
+ first_ppc_cpu->env.gpr[3] = fdt_addr;
first_ppc_cpu->env.gpr[5] = 0;
first_cpu->halted = 0;
first_ppc_cpu->env.nip = SPAPR_ENTRY_POINT;
+ spapr->cas_reboot = false;
}
static void spapr_create_nvram(sPAPRMachineState *spapr)
@@ -1682,7 +1695,6 @@ static void ppc_spapr_init(MachineState *machine)
MachineClass *mc = MACHINE_GET_CLASS(machine);
sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
const char *kernel_filename = machine->kernel_filename;
- const char *kernel_cmdline = machine->kernel_cmdline;
const char *initrd_filename = machine->initrd_filename;
PCIHostState *phb;
int i;
@@ -1692,10 +1704,7 @@ static void ppc_spapr_init(MachineState *machine)
void *rma = NULL;
hwaddr rma_alloc_size;
hwaddr node0_size = spapr_node0_size();
- uint32_t initrd_base = 0;
- long kernel_size = 0, initrd_size = 0;
long load_limit, fw_size;
- bool kernel_le = false;
char *filename;
int smt = kvmppc_smt_threads();
int spapr_cores = smp_cpus / smp_threads;
@@ -1769,10 +1778,22 @@ static void ppc_spapr_init(MachineState *machine)
DIV_ROUND_UP(max_cpus * smt, smp_threads),
XICS_IRQS_SPAPR, &error_fatal);
+ /* Set up containers for ibm,client-set-architecture negotiated options */
+ spapr->ov5 = spapr_ovec_new();
+ spapr->ov5_cas = spapr_ovec_new();
+
if (smc->dr_lmb_enabled) {
+ spapr_ovec_set(spapr->ov5, OV5_DRCONF_MEMORY);
spapr_validate_node_memory(machine, &error_fatal);
}
+ spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY);
+
+ /* advertise support for dedicated HP event source to guests */
+ if (spapr->use_hotplug_event_source) {
+ spapr_ovec_set(spapr->ov5, OV5_HP_EVT);
+ }
+
/* init CPUs */
if (machine->cpu_model == NULL) {
machine->cpu_model = kvm_enabled() ? "host" : smc->tcg_default_cpu;
@@ -1896,7 +1917,7 @@ static void ppc_spapr_init(MachineState *machine)
}
g_free(filename);
- /* Set up EPOW events infrastructure */
+ /* Set up RTAS event infrastructure */
spapr_events_init(spapr);
/* Set up the RTC RTAS interfaces */
@@ -1968,19 +1989,19 @@ static void ppc_spapr_init(MachineState *machine)
if (kernel_filename) {
uint64_t lowaddr = 0;
- kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE,
- 0, 0);
- if (kernel_size == ELF_LOAD_WRONG_ENDIAN) {
- kernel_size = load_elf(kernel_filename,
- translate_kernel_address, NULL,
- NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE,
- 0, 0);
- kernel_le = kernel_size > 0;
- }
- if (kernel_size < 0) {
- error_report("error loading %s: %s",
- kernel_filename, load_elf_strerror(kernel_size));
+ spapr->kernel_size = load_elf(kernel_filename, translate_kernel_address,
+ NULL, NULL, &lowaddr, NULL, 1,
+ PPC_ELF_MACHINE, 0, 0);
+ if (spapr->kernel_size == ELF_LOAD_WRONG_ENDIAN) {
+ spapr->kernel_size = load_elf(kernel_filename,
+ translate_kernel_address, NULL, NULL,
+ &lowaddr, NULL, 0, PPC_ELF_MACHINE,
+ 0, 0);
+ spapr->kernel_le = spapr->kernel_size > 0;
+ }
+ if (spapr->kernel_size < 0) {
+ error_report("error loading %s: %s", kernel_filename,
+ load_elf_strerror(spapr->kernel_size));
exit(1);
}
@@ -1989,17 +2010,17 @@ static void ppc_spapr_init(MachineState *machine)
/* Try to locate the initrd in the gap between the kernel
* and the firmware. Add a bit of space just in case
*/
- initrd_base = (KERNEL_LOAD_ADDR + kernel_size + 0x1ffff) & ~0xffff;
- initrd_size = load_image_targphys(initrd_filename, initrd_base,
- load_limit - initrd_base);
- if (initrd_size < 0) {
+ spapr->initrd_base = (KERNEL_LOAD_ADDR + spapr->kernel_size
+ + 0x1ffff) & ~0xffff;
+ spapr->initrd_size = load_image_targphys(initrd_filename,
+ spapr->initrd_base,
+ load_limit
+ - spapr->initrd_base);
+ if (spapr->initrd_size < 0) {
error_report("could not load initial ram disk '%s'",
initrd_filename);
exit(1);
}
- } else {
- initrd_base = 0;
- initrd_size = 0;
}
}
@@ -2025,13 +2046,6 @@ static void ppc_spapr_init(MachineState *machine)
register_savevm_live(NULL, "spapr/htab", -1, 1,
&savevm_htab_handlers, spapr);
- /* Prepare the device tree */
- spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
- kernel_size, kernel_le,
- kernel_cmdline,
- spapr->check_exception_irq);
- assert(spapr->fdt_skel != NULL);
-
/* used by RTAS */
QTAILQ_INIT(&spapr->ccs_list);
qemu_register_reset(spapr_ccs_reset_hook, spapr);
@@ -2129,16 +2143,41 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp)
spapr->kvm_type = g_strdup(value);
}
+static bool spapr_get_modern_hotplug_events(Object *obj, Error **errp)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+ return spapr->use_hotplug_event_source;
+}
+
+static void spapr_set_modern_hotplug_events(Object *obj, bool value,
+ Error **errp)
+{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+
+ spapr->use_hotplug_event_source = value;
+}
+
static void spapr_machine_initfn(Object *obj)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
spapr->htab_fd = -1;
+ spapr->use_hotplug_event_source = true;
object_property_add_str(obj, "kvm-type",
spapr_get_kvm_type, spapr_set_kvm_type, NULL);
object_property_set_description(obj, "kvm-type",
"Specifies the KVM virtualization mode (HV, PR)",
NULL);
+ object_property_add_bool(obj, "modern-hotplug-events",
+ spapr_get_modern_hotplug_events,
+ spapr_set_modern_hotplug_events,
+ NULL);
+ object_property_set_description(obj, "modern-hotplug-events",
+ "Use dedicated hotplug event mechanism in"
+ " place of standard EPOW events when possible"
+ " (required for memory hot-unplug support)",
+ NULL);
}
static void spapr_machine_finalizefn(Object *obj)
@@ -2163,14 +2202,16 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp)
}
}
-static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size,
- uint32_t node, Error **errp)
+static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
+ uint32_t node, bool dedicated_hp_event_source,
+ Error **errp)
{
sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck;
uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE;
int i, fdt_offset, fdt_size;
void *fdt;
+ uint64_t addr = addr_start;
for (i = 0; i < nr_lmbs; i++) {
drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
@@ -2189,7 +2230,17 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size,
* guest only in case of hotplugged memory
*/
if (dev->hotplugged) {
- spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs);
+ if (dedicated_hp_event_source) {
+ drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr_start / SPAPR_MEMORY_BLOCK_SIZE);
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ spapr_hotplug_req_add_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ nr_lmbs,
+ drck->get_index(drc));
+ } else {
+ spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ nr_lmbs);
+ }
}
}
@@ -2222,8 +2273,94 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
goto out;
}
- spapr_add_lmbs(dev, addr, size, node, &error_abort);
+ spapr_add_lmbs(dev, addr, size, node,
+ spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT),
+ &error_abort);
+
+out:
+ error_propagate(errp, local_err);
+}
+
+typedef struct sPAPRDIMMState {
+ uint32_t nr_lmbs;
+} sPAPRDIMMState;
+
+static void spapr_lmb_release(DeviceState *dev, void *opaque)
+{
+ sPAPRDIMMState *ds = (sPAPRDIMMState *)opaque;
+ HotplugHandler *hotplug_ctrl;
+
+ if (--ds->nr_lmbs) {
+ return;
+ }
+
+ g_free(ds);
+
+ /*
+ * Now that all the LMBs have been removed by the guest, call the
+ * pc-dimm unplug handler to cleanup up the pc-dimm device.
+ */
+ hotplug_ctrl = qdev_get_hotplug_handler(dev);
+ hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort);
+}
+
+static void spapr_del_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size,
+ Error **errp)
+{
+ sPAPRDRConnector *drc;
+ sPAPRDRConnectorClass *drck;
+ uint32_t nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE;
+ int i;
+ sPAPRDIMMState *ds = g_malloc0(sizeof(sPAPRDIMMState));
+ uint64_t addr = addr_start;
+
+ ds->nr_lmbs = nr_lmbs;
+ for (i = 0; i < nr_lmbs; i++) {
+ drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr / SPAPR_MEMORY_BLOCK_SIZE);
+ g_assert(drc);
+
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ drck->detach(drc, dev, spapr_lmb_release, ds, errp);
+ addr += SPAPR_MEMORY_BLOCK_SIZE;
+ }
+
+ drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ addr_start / SPAPR_MEMORY_BLOCK_SIZE);
+ drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
+ spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB,
+ nr_lmbs,
+ drck->get_index(drc));
+}
+
+static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev);
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+ PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ MemoryRegion *mr = ddc->get_memory_region(dimm);
+
+ pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr);
+ object_unparent(OBJECT(dev));
+}
+
+static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ Error *local_err = NULL;
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+ PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
+ MemoryRegion *mr = ddc->get_memory_region(dimm);
+ uint64_t size = memory_region_size(mr);
+ uint64_t addr;
+ addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ spapr_del_lmbs(dev, addr, size, &error_abort);
out:
error_propagate(errp, local_err);
}
@@ -2301,10 +2438,42 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev,
static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
+ sPAPRMachineState *sms = SPAPR_MACHINE(qdev_get_machine());
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
- error_setg(errp, "Memory hot unplug not supported by sPAPR");
+ if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) {
+ spapr_memory_unplug(hotplug_dev, dev, errp);
+ } else {
+ error_setg(errp, "Memory hot unplug not supported for this guest");
+ }
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
+ if (!mc->query_hotpluggable_cpus) {
+ error_setg(errp, "CPU hot unplug not supported on this machine");
+ return;
+ }
+ spapr_core_unplug(hotplug_dev, dev, errp);
+ }
+}
+
+static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ sPAPRMachineState *sms = SPAPR_MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) {
+ spapr_memory_unplug_request(hotplug_dev, dev, errp);
+ } else {
+ /* NOTE: this means there is a window after guest reset, prior to
+ * CAS negotiation, where unplug requests will fail due to the
+ * capability not being detected yet. This is a bit different than
+ * the case with PCI unplug, where the events will be queued and
+ * eventually handled by the guest after boot
+ */
+ error_setg(errp, "Memory hot unplug not supported for this guest");
+ }
} else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) {
if (!mc->query_hotpluggable_cpus) {
error_setg(errp, "CPU hot unplug not supported on this machine");
@@ -2450,6 +2619,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
hc->plug = spapr_machine_device_plug;
hc->unplug = spapr_machine_device_unplug;
mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id;
+ hc->unplug_request = spapr_machine_device_unplug_request;
smc->dr_lmb_enabled = true;
smc->tcg_default_cpu = "POWER8";
@@ -2585,7 +2755,10 @@ static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index,
static void spapr_machine_2_7_instance_options(MachineState *machine)
{
+ sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
+
spapr_machine_2_8_instance_options(machine);
+ spapr->use_hotplug_event_source = false;
}
static void spapr_machine_2_7_class_options(MachineClass *mc)
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index bc922bc86f..e0c14f6b77 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -184,7 +184,7 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
/*
* Setup CPU DT entries only for hotplugged CPUs. For boot time or
- * coldplugged CPUs DT entries are setup in spapr_finalize_fdt().
+ * coldplugged CPUs DT entries are setup in spapr_build_fdt().
*/
if (dev->hotplugged) {
fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr);
diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c
index 6e54fd4743..a0c44ee593 100644
--- a/hw/ppc/spapr_drc.c
+++ b/hw/ppc/spapr_drc.c
@@ -68,6 +68,23 @@ static uint32_t set_isolation_state(sPAPRDRConnector *drc,
}
}
+ /*
+ * Fail any requests to ISOLATE the LMB DRC if this LMB doesn't
+ * belong to a DIMM device that is marked for removal.
+ *
+ * Currently the guest userspace tool drmgr that drives the memory
+ * hotplug/unplug will just try to remove a set of 'removable' LMBs
+ * in response to a hot unplug request that is based on drc-count.
+ * If the LMB being removed doesn't belong to a DIMM device that is
+ * actually being unplugged, fail the isolation request here.
+ */
+ if (drc->type == SPAPR_DR_CONNECTOR_TYPE_LMB) {
+ if ((state == SPAPR_DR_ISOLATION_STATE_ISOLATED) &&
+ !drc->awaiting_release) {
+ return RTAS_OUT_HW_ERROR;
+ }
+ }
+
drc->isolation_state = state;
if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) {
diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c
index 6d3534541c..f85a9c32a7 100644
--- a/hw/ppc/spapr_events.c
+++ b/hw/ppc/spapr_events.c
@@ -40,6 +40,7 @@
#include "hw/ppc/spapr_drc.h"
#include "qemu/help_option.h"
#include "qemu/bcd.h"
+#include "hw/ppc/spapr_ovec.h"
#include <libfdt.h>
struct rtas_error_log {
@@ -174,6 +175,16 @@ struct epow_log_full {
struct rtas_event_log_v6_epow epow;
} QEMU_PACKED;
+union drc_identifier {
+ uint32_t index;
+ uint32_t count;
+ struct {
+ uint32_t count;
+ uint32_t index;
+ } count_indexed;
+ char name[1];
+} QEMU_PACKED;
+
struct rtas_event_log_v6_hp {
#define RTAS_LOG_V6_SECTION_ID_HOTPLUG 0x4850 /* HP */
struct rtas_event_log_v6_section_header hdr;
@@ -190,12 +201,9 @@ struct rtas_event_log_v6_hp {
#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
+#define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4
uint8_t reserved;
- union {
- uint32_t index;
- uint32_t count;
- char name[1];
- } drc;
+ union drc_identifier drc_id;
} QEMU_PACKED;
struct hp_log_full {
@@ -206,28 +214,132 @@ struct hp_log_full {
struct rtas_event_log_v6_hp hp;
} QEMU_PACKED;
-#define EVENT_MASK_INTERNAL_ERRORS 0x80000000
-#define EVENT_MASK_EPOW 0x40000000
-#define EVENT_MASK_HOTPLUG 0x10000000
-#define EVENT_MASK_IO 0x08000000
+typedef enum EventClass {
+ EVENT_CLASS_INTERNAL_ERRORS = 0,
+ EVENT_CLASS_EPOW = 1,
+ EVENT_CLASS_RESERVED = 2,
+ EVENT_CLASS_HOT_PLUG = 3,
+ EVENT_CLASS_IO = 4,
+ EVENT_CLASS_MAX
+} EventClassIndex;
+#define EVENT_CLASS_MASK(index) (1 << (31 - index))
+
+static const char * const event_names[EVENT_CLASS_MAX] = {
+ [EVENT_CLASS_INTERNAL_ERRORS] = "internal-errors",
+ [EVENT_CLASS_EPOW] = "epow-events",
+ [EVENT_CLASS_HOT_PLUG] = "hot-plug-events",
+ [EVENT_CLASS_IO] = "ibm,io-events",
+};
+
+struct sPAPREventSource {
+ int irq;
+ uint32_t mask;
+ bool enabled;
+};
+
+static sPAPREventSource *spapr_event_sources_new(void)
+{
+ return g_new0(sPAPREventSource, EVENT_CLASS_MAX);
+}
+
+static void spapr_event_sources_register(sPAPREventSource *event_sources,
+ EventClassIndex index, int irq)
+{
+ /* we only support 1 irq per event class at the moment */
+ g_assert(event_sources);
+ g_assert(!event_sources[index].enabled);
+ event_sources[index].irq = irq;
+ event_sources[index].mask = EVENT_CLASS_MASK(index);
+ event_sources[index].enabled = true;
+}
+
+static const sPAPREventSource *
+spapr_event_sources_get_source(sPAPREventSource *event_sources,
+ EventClassIndex index)
+{
+ g_assert(index < EVENT_CLASS_MAX);
+ g_assert(event_sources);
+
+ return &event_sources[index];
+}
+
+void spapr_dt_events(sPAPRMachineState *spapr, void *fdt)
+{
+ uint32_t irq_ranges[EVENT_CLASS_MAX * 2];
+ int i, count = 0, event_sources;
+ sPAPREventSource *events = spapr->event_sources;
+
+ g_assert(events);
+
+ _FDT(event_sources = fdt_add_subnode(fdt, 0, "event-sources"));
+
+ for (i = 0, count = 0; i < EVENT_CLASS_MAX; i++) {
+ int node_offset;
+ uint32_t interrupts[2];
+ const sPAPREventSource *source =
+ spapr_event_sources_get_source(events, i);
+ const char *source_name = event_names[i];
+
+ if (!source->enabled) {
+ continue;
+ }
+
+ interrupts[0] = cpu_to_be32(source->irq);
+ interrupts[1] = 0;
-void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq)
+ _FDT(node_offset = fdt_add_subnode(fdt, event_sources, source_name));
+ _FDT(fdt_setprop(fdt, node_offset, "interrupts", interrupts,
+ sizeof(interrupts)));
+
+ irq_ranges[count++] = interrupts[0];
+ irq_ranges[count++] = cpu_to_be32(1);
+ }
+
+ irq_ranges[count] = cpu_to_be32(count);
+ count++;
+
+ _FDT((fdt_setprop(fdt, event_sources, "interrupt-controller", NULL, 0)));
+ _FDT((fdt_setprop_cell(fdt, event_sources, "#interrupt-cells", 2)));
+ _FDT((fdt_setprop(fdt, event_sources, "interrupt-ranges",
+ irq_ranges, count * sizeof(uint32_t))));
+}
+
+static const sPAPREventSource *
+rtas_event_log_to_source(sPAPRMachineState *spapr, int log_type)
{
- uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)};
- uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0};
+ const sPAPREventSource *source;
+
+ g_assert(spapr->event_sources);
+
+ switch (log_type) {
+ case RTAS_LOG_TYPE_HOTPLUG:
+ source = spapr_event_sources_get_source(spapr->event_sources,
+ EVENT_CLASS_HOT_PLUG);
+ if (spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)) {
+ g_assert(source->enabled);
+ break;
+ }
+ /* fall back to epow for legacy hotplug interrupt source */
+ case RTAS_LOG_TYPE_EPOW:
+ source = spapr_event_sources_get_source(spapr->event_sources,
+ EVENT_CLASS_EPOW);
+ break;
+ default:
+ source = NULL;
+ }
- _FDT((fdt_begin_node(fdt, "event-sources")));
+ return source;
+}
- _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
- _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
- _FDT((fdt_property(fdt, "interrupt-ranges",
- irq_ranges, sizeof(irq_ranges))));
+static int rtas_event_log_to_irq(sPAPRMachineState *spapr, int log_type)
+{
+ const sPAPREventSource *source;
- _FDT((fdt_begin_node(fdt, "epow-events")));
- _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts))));
- _FDT((fdt_end_node(fdt)));
+ source = rtas_event_log_to_source(spapr, log_type);
+ g_assert(source);
+ g_assert(source->enabled);
- _FDT((fdt_end_node(fdt)));
+ return source->irq;
}
static void rtas_event_log_queue(int log_type, void *data, bool exception)
@@ -248,19 +360,15 @@ static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask,
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
sPAPREventLogEntry *entry = NULL;
- /* we only queue EPOW events atm. */
- if ((event_mask & EVENT_MASK_EPOW) == 0) {
- return NULL;
- }
-
QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
+ const sPAPREventSource *source =
+ rtas_event_log_to_source(spapr, entry->log_type);
+
if (entry->exception != exception) {
continue;
}
- /* EPOW and hotplug events are surfaced in the same manner */
- if (entry->log_type == RTAS_LOG_TYPE_EPOW ||
- entry->log_type == RTAS_LOG_TYPE_HOTPLUG) {
+ if (source->mask & event_mask) {
break;
}
}
@@ -277,19 +385,15 @@ static bool rtas_event_log_contains(uint32_t event_mask, bool exception)
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
sPAPREventLogEntry *entry = NULL;
- /* we only queue EPOW events atm. */
- if ((event_mask & EVENT_MASK_EPOW) == 0) {
- return false;
- }
-
QTAILQ_FOREACH(entry, &spapr->pending_events, next) {
+ const sPAPREventSource *source =
+ rtas_event_log_to_source(spapr, entry->log_type);
+
if (entry->exception != exception) {
continue;
}
- /* EPOW and hotplug events are surfaced in the same manner */
- if (entry->log_type == RTAS_LOG_TYPE_EPOW ||
- entry->log_type == RTAS_LOG_TYPE_HOTPLUG) {
+ if (source->mask & event_mask) {
return true;
}
}
@@ -377,7 +481,9 @@ static void spapr_powerdown_req(Notifier *n, void *opaque)
rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true);
- qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics,
+ rtas_event_log_to_irq(spapr,
+ RTAS_LOG_TYPE_EPOW)));
}
static void spapr_hotplug_set_signalled(uint32_t drc_index)
@@ -389,7 +495,7 @@ static void spapr_hotplug_set_signalled(uint32_t drc_index)
static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
sPAPRDRConnectorType drc_type,
- uint32_t drc)
+ union drc_identifier *drc_id)
{
sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
struct hp_log_full *new_hp;
@@ -434,7 +540,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
case SPAPR_DR_CONNECTOR_TYPE_PCI:
hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI;
if (hp->hotplug_action == RTAS_LOG_V6_HP_ACTION_ADD) {
- spapr_hotplug_set_signalled(drc);
+ spapr_hotplug_set_signalled(drc_id->index);
}
break;
case SPAPR_DR_CONNECTOR_TYPE_LMB:
@@ -452,48 +558,89 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action,
}
if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT) {
- hp->drc.count = cpu_to_be32(drc);
+ hp->drc_id.count = cpu_to_be32(drc_id->count);
} else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_INDEX) {
- hp->drc.index = cpu_to_be32(drc);
+ hp->drc_id.index = cpu_to_be32(drc_id->index);
+ } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED) {
+ /* we should not be using count_indexed value unless the guest
+ * supports dedicated hotplug event source
+ */
+ g_assert(spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT));
+ hp->drc_id.count_indexed.count =
+ cpu_to_be32(drc_id->count_indexed.count);
+ hp->drc_id.count_indexed.index =
+ cpu_to_be32(drc_id->count_indexed.index);
}
rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true);
- qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
+ qemu_irq_pulse(xics_get_qirq(spapr->xics,
+ rtas_event_log_to_irq(spapr,
+ RTAS_LOG_TYPE_HOTPLUG)));
}
void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
sPAPRDRConnectorType drc_type = drck->get_type(drc);
- uint32_t index = drck->get_index(drc);
+ union drc_identifier drc_id;
+ drc_id.index = drck->get_index(drc);
spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX,
- RTAS_LOG_V6_HP_ACTION_ADD, drc_type, index);
+ RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id);
}
void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc)
{
sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc);
sPAPRDRConnectorType drc_type = drck->get_type(drc);
- uint32_t index = drck->get_index(drc);
+ union drc_identifier drc_id;
+ drc_id.index = drck->get_index(drc);
spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX,
- RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, index);
+ RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
}
void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type,
uint32_t count)
{
+ union drc_identifier drc_id;
+
+ drc_id.count = count;
spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT,
- RTAS_LOG_V6_HP_ACTION_ADD, drc_type, count);
+ RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id);
}
void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type,
uint32_t count)
{
+ union drc_identifier drc_id;
+
+ drc_id.count = count;
spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT,
- RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, count);
+ RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
+}
+
+void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type,
+ uint32_t count, uint32_t index)
+{
+ union drc_identifier drc_id;
+
+ drc_id.count_indexed.count = count;
+ drc_id.count_indexed.index = index;
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED,
+ RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id);
+}
+
+void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type,
+ uint32_t count, uint32_t index)
+{
+ union drc_identifier drc_id;
+
+ drc_id.count_indexed.count = count;
+ drc_id.count_indexed.index = index;
+ spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED,
+ RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id);
}
static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
@@ -505,6 +652,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
uint64_t xinfo;
sPAPREventLogEntry *event;
struct rtas_error_log *hdr;
+ int i;
if ((nargs < 6) || (nargs > 7) || nret != 1) {
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
@@ -541,8 +689,14 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr,
* do the latter here, since our code relies on edge-triggered
* interrupts.
*/
- if (rtas_event_log_contains(mask, true)) {
- qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq));
+ for (i = 0; i < EVENT_CLASS_MAX; i++) {
+ if (rtas_event_log_contains(EVENT_CLASS_MASK(i), true)) {
+ const sPAPREventSource *source =
+ spapr_event_sources_get_source(spapr->event_sources, i);
+
+ g_assert(source->enabled);
+ qemu_irq_pulse(xics_get_qirq(spapr->xics, source->irq));
+ }
}
return;
@@ -594,8 +748,27 @@ out_no_events:
void spapr_events_init(sPAPRMachineState *spapr)
{
QTAILQ_INIT(&spapr->pending_events);
- spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, false,
- &error_fatal);
+
+ spapr->event_sources = spapr_event_sources_new();
+
+ spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_EPOW,
+ xics_spapr_alloc(spapr->xics, 0, false,
+ &error_fatal));
+
+ /* NOTE: if machine supports modern/dedicated hotplug event source,
+ * we add it to the device-tree unconditionally. This means we may
+ * have cases where the source is enabled in QEMU, but unused by the
+ * guest because it does not support modern hotplug events, so we
+ * take care to rely on checking for negotiation of OV5_HP_EVT option
+ * before attempting to use it to signal events, rather than simply
+ * checking that it's enabled.
+ */
+ if (spapr->use_hotplug_event_source) {
+ spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_HOT_PLUG,
+ xics_spapr_alloc(spapr->xics, 0, false,
+ &error_fatal));
+ }
+
spapr->epow_notifier.notify = spapr_powerdown_req;
qemu_register_powerdown_notifier(&spapr->epow_notifier);
spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception",
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index c5e7e8c995..7c46d4625b 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -11,6 +11,7 @@
#include "trace.h"
#include "sysemu/kvm.h"
#include "kvm_ppc.h"
+#include "hw/ppc/spapr_ovec.h"
struct SPRSyncState {
int spr;
@@ -880,32 +881,6 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return ret;
}
-/*
- * Return the offset to the requested option vector @vector in the
- * option vector table @table.
- */
-static target_ulong cas_get_option_vector(int vector, target_ulong table)
-{
- int i;
- char nr_vectors, nr_entries;
-
- if (!table) {
- return 0;
- }
-
- nr_vectors = (ldl_phys(&address_space_memory, table) >> 24) + 1;
- if (!vector || vector > nr_vectors) {
- return 0;
- }
- table++; /* skip nr option vectors */
-
- for (i = 0; i < vector - 1; i++) {
- nr_entries = ldl_phys(&address_space_memory, table) >> 24;
- table += nr_entries + 2;
- }
- return table;
-}
-
typedef struct {
uint32_t cpu_version;
Error *err;
@@ -961,23 +936,21 @@ static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr,
}
}
-#define OV5_DRCONF_MEMORY 0x20
-
static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
sPAPRMachineState *spapr,
target_ulong opcode,
target_ulong *args)
{
target_ulong list = ppc64_phys_to_real(args[0]);
- target_ulong ov_table, ov5;
+ target_ulong ov_table;
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_);
CPUState *cs;
- bool cpu_match = false, cpu_update = true, memory_update = false;
+ bool cpu_match = false, cpu_update = true;
unsigned old_cpu_version = cpu_->cpu_version;
unsigned compat_lvl = 0, cpu_version = 0;
unsigned max_lvl = get_compat_level(cpu_->max_compat);
int counter;
- char ov5_byte2;
+ sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates;
/* Parse PVR list */
for (counter = 0; counter < 512; ++counter) {
@@ -1033,19 +1006,34 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_,
/* For the future use: here @ov_table points to the first option vector */
ov_table = list;
- ov5 = cas_get_option_vector(5, ov_table);
- if (!ov5) {
- return H_SUCCESS;
- }
+ ov5_guest = spapr_ovec_parse_vector(ov_table, 5);
+
+ /* NOTE: there are actually a number of ov5 bits where input from the
+ * guest is always zero, and the platform/QEMU enables them independently
+ * of guest input. To model these properly we'd want some sort of mask,
+ * but since they only currently apply to memory migration as defined
+ * by LoPAPR 1.1, 14.5.4.8, which QEMU doesn't implement, we don't need
+ * to worry about this for now.
+ */
+ ov5_cas_old = spapr_ovec_clone(spapr->ov5_cas);
+ /* full range of negotiated ov5 capabilities */
+ spapr_ovec_intersect(spapr->ov5_cas, spapr->ov5, ov5_guest);
+ spapr_ovec_cleanup(ov5_guest);
+ /* capabilities that have been added since CAS-generated guest reset.
+ * if capabilities have since been removed, generate another reset
+ */
+ ov5_updates = spapr_ovec_new();
+ spapr->cas_reboot = spapr_ovec_diff(ov5_updates,
+ ov5_cas_old, spapr->ov5_cas);
- /* @list now points to OV 5 */
- ov5_byte2 = ldub_phys(&address_space_memory, ov5 + 2);
- if (ov5_byte2 & OV5_DRCONF_MEMORY) {
- memory_update = true;
+ if (!spapr->cas_reboot) {
+ spapr->cas_reboot =
+ (spapr_h_cas_compose_response(spapr, args[1], args[2], cpu_update,
+ ov5_updates) != 0);
}
+ spapr_ovec_cleanup(ov5_updates);
- if (spapr_h_cas_compose_response(spapr, args[1], args[2],
- cpu_update, memory_update)) {
+ if (spapr->cas_reboot) {
qemu_system_reset_request();
}
diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c
new file mode 100644
index 0000000000..c2a0d18577
--- /dev/null
+++ b/hw/ppc/spapr_ovec.c
@@ -0,0 +1,242 @@
+/*
+ * QEMU SPAPR Architecture Option Vector Helper Functions
+ *
+ * Copyright IBM Corp. 2016
+ *
+ * Authors:
+ * Bharata B Rao <bharata@linux.vnet.ibm.com>
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/ppc/spapr_ovec.h"
+#include "qemu/bitmap.h"
+#include "exec/address-spaces.h"
+#include "qemu/error-report.h"
+#include <libfdt.h>
+
+/* #define DEBUG_SPAPR_OVEC */
+
+#ifdef DEBUG_SPAPR_OVEC
+#define DPRINTFN(fmt, ...) \
+ do { fprintf(stderr, fmt "\n", ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTFN(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define OV_MAXBYTES 256 /* not including length byte */
+#define OV_MAXBITS (OV_MAXBYTES * BITS_PER_BYTE)
+
+/* we *could* work with bitmaps directly, but handling the bitmap privately
+ * allows us to more safely make assumptions about the bitmap size and
+ * simplify the calling code somewhat
+ */
+struct sPAPROptionVector {
+ unsigned long *bitmap;
+};
+
+sPAPROptionVector *spapr_ovec_new(void)
+{
+ sPAPROptionVector *ov;
+
+ ov = g_new0(sPAPROptionVector, 1);
+ ov->bitmap = bitmap_new(OV_MAXBITS);
+
+ return ov;
+}
+
+sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig)
+{
+ sPAPROptionVector *ov;
+
+ g_assert(ov_orig);
+
+ ov = spapr_ovec_new();
+ bitmap_copy(ov->bitmap, ov_orig->bitmap, OV_MAXBITS);
+
+ return ov;
+}
+
+void spapr_ovec_intersect(sPAPROptionVector *ov,
+ sPAPROptionVector *ov1,
+ sPAPROptionVector *ov2)
+{
+ g_assert(ov);
+ g_assert(ov1);
+ g_assert(ov2);
+
+ bitmap_and(ov->bitmap, ov1->bitmap, ov2->bitmap, OV_MAXBITS);
+}
+
+/* returns true if options bits were removed, false otherwise */
+bool spapr_ovec_diff(sPAPROptionVector *ov,
+ sPAPROptionVector *ov_old,
+ sPAPROptionVector *ov_new)
+{
+ unsigned long *change_mask = bitmap_new(OV_MAXBITS);
+ unsigned long *removed_bits = bitmap_new(OV_MAXBITS);
+ bool bits_were_removed = false;
+
+ g_assert(ov);
+ g_assert(ov_old);
+ g_assert(ov_new);
+
+ bitmap_xor(change_mask, ov_old->bitmap, ov_new->bitmap, OV_MAXBITS);
+ bitmap_and(ov->bitmap, ov_new->bitmap, change_mask, OV_MAXBITS);
+ bitmap_and(removed_bits, ov_old->bitmap, change_mask, OV_MAXBITS);
+
+ if (!bitmap_empty(removed_bits, OV_MAXBITS)) {
+ bits_were_removed = true;
+ }
+
+ g_free(change_mask);
+ g_free(removed_bits);
+
+ return bits_were_removed;
+}
+
+void spapr_ovec_cleanup(sPAPROptionVector *ov)
+{
+ if (ov) {
+ g_free(ov->bitmap);
+ g_free(ov);
+ }
+}
+
+void spapr_ovec_set(sPAPROptionVector *ov, long bitnr)
+{
+ g_assert(ov);
+ g_assert_cmpint(bitnr, <, OV_MAXBITS);
+
+ set_bit(bitnr, ov->bitmap);
+}
+
+void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr)
+{
+ g_assert(ov);
+ g_assert_cmpint(bitnr, <, OV_MAXBITS);
+
+ clear_bit(bitnr, ov->bitmap);
+}
+
+bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr)
+{
+ g_assert(ov);
+ g_assert_cmpint(bitnr, <, OV_MAXBITS);
+
+ return test_bit(bitnr, ov->bitmap) ? true : false;
+}
+
+static void guest_byte_to_bitmap(uint8_t entry, unsigned long *bitmap,
+ long bitmap_offset)
+{
+ int i;
+
+ for (i = 0; i < BITS_PER_BYTE; i++) {
+ if (entry & (1 << (BITS_PER_BYTE - 1 - i))) {
+ bitmap_set(bitmap, bitmap_offset + i, 1);
+ }
+ }
+}
+
+static uint8_t guest_byte_from_bitmap(unsigned long *bitmap, long bitmap_offset)
+{
+ uint8_t entry = 0;
+ int i;
+
+ for (i = 0; i < BITS_PER_BYTE; i++) {
+ if (test_bit(bitmap_offset + i, bitmap)) {
+ entry |= (1 << (BITS_PER_BYTE - 1 - i));
+ }
+ }
+
+ return entry;
+}
+
+static target_ulong vector_addr(target_ulong table_addr, int vector)
+{
+ uint16_t vector_count, vector_len;
+ int i;
+
+ vector_count = ldub_phys(&address_space_memory, table_addr) + 1;
+ if (vector > vector_count) {
+ return 0;
+ }
+ table_addr++; /* skip nr option vectors */
+
+ for (i = 0; i < vector - 1; i++) {
+ vector_len = ldub_phys(&address_space_memory, table_addr) + 1;
+ table_addr += vector_len + 1; /* bit-vector + length byte */
+ }
+ return table_addr;
+}
+
+sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector)
+{
+ sPAPROptionVector *ov;
+ target_ulong addr;
+ uint16_t vector_len;
+ int i;
+
+ g_assert(table_addr);
+ g_assert_cmpint(vector, >=, 1); /* vector numbering starts at 1 */
+
+ addr = vector_addr(table_addr, vector);
+ if (!addr) {
+ /* specified vector isn't present */
+ return NULL;
+ }
+
+ vector_len = ldub_phys(&address_space_memory, addr++) + 1;
+ g_assert_cmpint(vector_len, <=, OV_MAXBYTES);
+ ov = spapr_ovec_new();
+
+ for (i = 0; i < vector_len; i++) {
+ uint8_t entry = ldub_phys(&address_space_memory, addr + i);
+ if (entry) {
+ DPRINTFN("read guest vector %2d, byte %3d / %3d: 0x%.2x",
+ vector, i + 1, vector_len, entry);
+ guest_byte_to_bitmap(entry, ov->bitmap, i * BITS_PER_BYTE);
+ }
+ }
+
+ return ov;
+}
+
+int spapr_ovec_populate_dt(void *fdt, int fdt_offset,
+ sPAPROptionVector *ov, const char *name)
+{
+ uint8_t vec[OV_MAXBYTES + 1];
+ uint16_t vec_len;
+ unsigned long lastbit;
+ int i;
+
+ g_assert(ov);
+
+ lastbit = find_last_bit(ov->bitmap, OV_MAXBITS);
+ /* if no bits are set, include at least 1 byte of the vector so we can
+ * still encoded this in the device tree while abiding by the same
+ * encoding/sizing expected in ibm,client-architecture-support
+ */
+ vec_len = (lastbit == OV_MAXBITS) ? 1 : lastbit / BITS_PER_BYTE + 1;
+ g_assert_cmpint(vec_len, <=, OV_MAXBYTES);
+ /* guest expects vector len encoded as vec_len - 1, since the length byte
+ * is assumed and not included, and the first byte of the vector
+ * is assumed as well
+ */
+ vec[0] = vec_len - 1;
+
+ for (i = 1; i < vec_len + 1; i++) {
+ vec[i] = guest_byte_from_bitmap(ov->bitmap, (i - 1) * BITS_PER_BYTE);
+ if (vec[i]) {
+ DPRINTFN("encoding guest vector byte %3d / %3d: 0x%.2x",
+ i, vec_len, vec[i]);
+ }
+ }
+
+ return fdt_setprop(fdt, fdt_offset, name, vec, vec_len);
+}
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 2a1ccf59ea..7cde30ee09 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -1392,6 +1392,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
return;
}
+ if (sphb->numa_node != -1 &&
+ (sphb->numa_node >= MAX_NODES || !numa_info[sphb->numa_node].present)) {
+ error_setg(errp, "Invalid NUMA node ID for PCI host bridge");
+ return;
+ }
+
sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid);
namebuf = alloca(strlen(sphb->dtbusname) + 32);
@@ -1880,7 +1886,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb,
}
/* Advertise NUMA via ibm,associativity */
- if (nb_numa_nodes > 1) {
+ if (phb->numa_node != -1) {
_FDT(fdt_setprop(fdt, bus_off, "ibm,associativity", associativity,
sizeof(associativity)));
}
diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c
index 0db84c816d..bb19944686 100644
--- a/hw/ppc/spapr_rtas.c
+++ b/hw/ppc/spapr_rtas.c
@@ -46,6 +46,7 @@
#include "hw/ppc/spapr_drc.h"
#include "qemu/cutils.h"
#include "trace.h"
+#include "hw/ppc/fdt.h"
static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPRMachineState *spapr,
uint32_t drc_index)
@@ -710,78 +711,60 @@ void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn)
rtas_table[token].fn = fn;
}
-int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
- hwaddr rtas_size)
+void spapr_dt_rtas_tokens(void *fdt, int rtas)
{
- int ret;
int i;
- uint32_t lrdr_capacity[5];
- MachineState *machine = MACHINE(qdev_get_machine());
- sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
- uint64_t max_hotplug_addr = spapr->hotplug_memory.base +
- memory_region_size(&spapr->hotplug_memory.mr);
- ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
+ for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) {
+ struct rtas_call *call = &rtas_table[i];
+
+ if (!call->name) {
+ continue;
+ }
+
+ _FDT(fdt_setprop_cell(fdt, rtas, call->name, i + RTAS_TOKEN_BASE));
+ }
+}
+
+void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr)
+{
+ int rtas_node;
+ int ret;
+
+ /* Copy RTAS blob into guest RAM */
+ cpu_physical_memory_write(addr, spapr->rtas_blob, spapr->rtas_size);
+
+ ret = fdt_add_mem_rsv(fdt, addr, spapr->rtas_size);
if (ret < 0) {
error_report("Couldn't add RTAS reserve entry: %s",
- fdt_strerror(ret));
- return ret;
+ fdt_strerror(ret));
+ exit(1);
}
- ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base",
- rtas_addr);
+ /* Update the device tree with the blob's location */
+ rtas_node = fdt_path_offset(fdt, "/rtas");
+ assert(rtas_node >= 0);
+
+ ret = fdt_setprop_cell(fdt, rtas_node, "linux,rtas-base", addr);
if (ret < 0) {
error_report("Couldn't add linux,rtas-base property: %s",
- fdt_strerror(ret));
- return ret;
+ fdt_strerror(ret));
+ exit(1);
}
- ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
- rtas_addr);
+ ret = fdt_setprop_cell(fdt, rtas_node, "linux,rtas-entry", addr);
if (ret < 0) {
error_report("Couldn't add linux,rtas-entry property: %s",
- fdt_strerror(ret));
- return ret;
+ fdt_strerror(ret));
+ exit(1);
}
- ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size",
- rtas_size);
+ ret = fdt_setprop_cell(fdt, rtas_node, "rtas-size", spapr->rtas_size);
if (ret < 0) {
error_report("Couldn't add rtas-size property: %s",
- fdt_strerror(ret));
- return ret;
+ fdt_strerror(ret));
+ exit(1);
}
-
- for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) {
- struct rtas_call *call = &rtas_table[i];
-
- if (!call->name) {
- continue;
- }
-
- ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name,
- i + RTAS_TOKEN_BASE);
- if (ret < 0) {
- error_report("Couldn't add rtas token for %s: %s",
- call->name, fdt_strerror(ret));
- return ret;
- }
-
- }
-
- lrdr_capacity[0] = cpu_to_be32(max_hotplug_addr >> 32);
- lrdr_capacity[1] = cpu_to_be32(max_hotplug_addr & 0xffffffff);
- lrdr_capacity[2] = 0;
- lrdr_capacity[3] = cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE);
- lrdr_capacity[4] = cpu_to_be32(max_cpus/smp_threads);
- ret = qemu_fdt_setprop(fdt, "/rtas", "ibm,lrdr-capacity", lrdr_capacity,
- sizeof(lrdr_capacity));
- if (ret < 0) {
- error_report("Couldn't add ibm,lrdr-capacity rtas property");
- return ret;
- }
-
- return 0;
}
static void core_rtas_register_types(void)
diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c
index 3648aa5960..cc1e09c568 100644
--- a/hw/ppc/spapr_vio.c
+++ b/hw/ppc/spapr_vio.c
@@ -36,6 +36,7 @@
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
#include "hw/ppc/xics.h"
+#include "hw/ppc/fdt.h"
#include "trace.h"
#include <libfdt.h>
@@ -624,11 +625,21 @@ static int compare_reg(const void *p1, const void *p2)
return 1;
}
-int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
+void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt)
{
DeviceState *qdev, **qdevs;
BusChild *kid;
int i, num, ret = 0;
+ int node;
+
+ _FDT(node = fdt_add_subnode(fdt, 0, "vdevice"));
+
+ _FDT(fdt_setprop_string(fdt, node, "device_type", "vdevice"));
+ _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,vdevice"));
+ _FDT(fdt_setprop_cell(fdt, node, "#address-cells", 1));
+ _FDT(fdt_setprop_cell(fdt, node, "#size-cells", 0));
+ _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2));
+ _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0));
/* Count qdevs on the bus list */
num = 0;
@@ -650,43 +661,32 @@ int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
* to know that will mean they are in forward order in the tree. */
for (i = num - 1; i >= 0; i--) {
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)(qdevs[i]);
+ VIOsPAPRDeviceClass *vdc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
ret = vio_make_devnode(dev, fdt);
-
if (ret < 0) {
- goto out;
+ error_report("Couldn't create device node /vdevice/%s@%"PRIx32,
+ vdc->dt_name, dev->reg);
+ exit(1);
}
}
- ret = 0;
-out:
g_free(qdevs);
-
- return ret;
}
-int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus)
+gchar *spapr_vio_stdout_path(VIOsPAPRBus *bus)
{
VIOsPAPRDevice *dev;
char *name, *path;
- int ret, offset;
dev = spapr_vty_get_default(bus);
- if (!dev)
- return 0;
-
- offset = fdt_path_offset(fdt, "/chosen");
- if (offset < 0) {
- return offset;
+ if (!dev) {
+ return NULL;
}
name = spapr_vio_get_dev_name(DEVICE(dev));
path = g_strdup_printf("/vdevice/%s", name);
- ret = fdt_setprop_string(fdt, offset, "linux,stdout-path", path);
-
g_free(name);
- g_free(path);
-
- return ret;
+ return path;
}
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index b173b94949..9424f0e057 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -189,13 +189,11 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s)
assert(s->ctx == iothread_get_aio_context(vs->conf.iothread));
aio_context_acquire(s->ctx);
-
virtio_scsi_clear_aio(s);
+ aio_context_release(s->ctx);
blk_drain_all(); /* ensure there are no in-flight requests */
- aio_context_release(s->ctx);
-
for (i = 0; i < vs->conf.num_queues + 2; i++) {
virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
}
diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c
index b3915e4fd6..6224288ac3 100644
--- a/hw/sparc/sun4m.c
+++ b/hw/sparc/sun4m.c
@@ -35,10 +35,11 @@
#include "sysemu/sysemu.h"
#include "net/net.h"
#include "hw/boards.h"
-#include "hw/nvram/openbios_firmware_abi.h"
#include "hw/scsi/esp.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h"
+#include "hw/nvram/sun_nvram.h"
+#include "hw/nvram/chrp_nvram.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/char/escc.h"
#include "hw/empty_slot.h"
@@ -117,39 +118,17 @@ static void nvram_init(Nvram *nvram, uint8_t *macaddr,
int nvram_machine_id, const char *arch)
{
unsigned int i;
- uint32_t start, end;
+ int sysp_end;
uint8_t image[0x1ff0];
- struct OpenBIOS_nvpart_v1 *part_header;
NvramClass *k = NVRAM_GET_CLASS(nvram);
memset(image, '\0', sizeof(image));
- start = 0;
+ /* OpenBIOS nvram variables partition */
+ sysp_end = chrp_nvram_create_system_partition(image, 0);
- // OpenBIOS nvram variables
- // Variable partition
- part_header = (struct OpenBIOS_nvpart_v1 *)&image[start];
- part_header->signature = OPENBIOS_PART_SYSTEM;
- pstrcpy(part_header->name, sizeof(part_header->name), "system");
-
- end = start + sizeof(struct OpenBIOS_nvpart_v1);
- for (i = 0; i < nb_prom_envs; i++)
- end = OpenBIOS_set_var(image, end, prom_envs[i]);
-
- // End marker
- image[end++] = '\0';
-
- end = start + ((end - start + 15) & ~15);
- OpenBIOS_finish_partition(part_header, end - start);
-
- // free partition
- start = end;
- part_header = (struct OpenBIOS_nvpart_v1 *)&image[start];
- part_header->signature = OPENBIOS_PART_FREE;
- pstrcpy(part_header->name, sizeof(part_header->name), "free");
-
- end = 0x1fd0;
- OpenBIOS_finish_partition(part_header, end - start);
+ /* Free space partition */
+ chrp_nvram_create_free_partition(&image[sysp_end], 0x1fd0 - sysp_end);
Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr,
nvram_machine_id);
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index 7b8134ef51..271d8bc592 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -36,7 +36,8 @@
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
-#include "hw/nvram/openbios_firmware_abi.h"
+#include "hw/nvram/sun_nvram.h"
+#include "hw/nvram/chrp_nvram.h"
#include "hw/nvram/fw_cfg.h"
#include "hw/sysbus.h"
#include "hw/ide.h"
@@ -124,39 +125,17 @@ static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size,
const uint8_t *macaddr)
{
unsigned int i;
- uint32_t start, end;
+ int sysp_end;
uint8_t image[0x1ff0];
- struct OpenBIOS_nvpart_v1 *part_header;
NvramClass *k = NVRAM_GET_CLASS(nvram);
memset(image, '\0', sizeof(image));
- start = 0;
+ /* OpenBIOS nvram variables partition */
+ sysp_end = chrp_nvram_create_system_partition(image, 0);
- // OpenBIOS nvram variables
- // Variable partition
- part_header = (struct OpenBIOS_nvpart_v1 *)&image[start];
- part_header->signature = OPENBIOS_PART_SYSTEM;
- pstrcpy(part_header->name, sizeof(part_header->name), "system");
-
- end = start + sizeof(struct OpenBIOS_nvpart_v1);
- for (i = 0; i < nb_prom_envs; i++)
- end = OpenBIOS_set_var(image, end, prom_envs[i]);
-
- // End marker
- image[end++] = '\0';
-
- end = start + ((end - start + 15) & ~15);
- OpenBIOS_finish_partition(part_header, end - start);
-
- // free partition
- start = end;
- part_header = (struct OpenBIOS_nvpart_v1 *)&image[start];
- part_header->signature = OPENBIOS_PART_FREE;
- pstrcpy(part_header->name, sizeof(part_header->name), "free");
-
- end = 0x1fd0;
- OpenBIOS_finish_partition(part_header, end - start);
+ /* Free space partition */
+ chrp_nvram_create_free_partition(&image[sysp_end], 0x1fd0 - sysp_end);
Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, 0x80);
diff --git a/include/block/aio.h b/include/block/aio.h
index b9fe2cb37e..c7ae27c91c 100644
--- a/include/block/aio.h
+++ b/include/block/aio.h
@@ -18,7 +18,6 @@
#include "qemu/queue.h"
#include "qemu/event_notifier.h"
#include "qemu/thread.h"
-#include "qemu/rfifolock.h"
#include "qemu/timer.h"
typedef struct BlockAIOCB BlockAIOCB;
@@ -54,7 +53,7 @@ struct AioContext {
GSource source;
/* Protects all fields from multi-threaded access */
- RFifoLock lock;
+ QemuRecMutex lock;
/* The list of registered AIO handlers */
QLIST_HEAD(, AioHandler) aio_handlers;
@@ -116,9 +115,6 @@ struct AioContext {
bool notified;
EventNotifier notifier;
- /* Scheduling this BH forces the event loop it iterate */
- QEMUBH *notify_dummy_bh;
-
/* Thread pool for performing work and receiving completion callbacks */
struct ThreadPool *thread_pool;
@@ -453,6 +449,24 @@ static inline bool aio_node_check(AioContext *ctx, bool is_external)
}
/**
+ * Return the AioContext whose event loop runs in the current thread.
+ *
+ * If called from an IOThread this will be the IOThread's AioContext. If
+ * called from another thread it will be the main loop AioContext.
+ */
+AioContext *qemu_get_current_aio_context(void);
+
+/**
+ * @ctx: the aio context
+ *
+ * Return whether we are running in the I/O thread that manages @ctx.
+ */
+static inline bool aio_context_in_iothread(AioContext *ctx)
+{
+ return ctx == qemu_get_current_aio_context();
+}
+
+/**
* aio_context_setup:
* @ctx: the aio context
*
diff --git a/include/block/block.h b/include/block/block.h
index 398a050176..b7dc7d54ae 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -218,7 +218,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
BlockDriverState *bs,
QDict *options, int flags);
-int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
+int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp);
int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp);
int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp);
@@ -334,6 +334,35 @@ void bdrv_drain(BlockDriverState *bs);
void coroutine_fn bdrv_co_drain(BlockDriverState *bs);
void bdrv_drain_all(void);
+#define BDRV_POLL_WHILE(bs, cond) ({ \
+ bool waited_ = false; \
+ BlockDriverState *bs_ = (bs); \
+ AioContext *ctx_ = bdrv_get_aio_context(bs_); \
+ if (aio_context_in_iothread(ctx_)) { \
+ while ((cond)) { \
+ aio_poll(ctx_, true); \
+ waited_ = true; \
+ } \
+ } else { \
+ assert(qemu_get_current_aio_context() == \
+ qemu_get_aio_context()); \
+ /* Ask bdrv_dec_in_flight to wake up the main \
+ * QEMU AioContext. Extra I/O threads never take \
+ * other I/O threads' AioContexts (see for example \
+ * block_job_defer_to_main_loop for how to do it). \
+ */ \
+ assert(!bs_->wakeup); \
+ bs_->wakeup = true; \
+ while ((cond)) { \
+ aio_context_release(ctx_); \
+ aio_poll(qemu_get_aio_context(), true); \
+ aio_context_acquire(ctx_); \
+ waited_ = true; \
+ } \
+ bs_->wakeup = false; \
+ } \
+ waited_; })
+
int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int count);
int bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, int count);
int bdrv_has_zero_init_1(BlockDriverState *bs);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index e96e9ada57..e7ff58419c 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -62,8 +62,6 @@
enum BdrvTrackedRequestType {
BDRV_TRACKED_READ,
BDRV_TRACKED_WRITE,
- BDRV_TRACKED_FLUSH,
- BDRV_TRACKED_IOCTL,
BDRV_TRACKED_DISCARD,
};
@@ -445,7 +443,7 @@ struct BlockDriverState {
note this is a reference count */
CoQueue flush_queue; /* Serializing flush queue */
- BdrvTrackedRequest *active_flush_req; /* Flush request in flight */
+ bool active_flush_req; /* Flush request in flight? */
unsigned int write_gen; /* Current data generation */
unsigned int flushed_gen; /* Flushed write generation */
@@ -473,9 +471,12 @@ struct BlockDriverState {
/* Callback before write request is processed */
NotifierWithReturnList before_write_notifiers;
- /* number of in-flight serialising requests */
+ /* number of in-flight requests; overall and serialising */
+ unsigned int in_flight;
unsigned int serialising_in_flight;
+ bool wakeup;
+
/* Offset after the highest byte written to */
uint64_t wr_highest_offset;
@@ -634,6 +635,21 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
void (*aio_context_detached)(void *),
void *opaque);
+/**
+ * bdrv_wakeup:
+ * @bs: The BlockDriverState for which an I/O operation has been completed.
+ *
+ * Wake up the main thread if it is waiting on BDRV_POLL_WHILE. During
+ * synchronous I/O on a BlockDriverState that is attached to another
+ * I/O thread, the main thread lets the I/O thread's event loop run,
+ * waiting for the I/O operation to complete. A bdrv_wakeup will wake
+ * up the main thread if necessary.
+ *
+ * Manual calls to bdrv_wakeup are rarely necessary, because
+ * bdrv_dec_in_flight already calls it.
+ */
+void bdrv_wakeup(BlockDriverState *bs);
+
#ifdef _WIN32
int is_windows_drive(const char *filename);
#endif
@@ -787,6 +803,9 @@ bool bdrv_requests_pending(BlockDriverState *bs);
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
+void bdrv_inc_in_flight(BlockDriverState *bs);
+void bdrv_dec_in_flight(BlockDriverState *bs);
+
void blockdev_close_all_bdrv_states(void);
#endif /* BLOCK_INT_H */
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
index 4ddb4ae2e1..2bb39f4d29 100644
--- a/include/block/blockjob.h
+++ b/include/block/blockjob.h
@@ -92,6 +92,13 @@ typedef struct BlockJobDriver {
* besides job->blk to the new AioContext.
*/
void (*attached_aio_context)(BlockJob *job, AioContext *new_context);
+
+ /*
+ * If the callback is not NULL, it will be invoked when the job has to be
+ * synchronously cancelled or completed; it should drain BlockDriverStates
+ * as required to ensure progress.
+ */
+ void (*drain)(BlockJob *job);
} BlockJobDriver;
/**
diff --git a/include/glib-compat.h b/include/glib-compat.h
index 8093163bee..3f8370b3e4 100644
--- a/include/glib-compat.h
+++ b/include/glib-compat.h
@@ -304,4 +304,15 @@ static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func)
}
#endif
+#if !GLIB_CHECK_VERSION(2, 26, 0)
+static inline void g_source_set_name(GSource *source, const char *name)
+{
+ /* This is just a debugging aid, so leaving it a no-op */
+}
+static inline void g_source_set_name_by_id(guint tag, const char *name)
+{
+ /* This is just a debugging aid, so leaving it a no-op */
+}
+#endif
+
#endif
diff --git a/include/hw/nvram/chrp_nvram.h b/include/hw/nvram/chrp_nvram.h
new file mode 100644
index 0000000000..b4f5b2b104
--- /dev/null
+++ b/include/hw/nvram/chrp_nvram.h
@@ -0,0 +1,54 @@
+/*
+ * Common Hardware Reference Platform NVRAM functions.
+ *
+ * This code 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/>.
+ */
+
+#ifndef CHRP_NVRAM_H
+#define CHRP_NVRAM_H
+
+/* OpenBIOS NVRAM partition */
+typedef struct {
+ uint8_t signature;
+ uint8_t checksum;
+ uint16_t len; /* Big endian, length divided by 16 */
+ char name[12];
+} ChrpNvramPartHdr;
+
+#define CHRP_NVPART_SYSTEM 0x70
+#define CHRP_NVPART_FREE 0x7f
+
+static inline void
+chrp_nvram_finish_partition(ChrpNvramPartHdr *header, uint32_t size)
+{
+ unsigned int i, sum;
+ uint8_t *tmpptr;
+
+ /* Length divided by 16 */
+ header->len = cpu_to_be16(size >> 4);
+
+ /* Checksum */
+ tmpptr = (uint8_t *)header;
+ sum = *tmpptr;
+ for (i = 0; i < 14; i++) {
+ sum += tmpptr[2 + i];
+ sum = (sum + ((sum & 0xff00) >> 8)) & 0xff;
+ }
+ header->checksum = sum & 0xff;
+}
+
+int chrp_nvram_create_system_partition(uint8_t *data, int min_len);
+int chrp_nvram_create_free_partition(uint8_t *data, int len);
+
+#endif
diff --git a/include/hw/nvram/openbios_firmware_abi.h b/include/hw/nvram/sun_nvram.h
index 74cfd56180..68eaa60308 100644
--- a/include/hw/nvram/openbios_firmware_abi.h
+++ b/include/hw/nvram/sun_nvram.h
@@ -1,46 +1,5 @@
-#ifndef OPENBIOS_FIRMWARE_ABI_H
-#define OPENBIOS_FIRMWARE_ABI_H
-
-/* OpenBIOS NVRAM partition */
-struct OpenBIOS_nvpart_v1 {
- uint8_t signature;
- uint8_t checksum;
- uint16_t len; // BE, length divided by 16
- char name[12];
-};
-
-#define OPENBIOS_PART_SYSTEM 0x70
-#define OPENBIOS_PART_FREE 0x7f
-
-static inline void
-OpenBIOS_finish_partition(struct OpenBIOS_nvpart_v1 *header, uint32_t size)
-{
- unsigned int i, sum;
- uint8_t *tmpptr;
-
- // Length divided by 16
- header->len = cpu_to_be16(size >> 4);
-
- // Checksum
- tmpptr = (uint8_t *)header;
- sum = *tmpptr;
- for (i = 0; i < 14; i++) {
- sum += tmpptr[2 + i];
- sum = (sum + ((sum & 0xff00) >> 8)) & 0xff;
- }
- header->checksum = sum & 0xff;
-}
-
-static inline uint32_t
-OpenBIOS_set_var(uint8_t *nvram, uint32_t addr, const char *str)
-{
- uint32_t len;
-
- len = strlen(str) + 1;
- memcpy(&nvram[addr], str, len);
-
- return addr + len;
-}
+#ifndef SUN_NVRAM_H
+#define SUN_NVRAM_H
/* Sun IDPROM structure at the end of NVRAM */
/* from http://www.squirrel.com/squirrel/sun-nvram-hostid.faq.html */
@@ -72,4 +31,4 @@ Sun_init_header(struct Sun_nvram *header, const uint8_t *macaddr, int machine_id
header->checksum = tmp;
}
-#endif /* OPENBIOS_FIRMWARE_ABI_H */
+#endif /* SUN_NVRAM_H */
diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h
new file mode 100644
index 0000000000..02ac1c5f42
--- /dev/null
+++ b/include/hw/ppc/pnv.h
@@ -0,0 +1,129 @@
+/*
+ * QEMU PowerPC PowerNV various definitions
+ *
+ * Copyright (c) 2014-2016 BenH, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_H
+#define _PPC_PNV_H
+
+#include "hw/boards.h"
+#include "hw/sysbus.h"
+#include "hw/ppc/pnv_xscom.h"
+#include "hw/ppc/pnv_lpc.h"
+
+#define TYPE_PNV_CHIP "powernv-chip"
+#define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
+#define PNV_CHIP_CLASS(klass) \
+ OBJECT_CLASS_CHECK(PnvChipClass, (klass), TYPE_PNV_CHIP)
+#define PNV_CHIP_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(PnvChipClass, (obj), TYPE_PNV_CHIP)
+
+typedef enum PnvChipType {
+ PNV_CHIP_POWER8E, /* AKA Murano (default) */
+ PNV_CHIP_POWER8, /* AKA Venice */
+ PNV_CHIP_POWER8NVL, /* AKA Naples */
+ PNV_CHIP_POWER9, /* AKA Nimbus */
+} PnvChipType;
+
+typedef struct PnvChip {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ uint32_t chip_id;
+ uint64_t ram_start;
+ uint64_t ram_size;
+
+ uint32_t nr_cores;
+ uint64_t cores_mask;
+ void *cores;
+
+ hwaddr xscom_base;
+ MemoryRegion xscom_mmio;
+ MemoryRegion xscom;
+ AddressSpace xscom_as;
+
+ PnvLpcController lpc;
+} PnvChip;
+
+typedef struct PnvChipClass {
+ /*< private >*/
+ SysBusDeviceClass parent_class;
+
+ /*< public >*/
+ const char *cpu_model;
+ PnvChipType chip_type;
+ uint64_t chip_cfam_id;
+ uint64_t cores_mask;
+
+ hwaddr xscom_base;
+
+ uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id);
+} PnvChipClass;
+
+#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E"
+#define PNV_CHIP_POWER8E(obj) \
+ OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8E)
+
+#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8"
+#define PNV_CHIP_POWER8(obj) \
+ OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8)
+
+#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL"
+#define PNV_CHIP_POWER8NVL(obj) \
+ OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8NVL)
+
+#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9"
+#define PNV_CHIP_POWER9(obj) \
+ OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9)
+
+/*
+ * This generates a HW chip id depending on an index:
+ *
+ * 0x0, 0x1, 0x10, 0x11
+ *
+ * 4 chips should be the maximum
+ */
+#define PNV_CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1))
+
+#define TYPE_POWERNV_MACHINE MACHINE_TYPE_NAME("powernv")
+#define POWERNV_MACHINE(obj) \
+ OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE)
+
+typedef struct PnvMachineState {
+ /*< private >*/
+ MachineState parent_obj;
+
+ uint32_t initrd_base;
+ long initrd_size;
+
+ uint32_t num_chips;
+ PnvChip **chips;
+
+ ISABus *isa_bus;
+} PnvMachineState;
+
+#define PNV_FDT_ADDR 0x01000000
+#define PNV_TIMEBASE_FREQ 512000000ULL
+
+/*
+ * POWER8 MMIO base addresses
+ */
+#define PNV_XSCOM_SIZE 0x800000000ull
+#define PNV_XSCOM_BASE(chip) \
+ (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE)
+
+#endif /* _PPC_PNV_H */
diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h
new file mode 100644
index 0000000000..2955a41c90
--- /dev/null
+++ b/include/hw/ppc/pnv_core.h
@@ -0,0 +1,50 @@
+/*
+ * QEMU PowerPC PowerNV CPU Core model
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_CORE_H
+#define _PPC_PNV_CORE_H
+
+#include "hw/cpu/core.h"
+
+#define TYPE_PNV_CORE "powernv-cpu-core"
+#define PNV_CORE(obj) \
+ OBJECT_CHECK(PnvCore, (obj), TYPE_PNV_CORE)
+#define PNV_CORE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(PnvCoreClass, (klass), TYPE_PNV_CORE)
+#define PNV_CORE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(PnvCoreClass, (obj), TYPE_PNV_CORE)
+
+typedef struct PnvCore {
+ /*< private >*/
+ CPUCore parent_obj;
+
+ /*< public >*/
+ void *threads;
+ uint32_t pir;
+
+ MemoryRegion xscom_regs;
+} PnvCore;
+
+typedef struct PnvCoreClass {
+ DeviceClass parent_class;
+ ObjectClass *cpu_oc;
+} PnvCoreClass;
+
+extern char *pnv_core_typename(const char *model);
+
+#endif /* _PPC_PNV_CORE_H */
diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h
new file mode 100644
index 0000000000..38e5506975
--- /dev/null
+++ b/include/hw/ppc/pnv_lpc.h
@@ -0,0 +1,67 @@
+/*
+ * QEMU PowerPC PowerNV LPC controller
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_LPC_H
+#define _PPC_PNV_LPC_H
+
+#define TYPE_PNV_LPC "pnv-lpc"
+#define PNV_LPC(obj) \
+ OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
+
+typedef struct PnvLpcController {
+ DeviceState parent;
+
+ uint64_t eccb_stat_reg;
+ uint32_t eccb_data_reg;
+
+ /* OPB bus */
+ MemoryRegion opb_mr;
+ AddressSpace opb_as;
+
+ /* ISA IO and Memory space */
+ MemoryRegion isa_io;
+ MemoryRegion isa_mem;
+
+ /* Windows from OPB to ISA (aliases) */
+ MemoryRegion opb_isa_io;
+ MemoryRegion opb_isa_mem;
+ MemoryRegion opb_isa_fw;
+
+ /* Registers */
+ MemoryRegion lpc_hc_regs;
+ MemoryRegion opb_master_regs;
+
+ /* OPB Master LS registers */
+ uint32_t opb_irq_stat;
+ uint32_t opb_irq_mask;
+ uint32_t opb_irq_pol;
+ uint32_t opb_irq_input;
+
+ /* LPC HC registers */
+ uint32_t lpc_hc_fw_seg_idsel;
+ uint32_t lpc_hc_fw_rd_acc_size;
+ uint32_t lpc_hc_irqser_ctrl;
+ uint32_t lpc_hc_irqmask;
+ uint32_t lpc_hc_irqstat;
+ uint32_t lpc_hc_error_addr;
+
+ /* XSCOM registers */
+ MemoryRegion xscom_regs;
+} PnvLpcController;
+
+#endif /* _PPC_PNV_LPC_H */
diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h
new file mode 100644
index 0000000000..c0a2fbb9f6
--- /dev/null
+++ b/include/hw/ppc/pnv_xscom.h
@@ -0,0 +1,78 @@
+/*
+ * QEMU PowerPC PowerNV XSCOM bus definitions
+ *
+ * Copyright (c) 2016, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _PPC_PNV_XSCOM_H
+#define _PPC_PNV_XSCOM_H
+
+#include "qom/object.h"
+
+typedef struct PnvChip PnvChip;
+
+typedef struct PnvXScomInterface {
+ Object parent;
+} PnvXScomInterface;
+
+#define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface"
+#define PNV_XSCOM_INTERFACE(obj) \
+ OBJECT_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE)
+#define PNV_XSCOM_INTERFACE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(PnvXScomInterfaceClass, (klass), \
+ TYPE_PNV_XSCOM_INTERFACE)
+#define PNV_XSCOM_INTERFACE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(PnvXScomInterfaceClass, (obj), TYPE_PNV_XSCOM_INTERFACE)
+
+typedef struct PnvXScomInterfaceClass {
+ InterfaceClass parent;
+ int (*populate)(PnvXScomInterface *dev, void *fdt, int offset);
+} PnvXScomInterfaceClass;
+
+/*
+ * Layout of the XSCOM PCB addresses of EX core 1
+ *
+ * GPIO 0x1100xxxx
+ * SCOM 0x1101xxxx
+ * OHA 0x1102xxxx
+ * CLOCK CTL 0x1103xxxx
+ * FIR 0x1104xxxx
+ * THERM 0x1105xxxx
+ * <reserved> 0x1106xxxx
+ * ..
+ * 0x110Exxxx
+ * PCB SLAVE 0x110Fxxxx
+ */
+
+#define PNV_XSCOM_EX_BASE 0x10000000
+#define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24))
+#define PNV_XSCOM_EX_CORE_SIZE 0x100000
+
+#define PNV_XSCOM_LPC_BASE 0xb0020
+#define PNV_XSCOM_LPC_SIZE 0x4
+
+extern void pnv_xscom_realize(PnvChip *chip, Error **errp);
+extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);
+
+extern void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset,
+ MemoryRegion *mr);
+extern void pnv_xscom_region_init(MemoryRegion *mr,
+ struct Object *owner,
+ const MemoryRegionOps *ops,
+ void *opaque,
+ const char *name,
+ uint64_t size);
+
+#endif /* _PPC_PNV_XSCOM_H */
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index aeaba3edf9..bd5bcf70de 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -6,12 +6,14 @@
#include "hw/ppc/xics.h"
#include "hw/ppc/spapr_drc.h"
#include "hw/mem/pc-dimm.h"
+#include "hw/ppc/spapr_ovec.h"
struct VIOsPAPRBus;
struct sPAPRPHBState;
struct sPAPRNVRAM;
typedef struct sPAPRConfigureConnectorState sPAPRConfigureConnectorState;
typedef struct sPAPREventLogEntry sPAPREventLogEntry;
+typedef struct sPAPREventSource sPAPREventSource;
#define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL
#define SPAPR_ENTRY_POINT 0x100
@@ -63,17 +65,23 @@ struct sPAPRMachineState {
uint32_t htab_shift;
hwaddr rma_size;
int vrma_adjust;
- hwaddr fdt_addr, rtas_addr;
ssize_t rtas_size;
void *rtas_blob;
- void *fdt_skel;
+ long kernel_size;
+ bool kernel_le;
+ uint32_t initrd_base;
+ long initrd_size;
uint64_t rtc_offset; /* Now used only during incoming migration */
struct PPCTimebase tb;
bool has_graphics;
+ sPAPROptionVector *ov5; /* QEMU-supported option vectors */
+ sPAPROptionVector *ov5_cas; /* negotiated (via CAS) option vectors */
+ bool cas_reboot;
- uint32_t check_exception_irq;
Notifier epow_notifier;
QTAILQ_HEAD(, sPAPREventLogEntry) pending_events;
+ bool use_hotplug_event_source;
+ sPAPREventSource *event_sources;
/* Migration state */
int htab_save_index;
@@ -527,8 +535,8 @@ void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn);
target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *sm,
uint32_t token, uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets);
-int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr,
- hwaddr rtas_size);
+void spapr_dt_rtas_tokens(void *fdt, int rtas);
+void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr);
#define SPAPR_TCE_PAGE_SHIFT 12
#define SPAPR_TCE_PAGE_SIZE (1ULL << SPAPR_TCE_PAGE_SHIFT)
@@ -578,10 +586,11 @@ struct sPAPREventLogEntry {
};
void spapr_events_init(sPAPRMachineState *sm);
-void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq);
+void spapr_dt_events(sPAPRMachineState *sm, void *fdt);
int spapr_h_cas_compose_response(sPAPRMachineState *sm,
target_ulong addr, target_ulong size,
- bool cpu_update, bool memory_update);
+ bool cpu_update,
+ sPAPROptionVector *ov5_updates);
sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn);
void spapr_tce_table_enable(sPAPRTCETable *tcet,
uint32_t page_shift, uint64_t bus_offset,
@@ -601,6 +610,10 @@ void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type,
uint32_t count);
void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type,
uint32_t count);
+void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type,
+ uint32_t count, uint32_t index);
+void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type,
+ uint32_t count, uint32_t index);
void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp);
void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset,
sPAPRMachineState *spapr);
diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h
new file mode 100644
index 0000000000..6a06da32e6
--- /dev/null
+++ b/include/hw/ppc/spapr_ovec.h
@@ -0,0 +1,67 @@
+/*
+ * QEMU SPAPR Option/Architecture Vector Definitions
+ *
+ * Each architecture option is organized/documented by the following
+ * in LoPAPR 1.1, Table 244:
+ *
+ * <vector number>: the bit-vector in which the option is located
+ * <vector byte>: the byte offset of the vector entry
+ * <vector bit>: the bit offset within the vector entry
+ *
+ * where each vector entry can be one or more bytes.
+ *
+ * Firmware expects a somewhat literal encoding of this bit-vector
+ * structure, where each entry is stored in little-endian so that the
+ * byte ordering reflects that of the documentation, but where each bit
+ * offset is from "left-to-right" in the traditional representation of
+ * a byte value where the MSB is the left-most bit. Thus, each
+ * individual byte encodes the option bits in reverse order of the
+ * documented bit.
+ *
+ * These definitions/helpers attempt to abstract away this internal
+ * representation so that we can define/set/test for individual option
+ * bits using only the documented values. This is done mainly by relying
+ * on a bitmap to approximate the documented "bit-vector" structure and
+ * handling conversations to-from the internal representation under the
+ * covers.
+ *
+ * Copyright IBM Corp. 2016
+ *
+ * Authors:
+ * Michael Roth <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef _SPAPR_OVEC_H
+#define _SPAPR_OVEC_H
+
+#include "cpu.h"
+
+typedef struct sPAPROptionVector sPAPROptionVector;
+
+#define OV_BIT(byte, bit) ((byte - 1) * BITS_PER_BYTE + bit)
+
+/* option vector 5 */
+#define OV5_DRCONF_MEMORY OV_BIT(2, 2)
+#define OV5_FORM1_AFFINITY OV_BIT(5, 0)
+#define OV5_HP_EVT OV_BIT(6, 5)
+
+/* interfaces */
+sPAPROptionVector *spapr_ovec_new(void);
+sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig);
+void spapr_ovec_intersect(sPAPROptionVector *ov,
+ sPAPROptionVector *ov1,
+ sPAPROptionVector *ov2);
+bool spapr_ovec_diff(sPAPROptionVector *ov,
+ sPAPROptionVector *ov_old,
+ sPAPROptionVector *ov_new);
+void spapr_ovec_cleanup(sPAPROptionVector *ov);
+void spapr_ovec_set(sPAPROptionVector *ov, long bitnr);
+void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr);
+bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr);
+sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector);
+int spapr_ovec_populate_dt(void *fdt, int fdt_offset,
+ sPAPROptionVector *ov, const char *name);
+
+#endif /* !defined (_SPAPR_OVEC_H) */
diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h
index 40d0e5f6a3..14f502240e 100644
--- a/include/hw/ppc/spapr_vio.h
+++ b/include/hw/ppc/spapr_vio.h
@@ -76,14 +76,12 @@ struct VIOsPAPRDevice {
struct VIOsPAPRBus {
BusState bus;
uint32_t next_reg;
- int (*init)(VIOsPAPRDevice *dev);
- int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off);
};
extern VIOsPAPRBus *spapr_vio_bus_init(void);
extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg);
-extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt);
-extern int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus);
+void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt);
+extern gchar *spapr_vio_stdout_path(VIOsPAPRBus *bus);
static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev)
{
diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h
index 66ae55ded3..3f0c31610a 100644
--- a/include/hw/ppc/xics.h
+++ b/include/hw/ppc/xics.h
@@ -117,6 +117,8 @@ struct ICPState {
uint8_t mfrr;
qemu_irq output;
bool cap_irq_xics_enabled;
+
+ XICSState *xics;
};
#define TYPE_ICS_BASE "ics-base"
@@ -185,18 +187,21 @@ int xics_spapr_alloc(XICSState *icp, int irq_hint, bool lsi, Error **errp);
int xics_spapr_alloc_block(XICSState *icp, int num, bool lsi, bool align,
Error **errp);
void xics_spapr_free(XICSState *icp, int irq, int num);
+void spapr_dt_xics(XICSState *xics, void *fdt, uint32_t phandle);
void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu);
void xics_cpu_destroy(XICSState *icp, PowerPCCPU *cpu);
+void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers,
+ const char *typename, Error **errp);
/* Internal XICS interfaces */
int xics_get_cpu_index_by_dt_id(int cpu_dt_id);
-void icp_set_cppr(XICSState *icp, int server, uint8_t cppr);
-void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr);
+void icp_set_cppr(ICPState *icp, uint8_t cppr);
+void icp_set_mfrr(ICPState *icp, uint8_t mfrr);
uint32_t icp_accept(ICPState *ss);
uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr);
-void icp_eoi(XICSState *icp, int server, uint32_t xirr);
+void icp_eoi(ICPState *icp, uint32_t xirr);
void ics_simple_write_xive(ICSState *ics, int nr, int server,
uint8_t priority, uint8_t saved_priority);
diff --git a/include/io/channel.h b/include/io/channel.h
index 752e89f4dc..32a9470794 100644
--- a/include/io/channel.h
+++ b/include/io/channel.h
@@ -40,9 +40,9 @@ typedef struct QIOChannelClass QIOChannelClass;
typedef enum QIOChannelFeature QIOChannelFeature;
enum QIOChannelFeature {
- QIO_CHANNEL_FEATURE_FD_PASS = (1 << 0),
- QIO_CHANNEL_FEATURE_SHUTDOWN = (1 << 1),
- QIO_CHANNEL_FEATURE_LISTEN = (1 << 2),
+ QIO_CHANNEL_FEATURE_FD_PASS,
+ QIO_CHANNEL_FEATURE_SHUTDOWN,
+ QIO_CHANNEL_FEATURE_LISTEN,
};
@@ -79,6 +79,7 @@ typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc,
struct QIOChannel {
Object parent;
unsigned int features; /* bitmask of QIOChannelFeatures */
+ char *name;
#ifdef _WIN32
HANDLE event; /* For use with GSource on Win32 */
#endif
@@ -149,6 +150,28 @@ bool qio_channel_has_feature(QIOChannel *ioc,
QIOChannelFeature feature);
/**
+ * qio_channel_set_feature:
+ * @ioc: the channel object
+ * @feature: the feature to set support for
+ *
+ * Add channel support for the feature named in @feature.
+ */
+void qio_channel_set_feature(QIOChannel *ioc,
+ QIOChannelFeature feature);
+
+/**
+ * qio_channel_set_name:
+ * @ioc: the channel object
+ * @name: the name of the channel
+ *
+ * Sets the name of the channel, which serves as an aid
+ * to debugging. The name is used when creating GSource
+ * watches for this channel.
+ */
+void qio_channel_set_name(QIOChannel *ioc,
+ const char *name);
+
+/**
* qio_channel_readv_full:
* @ioc: the channel object
* @iov: the array of memory regions to read data into
diff --git a/include/qemu/rfifolock.h b/include/qemu/rfifolock.h
deleted file mode 100644
index b23ab538a6..0000000000
--- a/include/qemu/rfifolock.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Recursive FIFO lock
- *
- * Copyright Red Hat, Inc. 2013
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#ifndef QEMU_RFIFOLOCK_H
-#define QEMU_RFIFOLOCK_H
-
-#include "qemu/thread.h"
-
-/* Recursive FIFO lock
- *
- * This lock provides more features than a plain mutex:
- *
- * 1. Fairness - enforces FIFO order.
- * 2. Nesting - can be taken recursively.
- * 3. Contention callback - optional, called when thread must wait.
- *
- * The recursive FIFO lock is heavyweight so prefer other synchronization
- * primitives if you do not need its features.
- */
-typedef struct {
- QemuMutex lock; /* protects all fields */
-
- /* FIFO order */
- unsigned int head; /* active ticket number */
- unsigned int tail; /* waiting ticket number */
- QemuCond cond; /* used to wait for our ticket number */
-
- /* Nesting */
- QemuThread owner_thread; /* thread that currently has ownership */
- unsigned int nesting; /* amount of nesting levels */
-
- /* Contention callback */
- void (*cb)(void *); /* called when thread must wait, with ->lock
- * held so it may not recursively lock/unlock
- */
- void *cb_opaque;
-} RFifoLock;
-
-void rfifolock_init(RFifoLock *r, void (*cb)(void *), void *opaque);
-void rfifolock_destroy(RFifoLock *r);
-void rfifolock_lock(RFifoLock *r);
-void rfifolock_unlock(RFifoLock *r);
-
-#endif /* QEMU_RFIFOLOCK_H */
diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h
index aa03567e5e..09d1e15728 100644
--- a/include/qemu/thread-posix.h
+++ b/include/qemu/thread-posix.h
@@ -4,6 +4,12 @@
#include <pthread.h>
#include <semaphore.h>
+typedef QemuMutex QemuRecMutex;
+#define qemu_rec_mutex_destroy qemu_mutex_destroy
+#define qemu_rec_mutex_lock qemu_mutex_lock
+#define qemu_rec_mutex_try_lock qemu_mutex_try_lock
+#define qemu_rec_mutex_unlock qemu_mutex_unlock
+
struct QemuMutex {
pthread_mutex_t lock;
};
diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h
index c7ce8dcd45..5fb6541ae9 100644
--- a/include/qemu/thread-win32.h
+++ b/include/qemu/thread-win32.h
@@ -8,6 +8,16 @@ struct QemuMutex {
LONG owner;
};
+typedef struct QemuRecMutex QemuRecMutex;
+struct QemuRecMutex {
+ CRITICAL_SECTION lock;
+};
+
+void qemu_rec_mutex_destroy(QemuRecMutex *mutex);
+void qemu_rec_mutex_lock(QemuRecMutex *mutex);
+int qemu_rec_mutex_trylock(QemuRecMutex *mutex);
+void qemu_rec_mutex_unlock(QemuRecMutex *mutex);
+
struct QemuCond {
LONG waiters, target;
HANDLE sema;
diff --git a/include/qemu/thread.h b/include/qemu/thread.h
index 31237e93ee..e8e665f020 100644
--- a/include/qemu/thread.h
+++ b/include/qemu/thread.h
@@ -25,6 +25,9 @@ void qemu_mutex_lock(QemuMutex *mutex);
int qemu_mutex_trylock(QemuMutex *mutex);
void qemu_mutex_unlock(QemuMutex *mutex);
+/* Prototypes for other functions are in thread-posix.h/thread-win32.h. */
+void qemu_rec_mutex_init(QemuRecMutex *mutex);
+
void qemu_cond_init(QemuCond *cond);
void qemu_cond_destroy(QemuCond *cond);
diff --git a/io/channel-socket.c b/io/channel-socket.c
index 196a4f18f7..d7e03f6266 100644
--- a/io/channel-socket.c
+++ b/io/channel-socket.c
@@ -55,7 +55,7 @@ qio_channel_socket_new(void)
sioc->fd = -1;
ioc = QIO_CHANNEL(sioc);
- ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN);
+ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
#ifdef WIN32
ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -72,9 +72,6 @@ qio_channel_socket_set_fd(QIOChannelSocket *sioc,
int fd,
Error **errp)
{
- int val;
- socklen_t len = sizeof(val);
-
if (sioc->fd != -1) {
error_setg(errp, "Socket is already open");
return -1;
@@ -107,13 +104,9 @@ qio_channel_socket_set_fd(QIOChannelSocket *sioc,
#ifndef WIN32
if (sioc->localAddr.ss_family == AF_UNIX) {
QIOChannel *ioc = QIO_CHANNEL(sioc);
- ioc->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS);
+ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS);
}
#endif /* WIN32 */
- if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &val, &len) == 0 && val) {
- QIOChannel *ioc = QIO_CHANNEL(sioc);
- ioc->features |= (1 << QIO_CHANNEL_FEATURE_LISTEN);
- }
return 0;
@@ -220,6 +213,7 @@ int qio_channel_socket_listen_sync(QIOChannelSocket *ioc,
close(fd);
return -1;
}
+ qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_LISTEN);
return 0;
}
@@ -380,7 +374,8 @@ qio_channel_socket_accept(QIOChannelSocket *ioc,
#ifndef WIN32
if (cioc->localAddr.ss_family == AF_UNIX) {
- QIO_CHANNEL(cioc)->features |= (1 << QIO_CHANNEL_FEATURE_FD_PASS);
+ QIOChannel *ioc_local = QIO_CHANNEL(cioc);
+ qio_channel_set_feature(ioc_local, QIO_CHANNEL_FEATURE_FD_PASS);
}
#endif /* WIN32 */
@@ -403,7 +398,8 @@ static void qio_channel_socket_finalize(Object *obj)
QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj);
if (ioc->fd != -1) {
- if (QIO_CHANNEL(ioc)->features & QIO_CHANNEL_FEATURE_LISTEN) {
+ QIOChannel *ioc_local = QIO_CHANNEL(ioc);
+ if (qio_channel_has_feature(ioc_local, QIO_CHANNEL_FEATURE_LISTEN)) {
Error *err = NULL;
socket_listen_cleanup(ioc->fd, &err);
diff --git a/io/channel-tls.c b/io/channel-tls.c
index 9a8525c816..d24dc8c613 100644
--- a/io/channel-tls.c
+++ b/io/channel-tls.c
@@ -111,8 +111,8 @@ qio_channel_tls_new_client(QIOChannel *master,
ioc = QIO_CHANNEL(tioc);
tioc->master = master;
- if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) {
- ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN);
+ if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) {
+ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
}
object_ref(OBJECT(master));
diff --git a/io/channel-websock.c b/io/channel-websock.c
index 533bd4b3b5..f45bced82a 100644
--- a/io/channel-websock.c
+++ b/io/channel-websock.c
@@ -497,8 +497,8 @@ qio_channel_websock_new_server(QIOChannel *master)
ioc = QIO_CHANNEL(wioc);
wioc->master = master;
- if (master->features & (1 << QIO_CHANNEL_FEATURE_SHUTDOWN)) {
- ioc->features |= (1 << QIO_CHANNEL_FEATURE_SHUTDOWN);
+ if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) {
+ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
}
object_ref(OBJECT(master));
diff --git a/io/channel.c b/io/channel.c
index 923c4651ca..80924c1772 100644
--- a/io/channel.c
+++ b/io/channel.c
@@ -30,6 +30,21 @@ bool qio_channel_has_feature(QIOChannel *ioc,
}
+void qio_channel_set_feature(QIOChannel *ioc,
+ QIOChannelFeature feature)
+{
+ ioc->features |= (1 << feature);
+}
+
+
+void qio_channel_set_name(QIOChannel *ioc,
+ const char *name)
+{
+ g_free(ioc->name);
+ ioc->name = g_strdup(name);
+}
+
+
ssize_t qio_channel_readv_full(QIOChannel *ioc,
const struct iovec *iov,
size_t niov,
@@ -40,7 +55,7 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc,
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
if ((fds || nfds) &&
- !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) {
+ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
error_setg_errno(errp, EINVAL,
"Channel does not support file descriptor passing");
return -1;
@@ -60,7 +75,7 @@ ssize_t qio_channel_writev_full(QIOChannel *ioc,
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
if ((fds || nfds) &&
- !(ioc->features & (1 << QIO_CHANNEL_FEATURE_FD_PASS))) {
+ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) {
error_setg_errno(errp, EINVAL,
"Channel does not support file descriptor passing");
return -1;
@@ -129,7 +144,13 @@ GSource *qio_channel_create_watch(QIOChannel *ioc,
GIOCondition condition)
{
QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
- return klass->io_create_watch(ioc, condition);
+ GSource *ret = klass->io_create_watch(ioc, condition);
+
+ if (ioc->name) {
+ g_source_set_name(ret, ioc->name);
+ }
+
+ return ret;
}
@@ -275,24 +296,24 @@ void qio_channel_wait(QIOChannel *ioc,
}
-#ifdef _WIN32
static void qio_channel_finalize(Object *obj)
{
QIOChannel *ioc = QIO_CHANNEL(obj);
+ g_free(ioc->name);
+
+#ifdef _WIN32
if (ioc->event) {
CloseHandle(ioc->event);
}
-}
#endif
+}
static const TypeInfo qio_channel_info = {
.parent = TYPE_OBJECT,
.name = TYPE_QIO_CHANNEL,
.instance_size = sizeof(QIOChannel),
-#ifdef _WIN32
.instance_finalize = qio_channel_finalize,
-#endif
.abstract = true,
.class_size = sizeof(QIOChannelClass),
};
diff --git a/iothread.c b/iothread.c
index fbeb8deb38..bd70344811 100644
--- a/iothread.c
+++ b/iothread.c
@@ -16,10 +16,12 @@
#include "qom/object_interfaces.h"
#include "qemu/module.h"
#include "block/aio.h"
+#include "block/block.h"
#include "sysemu/iothread.h"
#include "qmp-commands.h"
#include "qemu/error-report.h"
#include "qemu/rcu.h"
+#include "qemu/main-loop.h"
typedef ObjectClass IOThreadClass;
@@ -28,26 +30,27 @@ typedef ObjectClass IOThreadClass;
#define IOTHREAD_CLASS(klass) \
OBJECT_CLASS_CHECK(IOThreadClass, klass, TYPE_IOTHREAD)
+static __thread IOThread *my_iothread;
+
+AioContext *qemu_get_current_aio_context(void)
+{
+ return my_iothread ? my_iothread->ctx : qemu_get_aio_context();
+}
+
static void *iothread_run(void *opaque)
{
IOThread *iothread = opaque;
- bool blocking;
rcu_register_thread();
+ my_iothread = iothread;
qemu_mutex_lock(&iothread->init_done_lock);
iothread->thread_id = qemu_get_thread_id();
qemu_cond_signal(&iothread->init_done_cond);
qemu_mutex_unlock(&iothread->init_done_lock);
- while (!iothread->stopping) {
- aio_context_acquire(iothread->ctx);
- blocking = true;
- while (!iothread->stopping && aio_poll(iothread->ctx, blocking)) {
- /* Progress was made, keep going */
- blocking = false;
- }
- aio_context_release(iothread->ctx);
+ while (!atomic_read(&iothread->stopping)) {
+ aio_poll(iothread->ctx, true);
}
rcu_unregister_thread();
@@ -190,6 +193,18 @@ IOThreadInfoList *qmp_query_iothreads(Error **errp)
void iothread_stop_all(void)
{
Object *container = object_get_objects_root();
+ BlockDriverState *bs;
+ BdrvNextIterator it;
+
+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
+ AioContext *ctx = bdrv_get_aio_context(bs);
+ if (ctx == qemu_get_aio_context()) {
+ continue;
+ }
+ aio_context_acquire(ctx);
+ bdrv_set_aio_context(bs, qemu_get_aio_context());
+ aio_context_release(ctx);
+ }
object_child_foreach(container, iothread_stop, NULL);
}
diff --git a/main-loop.c b/main-loop.c
index 6a7f8d30bd..66c4eb69a3 100644
--- a/main-loop.c
+++ b/main-loop.c
@@ -161,9 +161,11 @@ int qemu_init_main_loop(Error **errp)
qemu_notify_bh = qemu_bh_new(notify_event_cb, NULL);
gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
src = aio_get_g_source(qemu_aio_context);
+ g_source_set_name(src, "aio-context");
g_source_attach(src, NULL);
g_source_unref(src);
src = iohandler_get_g_source();
+ g_source_set_name(src, "io-handler");
g_source_attach(src, NULL);
g_source_unref(src);
return 0;
diff --git a/migration/exec.c b/migration/exec.c
index 2af63cced6..9157721dfe 100644
--- a/migration/exec.c
+++ b/migration/exec.c
@@ -38,6 +38,7 @@ void exec_start_outgoing_migration(MigrationState *s, const char *command, Error
return;
}
+ qio_channel_set_name(ioc, "migration-exec-outgoing");
migration_channel_connect(s, ioc, NULL);
object_unref(OBJECT(ioc));
}
@@ -64,6 +65,7 @@ void exec_start_incoming_migration(const char *command, Error **errp)
return;
}
+ qio_channel_set_name(ioc, "migration-exec-incoming");
qio_channel_add_watch(ioc,
G_IO_IN,
exec_accept_incoming_migration,
diff --git a/migration/fd.c b/migration/fd.c
index 84a10fd68f..58cb51a9e6 100644
--- a/migration/fd.c
+++ b/migration/fd.c
@@ -38,6 +38,7 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **
return;
}
+ qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-outgoing");
migration_channel_connect(s, ioc, NULL);
object_unref(OBJECT(ioc));
}
@@ -65,6 +66,7 @@ void fd_start_incoming_migration(const char *infd, Error **errp)
return;
}
+ qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-incoming");
qio_channel_add_watch(ioc,
G_IO_IN,
fd_accept_incoming_migration,
diff --git a/migration/migration.c b/migration/migration.c
index 4d417b76cf..156e70791a 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1567,6 +1567,7 @@ static int postcopy_start(MigrationState *ms, bool *old_vm_running)
* to do this we use a qemu_buf to hold the whole of the device state.
*/
bioc = qio_channel_buffer_new(4096);
+ qio_channel_set_name(QIO_CHANNEL(bioc), "migration-postcopy-buffer");
fb = qemu_fopen_channel_output(QIO_CHANNEL(bioc));
object_unref(OBJECT(bioc));
diff --git a/migration/savevm.c b/migration/savevm.c
index cfcbbd00d3..0363372acc 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1631,6 +1631,7 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis)
}
bioc = qio_channel_buffer_new(length);
+ qio_channel_set_name(QIO_CHANNEL(bioc), "migration-loadvm-buffer");
ret = qemu_get_buffer(mis->from_src_file,
bioc->data,
length);
@@ -2122,6 +2123,7 @@ void qmp_xen_save_devices_state(const char *filename, Error **errp)
if (!ioc) {
goto the_end;
}
+ qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-save-state");
f = qemu_fopen_channel_output(QIO_CHANNEL(ioc));
ret = qemu_save_device_state(f);
qemu_fclose(f);
@@ -2154,6 +2156,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp)
if (!ioc) {
return;
}
+ qio_channel_set_name(QIO_CHANNEL(ioc), "migration-xen-load-state");
f = qemu_fopen_channel_input(QIO_CHANNEL(ioc));
migration_incoming_state_new(f);
diff --git a/migration/socket.c b/migration/socket.c
index a21c0c5c35..11f80b119b 100644
--- a/migration/socket.c
+++ b/migration/socket.c
@@ -100,6 +100,7 @@ static void socket_start_outgoing_migration(MigrationState *s,
data->hostname = g_strdup(saddr->u.inet.data->host);
}
+ qio_channel_set_name(QIO_CHANNEL(sioc), "migration-socket-outgoing");
qio_channel_socket_connect_async(sioc,
saddr,
socket_outgoing_migration,
@@ -146,6 +147,7 @@ static gboolean socket_accept_incoming_migration(QIOChannel *ioc,
trace_migration_socket_incoming_accepted();
+ qio_channel_set_name(QIO_CHANNEL(sioc), "migration-socket-incoming");
migration_channel_process_incoming(migrate_get_current(),
QIO_CHANNEL(sioc));
object_unref(OBJECT(sioc));
@@ -162,6 +164,9 @@ static void socket_start_incoming_migration(SocketAddress *saddr,
{
QIOChannelSocket *listen_ioc = qio_channel_socket_new();
+ qio_channel_set_name(QIO_CHANNEL(listen_ioc),
+ "migration-socket-listener");
+
if (qio_channel_socket_listen_sync(listen_ioc, saddr, errp) < 0) {
object_unref(OBJECT(listen_ioc));
qapi_free_SocketAddress(saddr);
diff --git a/migration/tls.c b/migration/tls.c
index 12c053d15f..49ca9a8930 100644
--- a/migration/tls.c
+++ b/migration/tls.c
@@ -99,6 +99,7 @@ void migration_tls_channel_process_incoming(MigrationState *s,
}
trace_migration_tls_incoming_handshake_start();
+ qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-incoming");
qio_channel_tls_handshake(tioc,
migration_tls_incoming_handshake,
NULL,
@@ -154,6 +155,7 @@ void migration_tls_channel_connect(MigrationState *s,
}
trace_migration_tls_outgoing_handshake_start(hostname);
+ qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-outgoing");
qio_channel_tls_handshake(tioc,
migration_tls_outgoing_handshake,
s,
diff --git a/nbd/client.c b/nbd/client.c
index a92f1e2275..f6db8369b3 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -387,6 +387,7 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
if (!tioc) {
return NULL;
}
+ qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls");
data.loop = g_main_loop_new(g_main_context_default(), FALSE);
TRACE("Starting TLS handshake");
qio_channel_tls_handshake(tioc,
diff --git a/nbd/server.c b/nbd/server.c
index 472f584c32..36bcafcd50 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -349,6 +349,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
return NULL;
}
+ qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-server-tls");
TRACE("Starting TLS handshake");
data.loop = g_main_loop_new(g_main_context_default(), FALSE);
qio_channel_tls_handshake(tioc,
diff --git a/net/colo-compare.c b/net/colo-compare.c
index f791383dbc..4ac916adcb 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -218,16 +218,17 @@ static int colo_packet_compare_tcp(Packet *spkt, Packet *ppkt)
(spkt->size - ETH_HLEN));
if (res != 0 && trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE)) {
- trace_colo_compare_pkt_info(inet_ntoa(ppkt->ip->ip_src),
- inet_ntoa(ppkt->ip->ip_dst),
- ntohl(ptcp->th_seq),
- ntohl(ptcp->th_ack),
- ntohl(stcp->th_seq),
- ntohl(stcp->th_ack),
- res, ptcp->th_flags,
- stcp->th_flags,
- ppkt->size,
- spkt->size);
+ trace_colo_compare_pkt_info_src(inet_ntoa(ppkt->ip->ip_src),
+ ntohl(stcp->th_seq),
+ ntohl(stcp->th_ack),
+ res, stcp->th_flags,
+ spkt->size);
+
+ trace_colo_compare_pkt_info_dst(inet_ntoa(ppkt->ip->ip_dst),
+ ntohl(ptcp->th_seq),
+ ntohl(ptcp->th_ack),
+ res, ptcp->th_flags,
+ ppkt->size);
qemu_hexdump((char *)ppkt->data, stderr,
"colo-compare ppkt", ppkt->size);
diff --git a/net/trace-events b/net/trace-events
index b1913a6666..35198bc742 100644
--- a/net/trace-events
+++ b/net/trace-events
@@ -13,7 +13,8 @@ colo_compare_icmp_miscompare(const char *sta, int size) ": %s = %d"
colo_compare_ip_info(int psize, const char *sta, const char *stb, int ssize, const char *stc, const char *std) "ppkt size = %d, ip_src = %s, ip_dst = %s, spkt size = %d, ip_src = %s, ip_dst = %s"
colo_old_packet_check_found(int64_t old_time) "%" PRId64
colo_compare_miscompare(void) ""
-colo_compare_pkt_info(const char *src, const char *dst, uint32_t pseq, uint32_t pack, uint32_t sseq, uint32_t sack, int res, uint32_t pflag, uint32_t sflag, int psize, int ssize) "src/dst: %s/%s p: seq/ack=%u/%u s: seq/ack=%u/%u res=%d flags=%x/%x ppkt_size: %d spkt_size: %d\n"
+colo_compare_pkt_info_src(const char *src, uint32_t sseq, uint32_t sack, int res, uint32_t sflag, int ssize) "src/dst: %s s: seq/ack=%u/%u res=%d flags=%x spkt_size: %d\n"
+colo_compare_pkt_info_dst(const char *dst, uint32_t dseq, uint32_t dack, int res, uint32_t dflag, int dsize) "src/dst: %s d: seq/ack=%u/%u res=%d flags=%x dpkt_size: %d\n"
# net/filter-rewriter.c
colo_filter_rewriter_debug(void) ""
diff --git a/pc-bios/README b/pc-bios/README
index 5a8a93193e..47a913f9c7 100644
--- a/pc-bios/README
+++ b/pc-bios/README
@@ -17,7 +17,7 @@
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
https://github.com/aik/SLOF, and the image currently in qemu is
- built from git tag qemu-slof-20160223.
+ built from git tag qemu-slof-20161019.
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
legacy x86 software to communicate with an attached serial console as
@@ -42,3 +42,8 @@
it was compiled using the qemu-ppce500 target.
A git mirror is available at: git://git.qemu-project.org/u-boot.git
The hash used to compile the current version is: 2072e72
+
+- Skiboot (https://github.com/open-power/skiboot/) is an OPAL
+ (OpenPower Abstraction Layer) firmware for OpenPOWER systems. It can
+ run an hypervisor OS or simply a host OS on the "baremetal"
+ platform, also known as the PowerNV (Non-Virtualized) platform.
diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid
new file mode 100644
index 0000000000..0e59a8280d
--- /dev/null
+++ b/pc-bios/skiboot.lid
Binary files differ
diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin
index f2384939ef..30ce7ac384 100644
--- a/pc-bios/slof.bin
+++ b/pc-bios/slof.bin
Binary files differ
diff --git a/qemu-char.c b/qemu-char.c
index 6dd779fc3c..1e5a0e8cb9 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -1075,7 +1075,8 @@ static GSourceFuncs io_watch_poll_funcs = {
};
/* Can only be used for read */
-static guint io_add_watch_poll(QIOChannel *ioc,
+static guint io_add_watch_poll(CharDriverState *chr,
+ QIOChannel *ioc,
IOCanReadHandler *fd_can_read,
QIOChannelFunc fd_read,
gpointer user_data,
@@ -1083,6 +1084,7 @@ static guint io_add_watch_poll(QIOChannel *ioc,
{
IOWatchPoll *iwp;
int tag;
+ char *name;
iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs,
sizeof(IOWatchPoll));
@@ -1093,6 +1095,10 @@ static guint io_add_watch_poll(QIOChannel *ioc,
iwp->src = NULL;
iwp->context = context;
+ name = g_strdup_printf("chardev-iowatch-%s", chr->label);
+ g_source_set_name((GSource *)iwp, name);
+ g_free(name);
+
tag = g_source_attach(&iwp->parent, context);
g_source_unref(&iwp->parent);
return tag;
@@ -1232,7 +1238,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr,
remove_fd_in_watch(chr);
if (s->ioc_in) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc_in,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc_in,
fd_chr_read_poll,
fd_chr_read, chr,
context);
@@ -1261,6 +1267,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
{
CharDriverState *chr;
FDCharDriver *s;
+ char *name;
chr = qemu_chr_alloc(backend, errp);
if (!chr) {
@@ -1268,7 +1275,13 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
}
s = g_new0(FDCharDriver, 1);
s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in));
+ name = g_strdup_printf("chardev-file-in-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name);
+ g_free(name);
s->ioc_out = QIO_CHANNEL(qio_channel_file_new_fd(fd_out));
+ name = g_strdup_printf("chardev-file-out-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(s->ioc_out), name);
+ g_free(name);
qemu_set_nonblock(fd_out);
s->chr = chr;
chr->opaque = s;
@@ -1448,6 +1461,7 @@ static gboolean pty_chr_timer(gpointer opaque)
static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
{
PtyCharDriver *s = chr->opaque;
+ char *name;
if (s->timer_tag) {
g_source_remove(s->timer_tag);
@@ -1455,10 +1469,14 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
}
if (ms == 1000) {
+ name = g_strdup_printf("pty-timer-secs-%s", chr->label);
s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
} else {
+ name = g_strdup_printf("pty-timer-ms-%s", chr->label);
s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
}
+ g_source_set_name_by_id(s->timer_tag, name);
+ g_free(name);
}
/* Called with chr_write_lock held. */
@@ -1587,7 +1605,7 @@ static void pty_chr_state(CharDriverState *chr, int connected)
s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr);
}
if (!chr->fd_in_tag) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
pty_chr_read_poll,
pty_chr_read,
chr, NULL);
@@ -1622,6 +1640,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
int master_fd, slave_fd;
char pty_name[PATH_MAX];
ChardevCommon *common = backend->u.pty.data;
+ char *name;
master_fd = qemu_openpty_raw(&slave_fd, pty_name);
if (master_fd < 0) {
@@ -1654,6 +1673,9 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
*be_opened = false;
s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
+ name = g_strdup_printf("chardev-pty-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(s->ioc), name);
+ g_free(name);
s->timer_tag = 0;
return chr;
@@ -2744,7 +2766,7 @@ static void udp_chr_update_read_handler(CharDriverState *chr,
remove_fd_in_watch(chr);
if (s->ioc) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
udp_chr_read_poll,
udp_chr_read, chr,
context);
@@ -2822,9 +2844,13 @@ static gboolean socket_reconnect_timeout(gpointer opaque);
static void qemu_chr_socket_restart_timer(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
+ char *name;
assert(s->connected == 0);
s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
socket_reconnect_timeout, chr);
+ name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label);
+ g_source_set_name_by_id(s->reconnect_timer, name);
+ g_free(name);
}
static void check_report_connect_error(CharDriverState *chr,
@@ -3149,7 +3175,7 @@ static void tcp_chr_connect(void *opaque)
s->connected = 1;
if (s->ioc) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
tcp_chr_read_poll,
tcp_chr_read,
chr, NULL);
@@ -3168,7 +3194,7 @@ static void tcp_chr_update_read_handler(CharDriverState *chr,
remove_fd_in_watch(chr);
if (s->ioc) {
- chr->fd_in_tag = io_add_watch_poll(s->ioc,
+ chr->fd_in_tag = io_add_watch_poll(chr, s->ioc,
tcp_chr_read_poll,
tcp_chr_read, chr,
context);
@@ -3266,6 +3292,7 @@ static void tcp_chr_tls_init(CharDriverState *chr)
TCPCharDriver *s = chr->opaque;
QIOChannelTLS *tioc;
Error *err = NULL;
+ gchar *name;
if (s->is_listen) {
tioc = qio_channel_tls_new_server(
@@ -3283,6 +3310,11 @@ static void tcp_chr_tls_init(CharDriverState *chr)
tcp_chr_disconnect(chr);
return;
}
+ name = g_strdup_printf("chardev-tls-%s-%s",
+ s->is_listen ? "server" : "client",
+ chr->label);
+ qio_channel_set_name(QIO_CHANNEL(tioc), name);
+ g_free(name);
object_unref(OBJECT(s->ioc));
s->ioc = QIO_CHANNEL(tioc);
@@ -3293,6 +3325,19 @@ static void tcp_chr_tls_init(CharDriverState *chr)
}
+static void tcp_chr_set_client_ioc_name(CharDriverState *chr,
+ QIOChannelSocket *sioc)
+{
+ TCPCharDriver *s = chr->opaque;
+ char *name;
+ name = g_strdup_printf("chardev-tcp-%s-%s",
+ s->is_listen ? "server" : "client",
+ chr->label);
+ qio_channel_set_name(QIO_CHANNEL(sioc), name);
+ g_free(name);
+
+}
+
static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc)
{
TCPCharDriver *s = chr->opaque;
@@ -3338,6 +3383,7 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd)
if (!sioc) {
return -1;
}
+ tcp_chr_set_client_ioc_name(chr, sioc);
ret = tcp_chr_new_client(chr, sioc);
object_unref(OBJECT(sioc));
return ret;
@@ -3379,6 +3425,7 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp)
qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL);
} else {
sioc = qio_channel_socket_new();
+ tcp_chr_set_client_ioc_name(chr, sioc);
if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) {
object_unref(OBJECT(sioc));
return -1;
@@ -4586,6 +4633,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
}
sioc = qio_channel_socket_new();
+ tcp_chr_set_client_ioc_name(chr, sioc);
qio_channel_socket_connect_async(sioc, s->addr,
qemu_chr_socket_connected,
chr, NULL);
@@ -4688,12 +4736,19 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
if (s->reconnect_time) {
sioc = qio_channel_socket_new();
+ tcp_chr_set_client_ioc_name(chr, sioc);
qio_channel_socket_connect_async(sioc, s->addr,
qemu_chr_socket_connected,
chr, NULL);
} else {
if (s->is_listen) {
+ char *name;
sioc = qio_channel_socket_new();
+
+ name = g_strdup_printf("chardev-tcp-listener-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(sioc), name);
+ g_free(name);
+
if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
goto error;
}
@@ -4735,6 +4790,8 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
ChardevUdp *udp = backend->u.udp.data;
ChardevCommon *common = qapi_ChardevUdp_base(udp);
QIOChannelSocket *sioc = qio_channel_socket_new();
+ char *name;
+ CharDriverState *chr;
if (qio_channel_socket_dgram_sync(sioc,
udp->local, udp->remote,
@@ -4742,7 +4799,13 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
object_unref(OBJECT(sioc));
return NULL;
}
- return qemu_chr_open_udp(sioc, common, be_opened, errp);
+ chr = qemu_chr_open_udp(sioc, common, be_opened, errp);
+
+ name = g_strdup_printf("chardev-udp-%s", chr->label);
+ qio_channel_set_name(QIO_CHANNEL(sioc), name);
+ g_free(name);
+
+ return chr;
}
diff --git a/qemu-img.c b/qemu-img.c
index afcd51ff18..ac7f40d91a 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -795,6 +795,7 @@ static void run_block_job(BlockJob *job, Error **errp)
{
AioContext *aio_context = blk_get_aio_context(job->blk);
+ aio_context_acquire(aio_context);
do {
aio_poll(aio_context, true);
qemu_progress_print(job->len ?
@@ -802,6 +803,7 @@ static void run_block_job(BlockJob *job, Error **errp)
} while (!job->ready);
block_job_complete_sync(job, errp);
+ aio_context_release(aio_context);
/* A block job may finish instantaneously without publishing any progress,
* so just signal completion here */
@@ -819,6 +821,7 @@ static int img_commit(int argc, char **argv)
Error *local_err = NULL;
CommonBlockJobCBInfo cbi;
bool image_opts = false;
+ AioContext *aio_context;
fmt = NULL;
cache = BDRV_DEFAULT_CACHE;
@@ -928,8 +931,11 @@ static int img_commit(int argc, char **argv)
.bs = bs,
};
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
commit_active_start("commit", bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT,
common_block_job_cb, &cbi, &local_err, false);
+ aio_context_release(aio_context);
if (local_err) {
goto done;
}
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 3a3838a079..4750e9ab93 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -1956,7 +1956,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
qemu_opts_reset(&reopen_opts);
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
- bdrv_reopen_multiple(brq, &local_err);
+ bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
if (local_err) {
error_report_err(local_err);
} else {
@@ -2216,6 +2216,7 @@ static const cmdinfo_t help_cmd = {
bool qemuio_command(BlockBackend *blk, const char *cmd)
{
+ AioContext *ctx;
char *input;
const cmdinfo_t *ct;
char **v;
@@ -2227,7 +2228,10 @@ bool qemuio_command(BlockBackend *blk, const char *cmd)
if (c) {
ct = find_command(v[0]);
if (ct) {
+ ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context();
+ aio_context_acquire(ctx);
done = command(blk, ct, c, v);
+ aio_context_release(ctx);
} else {
fprintf(stderr, "command \"%s\" not found\n", v[0]);
}
diff --git a/roms/Makefile b/roms/Makefile
index 88b3709d4d..b5e5a69e91 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -63,6 +63,7 @@ default:
@echo " efirom -- update nic roms (bios+efi, this needs"
@echo " the EfiRom utility from edk2 / tianocore)"
@echo " slof -- update slof.bin"
+ @echo " skiboot -- update skiboot.lid"
@echo " u-boot.e500 -- update u-boot.e500"
bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k
@@ -103,7 +104,7 @@ build-lgplvgabios:
$(MAKE) -C vgabios $(vgabios_targets)
-.PHONY: sgabios
+.PHONY: sgabios skiboot
sgabios:
$(MAKE) -C sgabios
cp sgabios/sgabios.bin ../pc-bios
@@ -146,6 +147,10 @@ u-boot.e500:
$(powerpc_cross_prefix)strip u-boot/build.e500/u-boot -o \
../pc-bios/u-boot.e500
+skiboot:
+ $(MAKE) -C skiboot CROSS=$(powerpc64_cross_prefix)
+ cp skiboot/skiboot.lid ../pc-bios/skiboot.lid
+
clean:
rm -rf seabios/.config seabios/out seabios/builds
$(MAKE) -C vgabios clean
@@ -155,3 +160,4 @@ clean:
$(MAKE) -C ipxe/src veryclean
$(MAKE) -C SLOF clean
rm -rf u-boot/build.e500
+ $(MAKE) -C skiboot clean
diff --git a/roms/SLOF b/roms/SLOF
-Subproject e3d05727a074619fc12d0a67f05cf2c42c875cc
+Subproject efd65f49929d7db775b26066d538c8120ae3db9
diff --git a/roms/skiboot b/roms/skiboot
new file mode 160000
+Subproject 762d0082f18e4fb921a2d44a1051b02d8b0f638
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index c5850e858e..84b9d9e160 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -17,6 +17,7 @@ stub-obj-y += gdbstub.o
stub-obj-y += get-fd.o
stub-obj-y += get-next-serial.o
stub-obj-y += get-vm-name.o
+stub-obj-y += iothread.o
stub-obj-y += iothread-lock.o
stub-obj-y += is-daemonized.o
stub-obj-y += machine-init-done.o
diff --git a/stubs/iothread.c b/stubs/iothread.c
new file mode 100644
index 0000000000..8cc9e28c55
--- /dev/null
+++ b/stubs/iothread.c
@@ -0,0 +1,8 @@
+#include "qemu/osdep.h"
+#include "block/aio.h"
+#include "qemu/main-loop.h"
+
+AioContext *qemu_get_current_aio_context(void)
+{
+ return qemu_get_aio_context();
+}
diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h
index 48c5b811f3..6dfb54eb70 100644
--- a/target-m68k/cpu.h
+++ b/target-m68k/cpu.h
@@ -179,11 +179,11 @@ typedef enum {
CC_OP_FLAGS,
/* X in cc_x, C = X, N in cc_n, Z in cc_n, V via cc_n/cc_v. */
- CC_OP_ADD,
- CC_OP_SUB,
+ CC_OP_ADDB, CC_OP_ADDW, CC_OP_ADDL,
+ CC_OP_SUBB, CC_OP_SUBW, CC_OP_SUBL,
/* X in cc_x, {N,Z,C,V} via cc_n/cc_v. */
- CC_OP_CMP,
+ CC_OP_CMPB, CC_OP_CMPW, CC_OP_CMPL,
/* X in cc_x, C = 0, V = 0, N in cc_n, Z in cc_n. */
CC_OP_LOGIC,
diff --git a/target-m68k/helper.c b/target-m68k/helper.c
index 094a7e59a9..7aed9ffd2f 100644
--- a/target-m68k/helper.c
+++ b/target-m68k/helper.c
@@ -277,46 +277,6 @@ uint32_t HELPER(sats)(uint32_t val, uint32_t v)
return val;
}
-uint32_t HELPER(subx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2)
-{
- uint32_t res, new_x;
-
- if (env->cc_x) {
- new_x = (op1 <= op2);
- res = op1 - (op2 + 1);
- } else {
- new_x = (op1 < op2);
- res = op1 - op2;
- }
- env->cc_x = new_x;
- env->cc_c = new_x;
- env->cc_n = res;
- env->cc_z |= res; /* !Z is sticky */
- env->cc_v = (res ^ op1) & (op1 ^ op2);
-
- return res;
-}
-
-uint32_t HELPER(addx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2)
-{
- uint32_t res, new_x;
-
- if (env->cc_x) {
- res = op1 + op2 + 1;
- new_x = (res <= op2);
- } else {
- res = op1 + op2;
- new_x = (res < op2);
- }
- env->cc_x = new_x;
- env->cc_c = new_x;
- env->cc_n = res;
- env->cc_z |= res; /* !Z is sticky. */
- env->cc_v = (res ^ op1) & ~(op1 ^ op2);
-
- return res;
-}
-
void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
{
env->sr = val & 0xffe0;
@@ -624,32 +584,41 @@ void HELPER(mac_set_flags)(CPUM68KState *env, uint32_t acc)
}
}
+#define EXTSIGN(val, index) ( \
+ (index == 0) ? (int8_t)(val) : ((index == 1) ? (int16_t)(val) : (val)) \
+)
#define COMPUTE_CCR(op, x, n, z, v, c) { \
switch (op) { \
case CC_OP_FLAGS: \
/* Everything in place. */ \
break; \
- case CC_OP_ADD: \
+ case CC_OP_ADDB: \
+ case CC_OP_ADDW: \
+ case CC_OP_ADDL: \
res = n; \
src2 = v; \
- src1 = res - src2; \
+ src1 = EXTSIGN(res - src2, op - CC_OP_ADDB); \
c = x; \
z = n; \
v = (res ^ src1) & ~(src1 ^ src2); \
break; \
- case CC_OP_SUB: \
+ case CC_OP_SUBB: \
+ case CC_OP_SUBW: \
+ case CC_OP_SUBL: \
res = n; \
src2 = v; \
- src1 = res + src2; \
+ src1 = EXTSIGN(res + src2, op - CC_OP_SUBB); \
c = x; \
z = n; \
v = (res ^ src1) & (src1 ^ src2); \
break; \
- case CC_OP_CMP: \
+ case CC_OP_CMPB: \
+ case CC_OP_CMPW: \
+ case CC_OP_CMPL: \
src1 = n; \
src2 = v; \
- res = src1 - src2; \
+ res = EXTSIGN(src1 - src2, op - CC_OP_CMPB); \
n = res; \
z = res; \
c = src1 < src2; \
@@ -670,16 +639,16 @@ uint32_t cpu_m68k_get_ccr(CPUM68KState *env)
uint32_t res, src1, src2;
x = env->cc_x;
- c = env->cc_c;
n = env->cc_n;
z = env->cc_z;
v = env->cc_v;
+ c = env->cc_c;
COMPUTE_CCR(env->cc_op, x, n, z, v, c);
n = n >> 31;
- v = v >> 31;
z = (z == 0);
+ v = v >> 31;
return x * CCF_X + n * CCF_N + z * CCF_Z + v * CCF_V + c * CCF_C;
}
diff --git a/target-m68k/helper.h b/target-m68k/helper.h
index c8681480f0..2697e32d0b 100644
--- a/target-m68k/helper.h
+++ b/target-m68k/helper.h
@@ -3,8 +3,6 @@ DEF_HELPER_1(ff1, i32, i32)
DEF_HELPER_FLAGS_2(sats, TCG_CALL_NO_RWG_SE, i32, i32, i32)
DEF_HELPER_2(divu, void, env, i32)
DEF_HELPER_2(divs, void, env, i32)
-DEF_HELPER_3(addx_cc, i32, env, i32, i32)
-DEF_HELPER_3(subx_cc, i32, env, i32, i32)
DEF_HELPER_3(shl_cc, i32, env, i32, i32)
DEF_HELPER_3(shr_cc, i32, env, i32, i32)
DEF_HELPER_3(sar_cc, i32, env, i32, i32)
diff --git a/target-m68k/translate.c b/target-m68k/translate.c
index 6c6173a09e..ee0ffe3e07 100644
--- a/target-m68k/translate.c
+++ b/target-m68k/translate.c
@@ -178,9 +178,9 @@ typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn);
static const uint8_t cc_op_live[CC_OP_NB] = {
[CC_OP_FLAGS] = CCF_C | CCF_V | CCF_Z | CCF_N | CCF_X,
- [CC_OP_ADD] = CCF_X | CCF_N | CCF_V,
- [CC_OP_SUB] = CCF_X | CCF_N | CCF_V,
- [CC_OP_CMP] = CCF_X | CCF_N | CCF_V,
+ [CC_OP_ADDB ... CC_OP_ADDL] = CCF_X | CCF_N | CCF_V,
+ [CC_OP_SUBB ... CC_OP_SUBL] = CCF_X | CCF_N | CCF_V,
+ [CC_OP_CMPB ... CC_OP_CMPL] = CCF_X | CCF_N | CCF_V,
[CC_OP_LOGIC] = CCF_X | CCF_N
};
@@ -454,6 +454,33 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base)
return add;
}
+/* Sign or zero extend a value. */
+
+static inline void gen_ext(TCGv res, TCGv val, int opsize, int sign)
+{
+ switch (opsize) {
+ case OS_BYTE:
+ if (sign) {
+ tcg_gen_ext8s_i32(res, val);
+ } else {
+ tcg_gen_ext8u_i32(res, val);
+ }
+ break;
+ case OS_WORD:
+ if (sign) {
+ tcg_gen_ext16s_i32(res, val);
+ } else {
+ tcg_gen_ext16u_i32(res, val);
+ }
+ break;
+ case OS_LONG:
+ tcg_gen_mov_i32(res, val);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
/* Evaluate all the CC flags. */
static void gen_flush_flags(DisasContext *s)
@@ -464,13 +491,16 @@ static void gen_flush_flags(DisasContext *s)
case CC_OP_FLAGS:
return;
- case CC_OP_ADD:
+ case CC_OP_ADDB:
+ case CC_OP_ADDW:
+ case CC_OP_ADDL:
tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X);
tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N);
/* Compute signed overflow for addition. */
t0 = tcg_temp_new();
t1 = tcg_temp_new();
tcg_gen_sub_i32(t0, QREG_CC_N, QREG_CC_V);
+ gen_ext(t0, t0, s->cc_op - CC_OP_ADDB, 1);
tcg_gen_xor_i32(t1, QREG_CC_N, QREG_CC_V);
tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, t0);
tcg_temp_free(t0);
@@ -478,13 +508,16 @@ static void gen_flush_flags(DisasContext *s)
tcg_temp_free(t1);
break;
- case CC_OP_SUB:
+ case CC_OP_SUBB:
+ case CC_OP_SUBW:
+ case CC_OP_SUBL:
tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X);
tcg_gen_mov_i32(QREG_CC_Z, QREG_CC_N);
/* Compute signed overflow for subtraction. */
t0 = tcg_temp_new();
t1 = tcg_temp_new();
tcg_gen_add_i32(t0, QREG_CC_N, QREG_CC_V);
+ gen_ext(t0, t0, s->cc_op - CC_OP_SUBB, 1);
tcg_gen_xor_i32(t1, QREG_CC_N, QREG_CC_V);
tcg_gen_xor_i32(QREG_CC_V, QREG_CC_V, t0);
tcg_temp_free(t0);
@@ -492,9 +525,12 @@ static void gen_flush_flags(DisasContext *s)
tcg_temp_free(t1);
break;
- case CC_OP_CMP:
+ case CC_OP_CMPB:
+ case CC_OP_CMPW:
+ case CC_OP_CMPL:
tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_C, QREG_CC_N, QREG_CC_V);
tcg_gen_sub_i32(QREG_CC_Z, QREG_CC_N, QREG_CC_V);
+ gen_ext(QREG_CC_Z, QREG_CC_Z, s->cc_op - CC_OP_CMPB, 1);
/* Compute signed overflow for subtraction. */
t0 = tcg_temp_new();
tcg_gen_xor_i32(t0, QREG_CC_Z, QREG_CC_N);
@@ -526,34 +562,7 @@ static void gen_flush_flags(DisasContext *s)
s->cc_op_synced = 1;
}
-/* Sign or zero extend a value. */
-
-static inline void gen_ext(TCGv res, TCGv val, int opsize, int sign)
-{
- switch (opsize) {
- case OS_BYTE:
- if (sign) {
- tcg_gen_ext8s_i32(res, val);
- } else {
- tcg_gen_ext8u_i32(res, val);
- }
- break;
- case OS_WORD:
- if (sign) {
- tcg_gen_ext16s_i32(res, val);
- } else {
- tcg_gen_ext16u_i32(res, val);
- }
- break;
- case OS_LONG:
- tcg_gen_mov_i32(res, val);
- break;
- default:
- g_assert_not_reached();
- }
-}
-
-static TCGv gen_extend(TCGv val, int opsize, int sign)
+static inline TCGv gen_extend(TCGv val, int opsize, int sign)
{
TCGv tmp;
@@ -573,10 +582,17 @@ static void gen_logic_cc(DisasContext *s, TCGv val, int opsize)
set_cc_op(s, CC_OP_LOGIC);
}
-static void gen_update_cc_add(TCGv dest, TCGv src)
+static void gen_update_cc_cmp(DisasContext *s, TCGv dest, TCGv src, int opsize)
{
tcg_gen_mov_i32(QREG_CC_N, dest);
tcg_gen_mov_i32(QREG_CC_V, src);
+ set_cc_op(s, CC_OP_CMPB + opsize);
+}
+
+static void gen_update_cc_add(TCGv dest, TCGv src, int opsize)
+{
+ gen_ext(QREG_CC_N, dest, opsize, 1);
+ tcg_gen_mov_i32(QREG_CC_V, src);
}
static inline int opsize_bytes(int opsize)
@@ -822,7 +838,7 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond)
CCOp op = s->cc_op;
/* The CC_OP_CMP form can handle most normal comparisons directly. */
- if (op == CC_OP_CMP) {
+ if (op == CC_OP_CMPB || op == CC_OP_CMPW || op == CC_OP_CMPL) {
c->g1 = c->g2 = 1;
c->v1 = QREG_CC_N;
c->v2 = QREG_CC_V;
@@ -845,6 +861,7 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond)
c->v2 = tcg_const_i32(0);
c->v1 = tmp = tcg_temp_new();
tcg_gen_sub_i32(tmp, QREG_CC_N, QREG_CC_V);
+ gen_ext(tmp, tmp, op - CC_OP_CMPB, 1);
/* fallthru */
case 12: /* GE */
case 13: /* LT */
@@ -888,7 +905,9 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond)
case 10: /* PL (!N) */
case 11: /* MI (N) */
/* Several cases represent N normally. */
- if (op == CC_OP_ADD || op == CC_OP_SUB || op == CC_OP_LOGIC) {
+ if (op == CC_OP_ADDB || op == CC_OP_ADDW || op == CC_OP_ADDL ||
+ op == CC_OP_SUBB || op == CC_OP_SUBW || op == CC_OP_SUBL ||
+ op == CC_OP_LOGIC) {
c->v1 = QREG_CC_N;
tcond = TCG_COND_LT;
goto done;
@@ -897,7 +916,9 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond)
case 6: /* NE (!Z) */
case 7: /* EQ (Z) */
/* Some cases fold Z into N. */
- if (op == CC_OP_ADD || op == CC_OP_SUB || op == CC_OP_LOGIC) {
+ if (op == CC_OP_ADDB || op == CC_OP_ADDW || op == CC_OP_ADDL ||
+ op == CC_OP_SUBB || op == CC_OP_SUBW || op == CC_OP_SUBL ||
+ op == CC_OP_LOGIC) {
tcond = TCG_COND_EQ;
c->v1 = QREG_CC_N;
goto done;
@@ -906,7 +927,8 @@ static void gen_cc_cond(DisasCompare *c, DisasContext *s, int cond)
case 4: /* CC (!C) */
case 5: /* CS (C) */
/* Some cases fold C into X. */
- if (op == CC_OP_ADD || op == CC_OP_SUB) {
+ if (op == CC_OP_ADDB || op == CC_OP_ADDW || op == CC_OP_ADDL ||
+ op == CC_OP_ADDB || op == CC_OP_ADDW || op == CC_OP_ADDL) {
tcond = TCG_COND_NE;
c->v1 = QREG_CC_X;
goto done;
@@ -1008,25 +1030,6 @@ static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1)
free_cond(&c);
}
-DISAS_INSN(scc)
-{
- DisasCompare c;
- int cond;
- TCGv reg, tmp;
-
- cond = (insn >> 8) & 0xf;
- gen_cc_cond(&c, s, cond);
-
- tmp = tcg_temp_new();
- tcg_gen_setcond_i32(c.tcond, tmp, c.v1, c.v2);
- free_cond(&c);
-
- reg = DREG(insn, 0);
- tcg_gen_neg_i32(tmp, tmp);
- tcg_gen_deposit_i32(reg, reg, tmp, 0, 8);
- tcg_temp_free(tmp);
-}
-
/* Force a TB lookup after an instruction that changes the CPU state. */
static void gen_lookup_tb(DisasContext *s)
{
@@ -1106,6 +1109,48 @@ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest)
s->is_jmp = DISAS_TB_JUMP;
}
+DISAS_INSN(scc)
+{
+ DisasCompare c;
+ int cond;
+ TCGv tmp;
+
+ cond = (insn >> 8) & 0xf;
+ gen_cc_cond(&c, s, cond);
+
+ tmp = tcg_temp_new();
+ tcg_gen_setcond_i32(c.tcond, tmp, c.v1, c.v2);
+ free_cond(&c);
+
+ tcg_gen_neg_i32(tmp, tmp);
+ DEST_EA(env, insn, OS_BYTE, tmp, NULL);
+ tcg_temp_free(tmp);
+}
+
+DISAS_INSN(dbcc)
+{
+ TCGLabel *l1;
+ TCGv reg;
+ TCGv tmp;
+ int16_t offset;
+ uint32_t base;
+
+ reg = DREG(insn, 0);
+ base = s->pc;
+ offset = (int16_t)read_im16(env, s);
+ l1 = gen_new_label();
+ gen_jmpcc(s, (insn >> 8) & 0xf, l1);
+
+ tmp = tcg_temp_new();
+ tcg_gen_ext16s_i32(tmp, reg);
+ tcg_gen_addi_i32(tmp, tmp, -1);
+ gen_partset_reg(OS_WORD, reg, tmp);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, -1, l1);
+ gen_jmp_tb(s, 1, base + offset);
+ gen_set_label(l1);
+ gen_jmp_tb(s, 0, s->pc);
+}
+
DISAS_INSN(undef_mac)
{
gen_exception(s, s->pc - 2, EXCP_LINEA);
@@ -1215,35 +1260,37 @@ DISAS_INSN(addsub)
TCGv tmp;
TCGv addr;
int add;
+ int opsize;
add = (insn & 0x4000) != 0;
- reg = DREG(insn, 9);
+ opsize = insn_opsize(insn);
+ reg = gen_extend(DREG(insn, 9), opsize, 1);
dest = tcg_temp_new();
if (insn & 0x100) {
- SRC_EA(env, tmp, OS_LONG, 0, &addr);
+ SRC_EA(env, tmp, opsize, 1, &addr);
src = reg;
} else {
tmp = reg;
- SRC_EA(env, src, OS_LONG, 0, NULL);
+ SRC_EA(env, src, opsize, 1, NULL);
}
if (add) {
tcg_gen_add_i32(dest, tmp, src);
tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, src);
- set_cc_op(s, CC_OP_ADD);
+ set_cc_op(s, CC_OP_ADDB + opsize);
} else {
tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, tmp, src);
tcg_gen_sub_i32(dest, tmp, src);
- set_cc_op(s, CC_OP_SUB);
+ set_cc_op(s, CC_OP_SUBB + opsize);
}
- gen_update_cc_add(dest, src);
+ gen_update_cc_add(dest, src, opsize);
if (insn & 0x100) {
- DEST_EA(env, insn, OS_LONG, dest, &addr);
+ DEST_EA(env, insn, opsize, dest, &addr);
} else {
- tcg_gen_mov_i32(reg, dest);
+ gen_partset_reg(opsize, DREG(insn, 9), dest);
}
+ tcg_temp_free(dest);
}
-
/* Reverse the order of the bits in REG. */
DISAS_INSN(bitrev)
{
@@ -1267,39 +1314,36 @@ DISAS_INSN(bitop_reg)
else
opsize = OS_LONG;
op = (insn >> 6) & 3;
-
- gen_flush_flags(s);
-
SRC_EA(env, src1, opsize, 0, op ? &addr: NULL);
- src2 = DREG(insn, 9);
- dest = tcg_temp_new();
- tmp = tcg_temp_new();
+ gen_flush_flags(s);
+ src2 = tcg_temp_new();
if (opsize == OS_BYTE)
- tcg_gen_andi_i32(tmp, src2, 7);
+ tcg_gen_andi_i32(src2, DREG(insn, 9), 7);
else
- tcg_gen_andi_i32(tmp, src2, 31);
+ tcg_gen_andi_i32(src2, DREG(insn, 9), 31);
- src2 = tcg_const_i32(1);
- tcg_gen_shl_i32(src2, src2, tmp);
- tcg_temp_free(tmp);
+ tmp = tcg_const_i32(1);
+ tcg_gen_shl_i32(tmp, tmp, src2);
+ tcg_temp_free(src2);
- tcg_gen_and_i32(QREG_CC_Z, src1, src2);
+ tcg_gen_and_i32(QREG_CC_Z, src1, tmp);
+ dest = tcg_temp_new();
switch (op) {
case 1: /* bchg */
- tcg_gen_xor_i32(dest, src1, src2);
+ tcg_gen_xor_i32(dest, src1, tmp);
break;
case 2: /* bclr */
- tcg_gen_andc_i32(dest, src1, src2);
+ tcg_gen_andc_i32(dest, src1, tmp);
break;
case 3: /* bset */
- tcg_gen_or_i32(dest, src1, src2);
+ tcg_gen_or_i32(dest, src1, tmp);
break;
default: /* btst */
break;
}
- tcg_temp_free(src2);
+ tcg_temp_free(tmp);
if (op) {
DEST_EA(env, insn, opsize, dest, &addr);
}
@@ -1383,17 +1427,16 @@ DISAS_INSN(bitop_im)
return;
}
- gen_flush_flags(s);
-
SRC_EA(env, src1, opsize, 0, op ? &addr: NULL);
+ gen_flush_flags(s);
if (opsize == OS_BYTE)
bitnum &= 7;
else
bitnum &= 31;
mask = 1 << bitnum;
- tcg_gen_andi_i32(QREG_CC_Z, src1, mask);
+ tcg_gen_andi_i32(QREG_CC_Z, src1, mask);
if (op) {
tmp = tcg_temp_new();
@@ -1418,52 +1461,65 @@ DISAS_INSN(bitop_im)
DISAS_INSN(arith_im)
{
int op;
- uint32_t im;
+ TCGv im;
TCGv src1;
TCGv dest;
TCGv addr;
+ int opsize;
op = (insn >> 9) & 7;
- SRC_EA(env, src1, OS_LONG, 0, (op == 6) ? NULL : &addr);
- im = read_im32(env, s);
+ opsize = insn_opsize(insn);
+ switch (opsize) {
+ case OS_BYTE:
+ im = tcg_const_i32((int8_t)read_im8(env, s));
+ break;
+ case OS_WORD:
+ im = tcg_const_i32((int16_t)read_im16(env, s));
+ break;
+ case OS_LONG:
+ im = tcg_const_i32(read_im32(env, s));
+ break;
+ default:
+ abort();
+ }
+ SRC_EA(env, src1, opsize, 1, (op == 6) ? NULL : &addr);
dest = tcg_temp_new();
switch (op) {
case 0: /* ori */
- tcg_gen_ori_i32(dest, src1, im);
- gen_logic_cc(s, dest, OS_LONG);
+ tcg_gen_or_i32(dest, src1, im);
+ gen_logic_cc(s, dest, opsize);
break;
case 1: /* andi */
- tcg_gen_andi_i32(dest, src1, im);
- gen_logic_cc(s, dest, OS_LONG);
+ tcg_gen_and_i32(dest, src1, im);
+ gen_logic_cc(s, dest, opsize);
break;
case 2: /* subi */
- tcg_gen_mov_i32(dest, src1);
- tcg_gen_setcondi_i32(TCG_COND_LTU, QREG_CC_X, dest, im);
- tcg_gen_subi_i32(dest, dest, im);
- gen_update_cc_add(dest, tcg_const_i32(im));
- set_cc_op(s, CC_OP_SUB);
+ tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, src1, im);
+ tcg_gen_sub_i32(dest, src1, im);
+ gen_update_cc_add(dest, im, opsize);
+ set_cc_op(s, CC_OP_SUBB + opsize);
break;
case 3: /* addi */
- tcg_gen_mov_i32(dest, src1);
- tcg_gen_addi_i32(dest, dest, im);
- gen_update_cc_add(dest, tcg_const_i32(im));
- tcg_gen_setcondi_i32(TCG_COND_LTU, QREG_CC_X, dest, im);
- set_cc_op(s, CC_OP_ADD);
+ tcg_gen_add_i32(dest, src1, im);
+ gen_update_cc_add(dest, im, opsize);
+ tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, im);
+ set_cc_op(s, CC_OP_ADDB + opsize);
break;
case 5: /* eori */
- tcg_gen_xori_i32(dest, src1, im);
- gen_logic_cc(s, dest, OS_LONG);
+ tcg_gen_xor_i32(dest, src1, im);
+ gen_logic_cc(s, dest, opsize);
break;
case 6: /* cmpi */
- gen_update_cc_add(src1, tcg_const_i32(im));
- set_cc_op(s, CC_OP_CMP);
+ gen_update_cc_cmp(s, src1, im, opsize);
break;
default:
abort();
}
+ tcg_temp_free(im);
if (op != 6) {
- DEST_EA(env, insn, OS_LONG, dest, &addr);
+ DEST_EA(env, insn, opsize, dest, &addr);
}
+ tcg_temp_free(dest);
}
DISAS_INSN(byterev)
@@ -1513,11 +1569,44 @@ DISAS_INSN(move)
DISAS_INSN(negx)
{
- TCGv reg;
+ TCGv z;
+ TCGv src;
+ TCGv addr;
+ int opsize;
- gen_flush_flags(s);
- reg = DREG(insn, 0);
- gen_helper_subx_cc(reg, cpu_env, tcg_const_i32(0), reg);
+ opsize = insn_opsize(insn);
+ SRC_EA(env, src, opsize, 1, &addr);
+
+ gen_flush_flags(s); /* compute old Z */
+
+ /* Perform substract with borrow.
+ * (X, N) = -(src + X);
+ */
+
+ z = tcg_const_i32(0);
+ tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, src, z, QREG_CC_X, z);
+ tcg_gen_sub2_i32(QREG_CC_N, QREG_CC_X, z, z, QREG_CC_N, QREG_CC_X);
+ tcg_temp_free(z);
+ gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1);
+
+ tcg_gen_andi_i32(QREG_CC_X, QREG_CC_X, 1);
+
+ /* Compute signed-overflow for negation. The normal formula for
+ * subtraction is (res ^ src) & (src ^ dest), but with dest==0
+ * this simplies to res & src.
+ */
+
+ tcg_gen_and_i32(QREG_CC_V, QREG_CC_N, src);
+
+ /* Copy the rest of the results into place. */
+ tcg_gen_or_i32(QREG_CC_Z, QREG_CC_Z, QREG_CC_N); /* !Z is sticky */
+ tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X);
+
+ set_cc_op(s, CC_OP_FLAGS);
+
+ /* result is in QREG_CC_N */
+
+ DEST_EA(env, insn, opsize, QREG_CC_N, &addr);
}
DISAS_INSN(lea)
@@ -1564,16 +1653,20 @@ DISAS_INSN(move_from_ccr)
DISAS_INSN(neg)
{
- TCGv reg;
TCGv src1;
+ TCGv dest;
+ TCGv addr;
+ int opsize;
- reg = DREG(insn, 0);
- src1 = tcg_temp_new();
- tcg_gen_mov_i32(src1, reg);
- tcg_gen_neg_i32(reg, src1);
- gen_update_cc_add(reg, src1);
- tcg_gen_setcondi_i32(TCG_COND_NE, QREG_CC_X, src1, 0);
- set_cc_op(s, CC_OP_SUB);
+ opsize = insn_opsize(insn);
+ SRC_EA(env, src1, opsize, 1, &addr);
+ dest = tcg_temp_new();
+ tcg_gen_neg_i32(dest, src1);
+ set_cc_op(s, CC_OP_SUBB + opsize);
+ gen_update_cc_add(dest, src1, opsize);
+ tcg_gen_setcondi_i32(TCG_COND_NE, QREG_CC_X, dest, 0);
+ DEST_EA(env, insn, opsize, dest, &addr);
+ tcg_temp_free(dest);
}
static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only)
@@ -1617,11 +1710,17 @@ DISAS_INSN(move_to_ccr)
DISAS_INSN(not)
{
- TCGv reg;
+ TCGv src1;
+ TCGv dest;
+ TCGv addr;
+ int opsize;
- reg = DREG(insn, 0);
- tcg_gen_not_i32(reg, reg);
- gen_logic_cc(s, reg, OS_LONG);
+ opsize = insn_opsize(insn);
+ SRC_EA(env, src1, opsize, 1, &addr);
+ dest = tcg_temp_new();
+ tcg_gen_not_i32(dest, src1);
+ DEST_EA(env, insn, opsize, dest, &addr);
+ gen_logic_cc(s, dest, opsize);
}
DISAS_INSN(swap)
@@ -1639,6 +1738,11 @@ DISAS_INSN(swap)
gen_logic_cc(s, reg, OS_LONG);
}
+DISAS_INSN(bkpt)
+{
+ gen_exception(s, s->pc - 2, EXCP_DEBUG);
+}
+
DISAS_INSN(pea)
{
TCGv tmp;
@@ -1728,21 +1832,36 @@ DISAS_INSN(mull)
gen_logic_cc(s, dest, OS_LONG);
}
-DISAS_INSN(link)
+static void gen_link(DisasContext *s, uint16_t insn, int32_t offset)
{
- int16_t offset;
TCGv reg;
TCGv tmp;
- offset = cpu_ldsw_code(env, s->pc);
- s->pc += 2;
reg = AREG(insn, 0);
tmp = tcg_temp_new();
tcg_gen_subi_i32(tmp, QREG_SP, 4);
gen_store(s, OS_LONG, tmp, reg);
- if ((insn & 7) != 7)
+ if ((insn & 7) != 7) {
tcg_gen_mov_i32(reg, tmp);
+ }
tcg_gen_addi_i32(QREG_SP, tmp, offset);
+ tcg_temp_free(tmp);
+}
+
+DISAS_INSN(link)
+{
+ int16_t offset;
+
+ offset = read_im16(env, s);
+ gen_link(s, insn, offset);
+}
+
+DISAS_INSN(linkl)
+{
+ int32_t offset;
+
+ offset = read_im32(env, s);
+ gen_link(s, insn, offset);
}
DISAS_INSN(unlk)
@@ -1792,40 +1911,48 @@ DISAS_INSN(jump)
DISAS_INSN(addsubq)
{
- TCGv src1;
- TCGv src2;
+ TCGv src;
TCGv dest;
- int val;
+ TCGv val;
+ int imm;
TCGv addr;
+ int opsize;
- SRC_EA(env, src1, OS_LONG, 0, &addr);
- val = (insn >> 9) & 7;
- if (val == 0)
- val = 8;
+ if ((insn & 070) == 010) {
+ /* Operation on address register is always long. */
+ opsize = OS_LONG;
+ } else {
+ opsize = insn_opsize(insn);
+ }
+ SRC_EA(env, src, opsize, 1, &addr);
+ imm = (insn >> 9) & 7;
+ if (imm == 0) {
+ imm = 8;
+ }
+ val = tcg_const_i32(imm);
dest = tcg_temp_new();
- tcg_gen_mov_i32(dest, src1);
+ tcg_gen_mov_i32(dest, src);
if ((insn & 0x38) == 0x08) {
/* Don't update condition codes if the destination is an
address register. */
if (insn & 0x0100) {
- tcg_gen_subi_i32(dest, dest, val);
+ tcg_gen_sub_i32(dest, dest, val);
} else {
- tcg_gen_addi_i32(dest, dest, val);
+ tcg_gen_add_i32(dest, dest, val);
}
} else {
- src2 = tcg_const_i32(val);
if (insn & 0x0100) {
- tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, src2);
- tcg_gen_sub_i32(dest, dest, src2);
- set_cc_op(s, CC_OP_SUB);
+ tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, val);
+ tcg_gen_sub_i32(dest, dest, val);
+ set_cc_op(s, CC_OP_SUBB + opsize);
} else {
- tcg_gen_add_i32(dest, dest, src2);
- tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, src2);
- set_cc_op(s, CC_OP_ADD);
+ tcg_gen_add_i32(dest, dest, val);
+ tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, val);
+ set_cc_op(s, CC_OP_ADDB + opsize);
}
- gen_update_cc_add(dest, src2);
+ gen_update_cc_add(dest, val, opsize);
}
- DEST_EA(env, insn, OS_LONG, dest, &addr);
+ DEST_EA(env, insn, opsize, dest, &addr);
}
DISAS_INSN(tpf)
@@ -1907,19 +2034,21 @@ DISAS_INSN(or)
TCGv dest;
TCGv src;
TCGv addr;
+ int opsize;
- reg = DREG(insn, 9);
+ opsize = insn_opsize(insn);
+ reg = gen_extend(DREG(insn, 9), opsize, 0);
dest = tcg_temp_new();
if (insn & 0x100) {
- SRC_EA(env, src, OS_LONG, 0, &addr);
+ SRC_EA(env, src, opsize, 0, &addr);
tcg_gen_or_i32(dest, src, reg);
- DEST_EA(env, insn, OS_LONG, dest, &addr);
+ DEST_EA(env, insn, opsize, dest, &addr);
} else {
- SRC_EA(env, src, OS_LONG, 0, NULL);
+ SRC_EA(env, src, opsize, 0, NULL);
tcg_gen_or_i32(dest, src, reg);
- tcg_gen_mov_i32(reg, dest);
+ gen_partset_reg(opsize, DREG(insn, 9), dest);
}
- gen_logic_cc(s, dest, OS_LONG);
+ gen_logic_cc(s, dest, opsize);
}
DISAS_INSN(suba)
@@ -1927,20 +2056,80 @@ DISAS_INSN(suba)
TCGv src;
TCGv reg;
- SRC_EA(env, src, OS_LONG, 0, NULL);
+ SRC_EA(env, src, (insn & 0x100) ? OS_LONG : OS_WORD, 1, NULL);
reg = AREG(insn, 9);
tcg_gen_sub_i32(reg, reg, src);
}
-DISAS_INSN(subx)
+static inline void gen_subx(DisasContext *s, TCGv src, TCGv dest, int opsize)
{
- TCGv reg;
+ TCGv tmp;
+
+ gen_flush_flags(s); /* compute old Z */
+
+ /* Perform substract with borrow.
+ * (X, N) = dest - (src + X);
+ */
+
+ tmp = tcg_const_i32(0);
+ tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, src, tmp, QREG_CC_X, tmp);
+ tcg_gen_sub2_i32(QREG_CC_N, QREG_CC_X, dest, tmp, QREG_CC_N, QREG_CC_X);
+ gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1);
+ tcg_gen_andi_i32(QREG_CC_X, QREG_CC_X, 1);
+
+ /* Compute signed-overflow for substract. */
+
+ tcg_gen_xor_i32(QREG_CC_V, QREG_CC_N, dest);
+ tcg_gen_xor_i32(tmp, dest, src);
+ tcg_gen_and_i32(QREG_CC_V, QREG_CC_V, tmp);
+ tcg_temp_free(tmp);
+
+ /* Copy the rest of the results into place. */
+ tcg_gen_or_i32(QREG_CC_Z, QREG_CC_Z, QREG_CC_N); /* !Z is sticky */
+ tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X);
+
+ set_cc_op(s, CC_OP_FLAGS);
+
+ /* result is in QREG_CC_N */
+}
+
+DISAS_INSN(subx_reg)
+{
+ TCGv dest;
TCGv src;
+ int opsize;
- gen_flush_flags(s);
- reg = DREG(insn, 9);
- src = DREG(insn, 0);
- gen_helper_subx_cc(reg, cpu_env, reg, src);
+ opsize = insn_opsize(insn);
+
+ src = gen_extend(DREG(insn, 0), opsize, 1);
+ dest = gen_extend(DREG(insn, 9), opsize, 1);
+
+ gen_subx(s, src, dest, opsize);
+
+ gen_partset_reg(opsize, DREG(insn, 9), QREG_CC_N);
+}
+
+DISAS_INSN(subx_mem)
+{
+ TCGv src;
+ TCGv addr_src;
+ TCGv dest;
+ TCGv addr_dest;
+ int opsize;
+
+ opsize = insn_opsize(insn);
+
+ addr_src = AREG(insn, 0);
+ tcg_gen_subi_i32(addr_src, addr_src, opsize);
+ src = gen_load(s, opsize, addr_src, 1);
+
+ addr_dest = AREG(insn, 9);
+ tcg_gen_subi_i32(addr_dest, addr_dest, opsize);
+ dest = gen_load(s, opsize, addr_dest, 1);
+
+ gen_subx(s, src, dest, opsize);
+
+ gen_store(s, opsize, addr_dest, QREG_CC_N);
}
DISAS_INSN(mov3q)
@@ -1963,10 +2152,9 @@ DISAS_INSN(cmp)
int opsize;
opsize = insn_opsize(insn);
- SRC_EA(env, src, opsize, -1, NULL);
- reg = DREG(insn, 9);
- gen_update_cc_add(reg, src);
- set_cc_op(s, CC_OP_CMP);
+ SRC_EA(env, src, opsize, 1, NULL);
+ reg = gen_extend(DREG(insn, 9), opsize, 1);
+ gen_update_cc_cmp(s, reg, src, opsize);
}
DISAS_INSN(cmpa)
@@ -1982,23 +2170,50 @@ DISAS_INSN(cmpa)
}
SRC_EA(env, src, opsize, 1, NULL);
reg = AREG(insn, 9);
- gen_update_cc_add(reg, src);
- set_cc_op(s, CC_OP_CMP);
+ gen_update_cc_cmp(s, reg, src, opsize);
}
DISAS_INSN(eor)
{
TCGv src;
- TCGv reg;
TCGv dest;
TCGv addr;
+ int opsize;
- SRC_EA(env, src, OS_LONG, 0, &addr);
- reg = DREG(insn, 9);
+ opsize = insn_opsize(insn);
+
+ SRC_EA(env, src, opsize, 0, &addr);
dest = tcg_temp_new();
- tcg_gen_xor_i32(dest, src, reg);
- gen_logic_cc(s, dest, OS_LONG);
- DEST_EA(env, insn, OS_LONG, dest, &addr);
+ tcg_gen_xor_i32(dest, src, DREG(insn, 9));
+ gen_logic_cc(s, dest, opsize);
+ DEST_EA(env, insn, opsize, dest, &addr);
+}
+
+static void do_exg(TCGv reg1, TCGv reg2)
+{
+ TCGv temp = tcg_temp_new();
+ tcg_gen_mov_i32(temp, reg1);
+ tcg_gen_mov_i32(reg1, reg2);
+ tcg_gen_mov_i32(reg2, temp);
+ tcg_temp_free(temp);
+}
+
+DISAS_INSN(exg_aa)
+{
+ /* exchange Dx and Dy */
+ do_exg(DREG(insn, 9), DREG(insn, 0));
+}
+
+DISAS_INSN(exg_dd)
+{
+ /* exchange Ax and Ay */
+ do_exg(AREG(insn, 9), AREG(insn, 0));
+}
+
+DISAS_INSN(exg_da)
+{
+ /* exchange Dx and Ay */
+ do_exg(DREG(insn, 9), AREG(insn, 0));
}
DISAS_INSN(and)
@@ -2007,19 +2222,23 @@ DISAS_INSN(and)
TCGv reg;
TCGv dest;
TCGv addr;
+ int opsize;
- reg = DREG(insn, 9);
dest = tcg_temp_new();
+
+ opsize = insn_opsize(insn);
+ reg = DREG(insn, 9);
if (insn & 0x100) {
- SRC_EA(env, src, OS_LONG, 0, &addr);
+ SRC_EA(env, src, opsize, 0, &addr);
tcg_gen_and_i32(dest, src, reg);
- DEST_EA(env, insn, OS_LONG, dest, &addr);
+ DEST_EA(env, insn, opsize, dest, &addr);
} else {
- SRC_EA(env, src, OS_LONG, 0, NULL);
+ SRC_EA(env, src, opsize, 0, NULL);
tcg_gen_and_i32(dest, src, reg);
- tcg_gen_mov_i32(reg, dest);
+ gen_partset_reg(opsize, reg, dest);
}
- gen_logic_cc(s, dest, OS_LONG);
+ tcg_temp_free(dest);
+ gen_logic_cc(s, dest, opsize);
}
DISAS_INSN(adda)
@@ -2027,20 +2246,79 @@ DISAS_INSN(adda)
TCGv src;
TCGv reg;
- SRC_EA(env, src, OS_LONG, 0, NULL);
+ SRC_EA(env, src, (insn & 0x100) ? OS_LONG : OS_WORD, 1, NULL);
reg = AREG(insn, 9);
tcg_gen_add_i32(reg, reg, src);
}
-DISAS_INSN(addx)
+static inline void gen_addx(DisasContext *s, TCGv src, TCGv dest, int opsize)
{
- TCGv reg;
+ TCGv tmp;
+
+ gen_flush_flags(s); /* compute old Z */
+
+ /* Perform addition with carry.
+ * (X, N) = src + dest + X;
+ */
+
+ tmp = tcg_const_i32(0);
+ tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, QREG_CC_X, tmp, dest, tmp);
+ tcg_gen_add2_i32(QREG_CC_N, QREG_CC_X, QREG_CC_N, QREG_CC_X, src, tmp);
+ gen_ext(QREG_CC_N, QREG_CC_N, opsize, 1);
+
+ /* Compute signed-overflow for addition. */
+
+ tcg_gen_xor_i32(QREG_CC_V, QREG_CC_N, src);
+ tcg_gen_xor_i32(tmp, dest, src);
+ tcg_gen_andc_i32(QREG_CC_V, QREG_CC_V, tmp);
+ tcg_temp_free(tmp);
+
+ /* Copy the rest of the results into place. */
+ tcg_gen_or_i32(QREG_CC_Z, QREG_CC_Z, QREG_CC_N); /* !Z is sticky */
+ tcg_gen_mov_i32(QREG_CC_C, QREG_CC_X);
+
+ set_cc_op(s, CC_OP_FLAGS);
+
+ /* result is in QREG_CC_N */
+}
+
+DISAS_INSN(addx_reg)
+{
+ TCGv dest;
TCGv src;
+ int opsize;
- gen_flush_flags(s);
- reg = DREG(insn, 9);
- src = DREG(insn, 0);
- gen_helper_addx_cc(reg, cpu_env, reg, src);
+ opsize = insn_opsize(insn);
+
+ dest = gen_extend(DREG(insn, 9), opsize, 1);
+ src = gen_extend(DREG(insn, 0), opsize, 1);
+
+ gen_addx(s, src, dest, opsize);
+
+ gen_partset_reg(opsize, DREG(insn, 9), QREG_CC_N);
+}
+
+DISAS_INSN(addx_mem)
+{
+ TCGv src;
+ TCGv addr_src;
+ TCGv dest;
+ TCGv addr_dest;
+ int opsize;
+
+ opsize = insn_opsize(insn);
+
+ addr_src = AREG(insn, 0);
+ tcg_gen_subi_i32(addr_src, addr_src, opsize_bytes(opsize));
+ src = gen_load(s, opsize, addr_src, 1);
+
+ addr_dest = AREG(insn, 9);
+ tcg_gen_subi_i32(addr_dest, addr_dest, opsize_bytes(opsize));
+ dest = gen_load(s, opsize, addr_dest, 1);
+
+ gen_addx(s, src, dest, opsize);
+
+ gen_store(s, opsize, addr_dest, QREG_CC_N);
}
/* TODO: This could be implemented without helper functions. */
@@ -3039,6 +3317,8 @@ void register_m68k_insns (CPUM68KState *env)
BASE(move, 3000, f000);
INSN(strldsr, 40e7, ffff, CF_ISA_APLUSC);
INSN(negx, 4080, fff8, CF_ISA_A);
+ INSN(negx, 4000, ff00, M68000);
+ INSN(undef, 40c0, ffc0, M68000);
INSN(move_from_sr, 40c0, fff8, CF_ISA_A);
INSN(move_from_sr, 40c0, ffc0, M68000);
BASE(lea, 41c0, f1c0);
@@ -3054,8 +3334,10 @@ void register_m68k_insns (CPUM68KState *env)
INSN(not, 4600, ff00, M68000);
INSN(undef, 46c0, ffc0, M68000);
INSN(move_to_sr, 46c0, ffc0, CF_ISA_A);
+ INSN(linkl, 4808, fff8, M68000);
BASE(pea, 4840, ffc0);
BASE(swap, 4840, fff8);
+ INSN(bkpt, 4848, fff8, BKPT);
BASE(movem, 48c0, fbc0);
BASE(ext, 4880, fff8);
BASE(ext, 48c0, fff8);
@@ -3082,13 +3364,12 @@ void register_m68k_insns (CPUM68KState *env)
BASE(rts, 4e75, ffff);
INSN(movec, 4e7b, ffff, CF_ISA_A);
BASE(jump, 4e80, ffc0);
- INSN(jump, 4ec0, ffc0, CF_ISA_A);
- INSN(addsubq, 5180, f1c0, CF_ISA_A);
- INSN(jump, 4ec0, ffc0, M68000);
+ BASE(jump, 4ec0, ffc0);
INSN(addsubq, 5000, f080, M68000);
- INSN(addsubq, 5080, f0c0, M68000);
- INSN(scc, 50c0, f0f8, CF_ISA_A);
- INSN(addsubq, 5080, f1c0, CF_ISA_A);
+ BASE(addsubq, 5080, f0c0);
+ INSN(scc, 50c0, f0f8, CF_ISA_A); /* Scc.B Dx */
+ INSN(scc, 50c0, f0c0, M68000); /* Scc.B <EA> */
+ INSN(dbcc, 50c8, f0f8, M68000);
INSN(tpf, 51f8, fff8, CF_ISA_A);
/* Branch instructions. */
@@ -3105,8 +3386,12 @@ void register_m68k_insns (CPUM68KState *env)
BASE(or, 8000, f000);
BASE(divw, 80c0, f0c0);
BASE(addsub, 9000, f000);
- INSN(subx, 9180, f1f8, CF_ISA_A);
+ INSN(undef, 90c0, f0c0, CF_ISA_A);
+ INSN(subx_reg, 9180, f1f8, CF_ISA_A);
+ INSN(subx_reg, 9100, f138, M68000);
+ INSN(subx_mem, 9108, f138, M68000);
INSN(suba, 91c0, f1c0, CF_ISA_A);
+ INSN(suba, 90c0, f0c0, M68000);
BASE(undef_mac, a000, f000);
INSN(mac, a000, f100, CF_EMAC);
@@ -3132,9 +3417,15 @@ void register_m68k_insns (CPUM68KState *env)
INSN(cmpa, b0c0, f0c0, M68000);
INSN(eor, b180, f1c0, CF_ISA_A);
BASE(and, c000, f000);
+ INSN(exg_dd, c140, f1f8, M68000);
+ INSN(exg_aa, c148, f1f8, M68000);
+ INSN(exg_da, c188, f1f8, M68000);
BASE(mulw, c0c0, f0c0);
BASE(addsub, d000, f000);
- INSN(addx, d180, f1f8, CF_ISA_A);
+ INSN(undef, d0c0, f0c0, CF_ISA_A);
+ INSN(addx_reg, d180, f1f8, CF_ISA_A);
+ INSN(addx_reg, d100, f138, M68000);
+ INSN(addx_mem, d108, f138, M68000);
INSN(adda, d1c0, f1c0, CF_ISA_A);
INSN(adda, d0c0, f0c0, M68000);
INSN(shift_im, e080, f0f0, CF_ISA_A);
diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c
index 921c39d33f..808760bf53 100644
--- a/target-ppc/excp_helper.c
+++ b/target-ppc/excp_helper.c
@@ -213,7 +213,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
cs->halted = 1;
cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
}
- new_msr |= (target_ulong)MSR_HVB;
+ if (env->msr_mask & MSR_HVB) {
+ /* ISA specifies HV, but can be delivered to guest with HV clear
+ * (e.g., see FWNMI in PAPR).
+ */
+ new_msr |= (target_ulong)MSR_HVB;
+ }
ail = 0;
/* machine check exceptions don't have ME set */
@@ -385,14 +390,23 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
srr1 = SPR_BOOKE_CSRR1;
break;
case POWERPC_EXCP_RESET: /* System reset exception */
+ /* A power-saving exception sets ME, otherwise it is unchanged */
if (msr_pow) {
/* indicate that we resumed from power save mode */
msr |= 0x10000;
+ new_msr |= ((target_ulong)1 << MSR_ME);
+ }
+ if (env->msr_mask & MSR_HVB) {
+ /* ISA specifies HV, but can be delivered to guest with HV clear
+ * (e.g., see FWNMI in PAPR, NMI injection in QEMU).
+ */
+ new_msr |= (target_ulong)MSR_HVB;
} else {
- new_msr &= ~((target_ulong)1 << MSR_ME);
+ if (msr_pow) {
+ cpu_abort(cs, "Trying to deliver power-saving system reset "
+ "exception %d with no HV support\n", excp);
+ }
}
-
- new_msr |= (target_ulong)MSR_HVB;
ail = 0;
break;
case POWERPC_EXCP_DSEG: /* Data segment exception */
@@ -609,9 +623,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
env->spr[srr1] = msr;
/* Sanity check */
- if (!(env->msr_mask & MSR_HVB) && (srr0 == SPR_HSRR0)) {
- cpu_abort(cs, "Trying to deliver HV exception %d with "
- "no HV support\n", excp);
+ if (!(env->msr_mask & MSR_HVB)) {
+ if (new_msr & MSR_HVB) {
+ cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with "
+ "no HV support\n", excp);
+ }
+ if (srr0 == SPR_HSRR0) {
+ cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with "
+ "no HV support\n", excp);
+ }
}
/* If any alternate SRR register are defined, duplicate saved values */
diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c
index b0760f041d..8a389e19af 100644
--- a/target-ppc/fpu_helper.c
+++ b/target-ppc/fpu_helper.c
@@ -2362,6 +2362,58 @@ VSX_MADD(xvnmaddmsp, 4, float32, VsrW(i), NMADD_FLGS, 0, 0, 0)
VSX_MADD(xvnmsubasp, 4, float32, VsrW(i), NMSUB_FLGS, 1, 0, 0)
VSX_MADD(xvnmsubmsp, 4, float32, VsrW(i), NMSUB_FLGS, 0, 0, 0)
+/* VSX_SCALAR_CMP_DP - VSX scalar floating point compare double precision
+ * op - instruction mnemonic
+ * cmp - comparison operation
+ * exp - expected result of comparison
+ * svxvc - set VXVC bit
+ */
+#define VSX_SCALAR_CMP_DP(op, cmp, exp, svxvc) \
+void helper_##op(CPUPPCState *env, uint32_t opcode) \
+{ \
+ ppc_vsr_t xt, xa, xb; \
+ bool vxsnan_flag = false, vxvc_flag = false, vex_flag = false; \
+ \
+ getVSR(xA(opcode), &xa, env); \
+ getVSR(xB(opcode), &xb, env); \
+ getVSR(xT(opcode), &xt, env); \
+ \
+ if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) || \
+ float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) { \
+ vxsnan_flag = true; \
+ if (fpscr_ve == 0 && svxvc) { \
+ vxvc_flag = true; \
+ } \
+ } else if (svxvc) { \
+ vxvc_flag = float64_is_quiet_nan(xa.VsrD(0), &env->fp_status) || \
+ float64_is_quiet_nan(xb.VsrD(0), &env->fp_status); \
+ } \
+ if (vxsnan_flag) { \
+ float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \
+ } \
+ if (vxvc_flag) { \
+ float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \
+ } \
+ vex_flag = fpscr_ve && (vxvc_flag || vxsnan_flag); \
+ \
+ if (!vex_flag) { \
+ if (float64_##cmp(xb.VsrD(0), xa.VsrD(0), &env->fp_status) == exp) { \
+ xt.VsrD(0) = -1; \
+ xt.VsrD(1) = 0; \
+ } else { \
+ xt.VsrD(0) = 0; \
+ xt.VsrD(1) = 0; \
+ } \
+ } \
+ putVSR(xT(opcode), &xt, env); \
+ helper_float_check_status(env); \
+}
+
+VSX_SCALAR_CMP_DP(xscmpeqdp, eq, 1, 0)
+VSX_SCALAR_CMP_DP(xscmpgedp, le, 1, 1)
+VSX_SCALAR_CMP_DP(xscmpgtdp, lt, 1, 1)
+VSX_SCALAR_CMP_DP(xscmpnedp, eq, 0, 0)
+
#define VSX_SCALAR_CMP(op, ordered) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
@@ -2445,8 +2497,9 @@ VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i))
* fld - vsr_t field (VsrD(*) or VsrW(*))
* cmp - comparison operation
* svxvc - set VXVC bit
+ * exp - expected result of comparison
*/
-#define VSX_CMP(op, nels, tp, fld, cmp, svxvc) \
+#define VSX_CMP(op, nels, tp, fld, cmp, svxvc, exp) \
void helper_##op(CPUPPCState *env, uint32_t opcode) \
{ \
ppc_vsr_t xt, xa, xb; \
@@ -2471,7 +2524,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
xt.fld = 0; \
all_true = 0; \
} else { \
- if (tp##_##cmp(xb.fld, xa.fld, &env->fp_status) == 1) { \
+ if (tp##_##cmp(xb.fld, xa.fld, &env->fp_status) == exp) { \
xt.fld = -1; \
all_false = 0; \
} else { \
@@ -2488,12 +2541,14 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \
float_check_status(env); \
}
-VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0)
-VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1)
-VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1)
-VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0)
-VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1)
-VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1)
+VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0, 1)
+VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1, 1)
+VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1, 1)
+VSX_CMP(xvcmpnedp, 2, float64, VsrD(i), eq, 0, 0)
+VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0, 1)
+VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1, 1)
+VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1, 1)
+VSX_CMP(xvcmpnesp, 4, float32, VsrW(i), eq, 0, 0)
/* VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion
* op - instruction mnemonic
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index 04c64217b7..3916b2eddc 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -272,6 +272,8 @@ DEF_HELPER_2(vextsh2w, void, avr, avr)
DEF_HELPER_2(vextsb2d, void, avr, avr)
DEF_HELPER_2(vextsh2d, void, avr, avr)
DEF_HELPER_2(vextsw2d, void, avr, avr)
+DEF_HELPER_2(vnegw, void, avr, avr)
+DEF_HELPER_2(vnegd, void, avr, avr)
DEF_HELPER_2(vupkhpx, void, avr, avr)
DEF_HELPER_2(vupklpx, void, avr, avr)
DEF_HELPER_2(vupkhsb, void, avr, avr)
@@ -387,6 +389,10 @@ DEF_HELPER_2(xsnmaddadp, void, env, i32)
DEF_HELPER_2(xsnmaddmdp, void, env, i32)
DEF_HELPER_2(xsnmsubadp, void, env, i32)
DEF_HELPER_2(xsnmsubmdp, void, env, i32)
+DEF_HELPER_2(xscmpeqdp, void, env, i32)
+DEF_HELPER_2(xscmpgtdp, void, env, i32)
+DEF_HELPER_2(xscmpgedp, void, env, i32)
+DEF_HELPER_2(xscmpnedp, void, env, i32)
DEF_HELPER_2(xscmpodp, void, env, i32)
DEF_HELPER_2(xscmpudp, void, env, i32)
DEF_HELPER_2(xsmaxdp, void, env, i32)
@@ -448,6 +454,7 @@ DEF_HELPER_2(xvmindp, void, env, i32)
DEF_HELPER_2(xvcmpeqdp, void, env, i32)
DEF_HELPER_2(xvcmpgedp, void, env, i32)
DEF_HELPER_2(xvcmpgtdp, void, env, i32)
+DEF_HELPER_2(xvcmpnedp, void, env, i32)
DEF_HELPER_2(xvcvdpsp, void, env, i32)
DEF_HELPER_2(xvcvdpsxds, void, env, i32)
DEF_HELPER_2(xvcvdpsxws, void, env, i32)
@@ -485,6 +492,7 @@ DEF_HELPER_2(xvminsp, void, env, i32)
DEF_HELPER_2(xvcmpeqsp, void, env, i32)
DEF_HELPER_2(xvcmpgesp, void, env, i32)
DEF_HELPER_2(xvcmpgtsp, void, env, i32)
+DEF_HELPER_2(xvcmpnesp, void, env, i32)
DEF_HELPER_2(xvcvspdp, void, env, i32)
DEF_HELPER_2(xvcvspsxds, void, env, i32)
DEF_HELPER_2(xvcvspsxws, void, env, i32)
diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c
index 5aee0a81c7..dca479838d 100644
--- a/target-ppc/int_helper.c
+++ b/target-ppc/int_helper.c
@@ -1949,6 +1949,18 @@ VEXT_SIGNED(vextsh2d, s64, UINT16_MAX, int16_t, int64_t)
VEXT_SIGNED(vextsw2d, s64, UINT32_MAX, int32_t, int64_t)
#undef VEXT_SIGNED
+#define VNEG(name, element) \
+void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \
+{ \
+ int i; \
+ VECTOR_FOR_INORDER_I(i, element) { \
+ r->element[i] = -b->element[i]; \
+ } \
+}
+VNEG(vnegw, s32)
+VNEG(vnegd, s64)
+#undef VNEG
+
#define VSPLTI(suffix, element, splat_type) \
void helper_vspltis##suffix(ppc_avr_t *r, uint32_t splat) \
{ \
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index dab8f19a91..43505a936c 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -322,7 +322,7 @@ static void gen_debug_exception(DisasContext *ctx)
*/
if ((ctx->exception != POWERPC_EXCP_BRANCH) &&
(ctx->exception != POWERPC_EXCP_SYNC)) {
- gen_update_nip(ctx, ctx->nip - 4);
+ gen_update_nip(ctx, ctx->nip);
}
t0 = tcg_const_i32(EXCP_DEBUG);
gen_helper_raise_exception(cpu_env, t0);
@@ -376,6 +376,9 @@ GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type, type2)
#define GEN_HANDLER_E_2(name, opc1, opc2, opc3, opc4, inval, type, type2) \
GEN_OPCODE3(name, opc1, opc2, opc3, opc4, inval, type, type2)
+#define GEN_HANDLER2_E_2(name, onam, opc1, opc2, opc3, opc4, inval, typ, typ2) \
+GEN_OPCODE4(name, onam, opc1, opc2, opc3, opc4, inval, typ, typ2)
+
typedef struct opcode_t {
unsigned char opc1, opc2, opc3, opc4;
#if HOST_LONG_BITS == 64 /* Explicitly align to 64 bits */
@@ -662,6 +665,21 @@ EXTRACT_HELPER(IMM8, 11, 8);
}, \
.oname = stringify(name), \
}
+#define GEN_OPCODE4(name, onam, op1, op2, op3, op4, invl, _typ, _typ2) \
+{ \
+ .opc1 = op1, \
+ .opc2 = op2, \
+ .opc3 = op3, \
+ .opc4 = op4, \
+ .handler = { \
+ .inval1 = invl, \
+ .type = _typ, \
+ .type2 = _typ2, \
+ .handler = &gen_##name, \
+ .oname = onam, \
+ }, \
+ .oname = onam, \
+}
#else
#define GEN_OPCODE(name, op1, op2, op3, invl, _typ, _typ2) \
{ \
@@ -720,6 +738,20 @@ EXTRACT_HELPER(IMM8, 11, 8);
}, \
.oname = stringify(name), \
}
+#define GEN_OPCODE4(name, onam, op1, op2, op3, op4, invl, _typ, _typ2) \
+{ \
+ .opc1 = op1, \
+ .opc2 = op2, \
+ .opc3 = op3, \
+ .opc4 = op4, \
+ .handler = { \
+ .inval1 = invl, \
+ .type = _typ, \
+ .type2 = _typ2, \
+ .handler = &gen_##name, \
+ }, \
+ .oname = onam, \
+}
#endif
/* SPR load/store helpers */
diff --git a/target-ppc/translate/vmx-impl.inc.c b/target-ppc/translate/vmx-impl.inc.c
index c8998f3eab..fc612d9f37 100644
--- a/target-ppc/translate/vmx-impl.inc.c
+++ b/target-ppc/translate/vmx-impl.inc.c
@@ -182,6 +182,52 @@ static void gen_mtvscr(DisasContext *ctx)
tcg_temp_free_ptr(p);
}
+#define GEN_VX_VMUL10(name, add_cin, ret_carry) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv_i64 t0 = tcg_temp_new_i64(); \
+ TCGv_i64 t1 = tcg_temp_new_i64(); \
+ TCGv_i64 t2 = tcg_temp_new_i64(); \
+ TCGv_i64 ten, z; \
+ \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ \
+ ten = tcg_const_i64(10); \
+ z = tcg_const_i64(0); \
+ \
+ if (add_cin) { \
+ tcg_gen_mulu2_i64(t0, t1, cpu_avrl[rA(ctx->opcode)], ten); \
+ tcg_gen_andi_i64(t2, cpu_avrl[rB(ctx->opcode)], 0xF); \
+ tcg_gen_add2_i64(cpu_avrl[rD(ctx->opcode)], t2, t0, t1, t2, z); \
+ } else { \
+ tcg_gen_mulu2_i64(cpu_avrl[rD(ctx->opcode)], t2, \
+ cpu_avrl[rA(ctx->opcode)], ten); \
+ } \
+ \
+ if (ret_carry) { \
+ tcg_gen_mulu2_i64(t0, t1, cpu_avrh[rA(ctx->opcode)], ten); \
+ tcg_gen_add2_i64(t0, cpu_avrl[rD(ctx->opcode)], t0, t1, t2, z); \
+ tcg_gen_movi_i64(cpu_avrh[rD(ctx->opcode)], 0); \
+ } else { \
+ tcg_gen_mul_i64(t0, cpu_avrh[rA(ctx->opcode)], ten); \
+ tcg_gen_add_i64(cpu_avrh[rD(ctx->opcode)], t0, t2); \
+ } \
+ \
+ tcg_temp_free_i64(t0); \
+ tcg_temp_free_i64(t1); \
+ tcg_temp_free_i64(t2); \
+ tcg_temp_free_i64(ten); \
+ tcg_temp_free_i64(z); \
+} \
+
+GEN_VX_VMUL10(vmul10uq, 0, 0);
+GEN_VX_VMUL10(vmul10euq, 1, 0);
+GEN_VX_VMUL10(vmul10cuq, 0, 1);
+GEN_VX_VMUL10(vmul10ecuq, 1, 1);
+
/* Logical operations */
#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \
static void glue(gen_, name)(DisasContext *ctx) \
@@ -276,8 +322,30 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
} \
}
+/* Adds support to provide invalid mask */
+#define GEN_VXFORM_DUAL_EXT(name0, flg0, flg2_0, inval0, \
+ name1, flg1, flg2_1, inval1) \
+static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
+{ \
+ if ((Rc(ctx->opcode) == 0) && \
+ ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0)) && \
+ !(ctx->opcode & inval0)) { \
+ gen_##name0(ctx); \
+ } else if ((Rc(ctx->opcode) == 1) && \
+ ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1)) && \
+ !(ctx->opcode & inval1)) { \
+ gen_##name1(ctx); \
+ } else { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ } \
+}
+
GEN_VXFORM(vaddubm, 0, 0);
+GEN_VXFORM_DUAL_EXT(vaddubm, PPC_ALTIVEC, PPC_NONE, 0, \
+ vmul10cuq, PPC_NONE, PPC2_ISA300, 0x0000F800)
GEN_VXFORM(vadduhm, 0, 1);
+GEN_VXFORM_DUAL(vadduhm, PPC_ALTIVEC, PPC_NONE, \
+ vmul10ecuq, PPC_NONE, PPC2_ISA300)
GEN_VXFORM(vadduwm, 0, 2);
GEN_VXFORM(vaddudm, 0, 3);
GEN_VXFORM(vsububm, 0, 16);
@@ -390,7 +458,11 @@ GEN_VXFORM(vsro, 6, 17);
GEN_VXFORM(vaddcuw, 0, 6);
GEN_VXFORM(vsubcuw, 0, 22);
GEN_VXFORM_ENV(vaddubs, 0, 8);
+GEN_VXFORM_DUAL_EXT(vaddubs, PPC_ALTIVEC, PPC_NONE, 0, \
+ vmul10uq, PPC_NONE, PPC2_ISA300, 0x0000F800)
GEN_VXFORM_ENV(vadduhs, 0, 9);
+GEN_VXFORM_DUAL(vadduhs, PPC_ALTIVEC, PPC_NONE, \
+ vmul10euq, PPC_NONE, PPC2_ISA300)
GEN_VXFORM_ENV(vadduws, 0, 10);
GEN_VXFORM_ENV(vaddsbs, 0, 12);
GEN_VXFORM_ENV(vaddshs, 0, 13);
@@ -815,6 +887,8 @@ GEN_VXFORM_NOA(vclzb, 1, 28)
GEN_VXFORM_NOA(vclzh, 1, 29)
GEN_VXFORM_NOA(vclzw, 1, 30)
GEN_VXFORM_NOA(vclzd, 1, 31)
+GEN_VXFORM_NOA_2(vnegw, 1, 24, 6)
+GEN_VXFORM_NOA_2(vnegd, 1, 24, 7)
GEN_VXFORM_NOA_2(vextsb2w, 1, 24, 16)
GEN_VXFORM_NOA_2(vextsh2w, 1, 24, 17)
GEN_VXFORM_NOA_2(vextsb2d, 1, 24, 24)
diff --git a/target-ppc/translate/vmx-ops.inc.c b/target-ppc/translate/vmx-ops.inc.c
index 68cba3e474..cc7ed7eeed 100644
--- a/target-ppc/translate/vmx-ops.inc.c
+++ b/target-ppc/translate/vmx-ops.inc.c
@@ -55,8 +55,8 @@ GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, type0, type1)
GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, tp0, tp1), \
GEN_HANDLER_E(name0##_##name1, 0x4, opc2, (opc3 | 0x10), 0x00000000, tp0, tp1),
-GEN_VXFORM(vaddubm, 0, 0),
-GEN_VXFORM(vadduhm, 0, 1),
+GEN_VXFORM_DUAL(vaddubm, vmul10cuq, 0, 0, PPC_ALTIVEC, PPC_NONE),
+GEN_VXFORM_DUAL(vadduhm, vmul10ecuq, 0, 1, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM(vadduwm, 0, 2),
GEN_VXFORM_207(vaddudm, 0, 3),
GEN_VXFORM_DUAL(vsububm, bcdadd, 0, 16, PPC_ALTIVEC, PPC_NONE),
@@ -123,8 +123,8 @@ GEN_VXFORM(vslo, 6, 16),
GEN_VXFORM(vsro, 6, 17),
GEN_VXFORM(vaddcuw, 0, 6),
GEN_VXFORM(vsubcuw, 0, 22),
-GEN_VXFORM(vaddubs, 0, 8),
-GEN_VXFORM(vadduhs, 0, 9),
+GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE),
+GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE),
GEN_VXFORM(vadduws, 0, 10),
GEN_VXFORM(vaddsbs, 0, 12),
GEN_VXFORM(vaddshs, 0, 13),
@@ -215,6 +215,8 @@ GEN_VXFORM_DUAL_INV(vspltish, vinserth, 6, 13, 0x00000000, 0x100000,
GEN_VXFORM_DUAL_INV(vspltisw, vinsertw, 6, 14, 0x00000000, 0x100000,
PPC_ALTIVEC),
GEN_VXFORM_300_EXT(vinsertd, 6, 15, 0x100000),
+GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06),
+GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07),
GEN_VXFORM_300_EO(vextsb2w, 0x01, 0x18, 0x10),
GEN_VXFORM_300_EO(vextsh2w, 0x01, 0x18, 0x11),
GEN_VXFORM_300_EO(vextsb2d, 0x01, 0x18, 0x18),
diff --git a/target-ppc/translate/vsx-impl.inc.c b/target-ppc/translate/vsx-impl.inc.c
index 23ec1e115c..5a27be4bd4 100644
--- a/target-ppc/translate/vsx-impl.inc.c
+++ b/target-ppc/translate/vsx-impl.inc.c
@@ -132,6 +132,22 @@ static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl,
tcg_temp_free_i64(mask);
}
+static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl,
+ TCGv_i64 inh, TCGv_i64 inl)
+{
+ TCGv_i64 hi = tcg_temp_new_i64();
+ TCGv_i64 lo = tcg_temp_new_i64();
+
+ tcg_gen_bswap64_i64(hi, inh);
+ tcg_gen_bswap64_i64(lo, inl);
+ tcg_gen_shri_i64(outh, hi, 32);
+ tcg_gen_deposit_i64(outh, outh, hi, 32, 32);
+ tcg_gen_shri_i64(outl, lo, 32);
+ tcg_gen_deposit_i64(outl, outl, lo, 32, 32);
+
+ tcg_temp_free_i64(hi);
+ tcg_temp_free_i64(lo);
+}
static void gen_lxvh8x(DisasContext *ctx)
{
TCGv EA;
@@ -604,6 +620,10 @@ GEN_VSX_HELPER_2(xsnmaddadp, 0x04, 0x14, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xsnmaddmdp, 0x04, 0x15, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xsnmsubadp, 0x04, 0x16, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xsnmsubmdp, 0x04, 0x17, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xscmpeqdp, 0x0C, 0x00, 0, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscmpgtdp, 0x0C, 0x01, 0, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscmpgedp, 0x0C, 0x02, 0, PPC2_ISA300)
+GEN_VSX_HELPER_2(xscmpnedp, 0x0C, 0x03, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX)
@@ -665,6 +685,7 @@ GEN_VSX_HELPER_2(xvmindp, 0x00, 0x1D, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xvcmpnedp, 0x0C, 0x0F, 0, PPC2_ISA300)
GEN_VSX_HELPER_2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX)
@@ -702,6 +723,7 @@ GEN_VSX_HELPER_2(xvminsp, 0x00, 0x19, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX)
+GEN_VSX_HELPER_2(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcvspsxds, 0x10, 0x19, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvcvspsxws, 0x10, 0x09, 0, PPC2_VSX)
@@ -717,6 +739,67 @@ GEN_VSX_HELPER_2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX)
GEN_VSX_HELPER_2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX)
+static void gen_xxbrd(DisasContext *ctx)
+{
+ TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+ TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+ TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+ TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ tcg_gen_bswap64_i64(xth, xbh);
+ tcg_gen_bswap64_i64(xtl, xbl);
+}
+
+static void gen_xxbrh(DisasContext *ctx)
+{
+ TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+ TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+ TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+ TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_bswap16x8(xth, xtl, xbh, xbl);
+}
+
+static void gen_xxbrq(DisasContext *ctx)
+{
+ TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+ TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+ TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+ TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+ TCGv_i64 t0 = tcg_temp_new_i64();
+
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ tcg_gen_bswap64_i64(t0, xbl);
+ tcg_gen_bswap64_i64(xtl, xbh);
+ tcg_gen_mov_i64(xth, t0);
+ tcg_temp_free_i64(t0);
+}
+
+static void gen_xxbrw(DisasContext *ctx)
+{
+ TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode));
+ TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode));
+ TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode));
+ TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode));
+
+ if (unlikely(!ctx->vsx_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VSXU);
+ return;
+ }
+ gen_bswap32x4(xth, xtl, xbh, xbl);
+}
+
#define VSX_LOGICAL(name, tcg_op) \
static void glue(gen_, name)(DisasContext * ctx) \
{ \
diff --git a/target-ppc/translate/vsx-ops.inc.c b/target-ppc/translate/vsx-ops.inc.c
index 10eb4b9470..3d9104155a 100644
--- a/target-ppc/translate/vsx-ops.inc.c
+++ b/target-ppc/translate/vsx-ops.inc.c
@@ -39,6 +39,10 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2)
GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \
GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2)
+#define GEN_XX2FORM_EO(name, opc2, opc3, opc4, fl2) \
+GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 0, opc3, opc4, 0, PPC_NONE, fl2), \
+GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 1, opc3, opc4, 0, PPC_NONE, fl2)
+
#define GEN_XX3FORM(name, opc2, opc3, fl2) \
GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \
GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2), \
@@ -110,6 +114,10 @@ GEN_XX3FORM(xsnmaddadp, 0x04, 0x14, PPC2_VSX),
GEN_XX3FORM(xsnmaddmdp, 0x04, 0x15, PPC2_VSX),
GEN_XX3FORM(xsnmsubadp, 0x04, 0x16, PPC2_VSX),
GEN_XX3FORM(xsnmsubmdp, 0x04, 0x17, PPC2_VSX),
+GEN_XX3FORM(xscmpeqdp, 0x0C, 0x00, PPC2_ISA300),
+GEN_XX3FORM(xscmpgtdp, 0x0C, 0x01, PPC2_ISA300),
+GEN_XX3FORM(xscmpgedp, 0x0C, 0x02, PPC2_ISA300),
+GEN_XX3FORM(xscmpnedp, 0x0C, 0x03, PPC2_ISA300),
GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX),
GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX),
GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX),
@@ -171,6 +179,7 @@ GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX),
GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX),
GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX),
GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX),
+GEN_XX3_RC_FORM(xvcmpnedp, 0x0C, 0x0F, PPC2_ISA300),
GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX),
GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX),
GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX),
@@ -208,6 +217,7 @@ GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX),
GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX),
GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX),
GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX),
+GEN_XX3_RC_FORM(xvcmpnesp, 0x0C, 0x0B, PPC2_ISA300),
GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX),
GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX),
GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX),
@@ -222,6 +232,10 @@ GEN_XX2FORM(xvrspic, 0x16, 0x0A, PPC2_VSX),
GEN_XX2FORM(xvrspim, 0x12, 0x0B, PPC2_VSX),
GEN_XX2FORM(xvrspip, 0x12, 0x0A, PPC2_VSX),
GEN_XX2FORM(xvrspiz, 0x12, 0x09, PPC2_VSX),
+GEN_XX2FORM_EO(xxbrh, 0x16, 0x1D, 0x07, PPC2_ISA300),
+GEN_XX2FORM_EO(xxbrw, 0x16, 0x1D, 0x0F, PPC2_ISA300),
+GEN_XX2FORM_EO(xxbrd, 0x16, 0x1D, 0x17, PPC2_ISA300),
+GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300),
#define VSX_LOGICAL(name, opc2, opc3, fl2) \
GEN_XX3FORM(name, opc2, opc3, fl2)
diff --git a/tests/.gitignore b/tests/.gitignore
index 64e050e859..c0d7857538 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -67,7 +67,6 @@ test-qmp-marshal.c
test-qobject-output-visitor
test-rcu-list
test-replication
-test-rfifolock
test-string-input-visitor
test-string-output-visitor
test-thread-pool
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 22656eaf26..de516341fd 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -45,7 +45,6 @@ check-unit-y += tests/test-visitor-serialization$(EXESUF)
check-unit-y += tests/test-iov$(EXESUF)
gcov-files-test-iov-y = util/iov.c
check-unit-y += tests/test-aio$(EXESUF)
-check-unit-$(CONFIG_POSIX) += tests/test-rfifolock$(EXESUF)
check-unit-y += tests/test-throttle$(EXESUF)
gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c
gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c
@@ -282,6 +281,7 @@ check-qtest-ppc64-y += tests/usb-hcd-uhci-test$(EXESUF)
gcov-files-ppc64-y += hw/usb/hcd-uhci.c
check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF)
gcov-files-ppc64-y += hw/usb/hcd-xhci.c
+check-qtest-ppc64-y += $(check-qtest-virtio-y)
check-qtest-sh4-y = tests/endianness-test$(EXESUF)
@@ -490,7 +490,6 @@ tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y)
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
-tests/test-rfifolock$(EXESUF): tests/test-rfifolock.o $(test-util-obj-y)
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
@@ -615,7 +614,7 @@ libqos-pc-obj-y += tests/libqos/ahci.o
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o
libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o
-libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
+libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
diff --git a/tests/ahci-test.c b/tests/ahci-test.c
index 9c0adce220..70bcafa9e4 100644
--- a/tests/ahci-test.c
+++ b/tests/ahci-test.c
@@ -78,25 +78,23 @@ static void string_bswap16(uint16_t *s, size_t bytes)
/**
* Verify that the transfer did not corrupt our state at all.
*/
-static void verify_state(AHCIQState *ahci)
+static void verify_state(AHCIQState *ahci, uint64_t hba_old)
{
int i, j;
uint32_t ahci_fingerprint;
uint64_t hba_base;
- uint64_t hba_stored;
AHCICommandHeader cmd;
ahci_fingerprint = qpci_config_readl(ahci->dev, PCI_VENDOR_ID);
g_assert_cmphex(ahci_fingerprint, ==, ahci->fingerprint);
/* If we haven't initialized, this is as much as can be validated. */
- if (!ahci->hba_base) {
+ if (!ahci->enabled) {
return;
}
hba_base = (uint64_t)qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5);
- hba_stored = (uint64_t)(uintptr_t)ahci->hba_base;
- g_assert_cmphex(hba_base, ==, hba_stored);
+ g_assert_cmphex(hba_base, ==, hba_old);
g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP), ==, ahci->cap);
g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP2), ==, ahci->cap2);
@@ -119,12 +117,15 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri)
QOSState *tmp = to->parent;
QPCIDevice *dev = to->dev;
char *uri_local = NULL;
+ uint64_t hba_old;
if (uri == NULL) {
uri_local = g_strdup_printf("%s%s", "unix:", mig_socket);
uri = uri_local;
}
+ hba_old = (uint64_t)qpci_config_readl(from->dev, PCI_BASE_ADDRESS_5);
+
/* context will be 'to' after completion. */
migrate(from->parent, to->parent, uri);
@@ -141,7 +142,7 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri)
from->parent = tmp;
from->dev = dev;
- verify_state(to);
+ verify_state(to, hba_old);
g_free(uri_local);
}
diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c
index 3979b20bb0..8c42ca919f 100644
--- a/tests/e1000e-test.c
+++ b/tests/e1000e-test.c
@@ -87,7 +87,7 @@
typedef struct e1000e_device {
QPCIDevice *pci_dev;
- void *mac_regs;
+ QPCIBar mac_regs;
uint64_t tx_ring;
uint64_t rx_ring;
@@ -119,12 +119,12 @@ static QPCIDevice *e1000e_device_find(QPCIBus *bus)
static void e1000e_macreg_write(e1000e_device *d, uint32_t reg, uint32_t val)
{
- qpci_io_writel(d->pci_dev, d->mac_regs + reg, val);
+ qpci_io_writel(d->pci_dev, d->mac_regs, reg, val);
}
static uint32_t e1000e_macreg_read(e1000e_device *d, uint32_t reg)
{
- return qpci_io_readl(d->pci_dev, d->mac_regs + reg);
+ return qpci_io_readl(d->pci_dev, d->mac_regs, reg);
}
static void e1000e_device_init(QPCIBus *bus, e1000e_device *d)
@@ -138,7 +138,6 @@ static void e1000e_device_init(QPCIBus *bus, e1000e_device *d)
/* Map BAR0 (mac registers) */
d->mac_regs = qpci_iomap(d->pci_dev, 0, NULL);
- g_assert_nonnull(d->mac_regs);
/* Reset the device */
val = e1000e_macreg_read(d, E1000E_CTRL);
diff --git a/tests/ide-test.c b/tests/ide-test.c
index a8a4081f78..67c7df0c8d 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -137,7 +137,7 @@ static void ide_test_quit(void)
qtest_end();
}
-static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
+static QPCIDevice *get_pci_device(QPCIBar *bmdma_bar, QPCIBar *ide_bar)
{
QPCIDevice *dev;
uint16_t vendor_id, device_id;
@@ -156,7 +156,9 @@ static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
/* Map bmdma BAR */
- *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4, NULL);
+ *bmdma_bar = qpci_iomap(dev, 4, NULL);
+
+ *ide_bar = qpci_legacy_iomap(dev, IDE_BASE);
qpci_device_enable(dev);
@@ -179,17 +181,18 @@ typedef struct PrdtEntry {
static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
PrdtEntry *prdt, int prdt_entries,
- void(*post_exec)(uint64_t sector, int nb_sectors))
+ void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar,
+ uint64_t sector, int nb_sectors))
{
QPCIDevice *dev;
- uint16_t bmdma_base;
+ QPCIBar bmdma_bar, ide_bar;
uintptr_t guest_prdt;
size_t len;
bool from_dev;
uint8_t status;
int flags;
- dev = get_pci_device(&bmdma_base);
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
flags = cmd & ~0xff;
cmd &= 0xff;
@@ -214,59 +217,60 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
}
/* Select device 0 */
- outb(IDE_BASE + reg_device, 0 | LBA);
+ qpci_io_writeb(dev, ide_bar, reg_device, 0 | LBA);
/* Stop any running transfer, clear any pending interrupt */
- outb(bmdma_base + bmreg_cmd, 0);
- outb(bmdma_base + bmreg_status, BM_STS_INTR);
+ qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0);
+ qpci_io_writeb(dev, bmdma_bar, bmreg_status, BM_STS_INTR);
/* Setup PRDT */
len = sizeof(*prdt) * prdt_entries;
guest_prdt = guest_alloc(guest_malloc, len);
memwrite(guest_prdt, prdt, len);
- outl(bmdma_base + bmreg_prdt, guest_prdt);
+ qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt);
/* ATA DMA command */
if (cmd == CMD_PACKET) {
/* Enables ATAPI DMA; otherwise PIO is attempted */
- outb(IDE_BASE + reg_feature, 0x01);
+ qpci_io_writeb(dev, ide_bar, reg_feature, 0x01);
} else {
- outb(IDE_BASE + reg_nsectors, nb_sectors);
- outb(IDE_BASE + reg_lba_low, sector & 0xff);
- outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
- outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff);
+ qpci_io_writeb(dev, ide_bar, reg_nsectors, nb_sectors);
+ qpci_io_writeb(dev, ide_bar, reg_lba_low, sector & 0xff);
+ qpci_io_writeb(dev, ide_bar, reg_lba_middle, (sector >> 8) & 0xff);
+ qpci_io_writeb(dev, ide_bar, reg_lba_high, (sector >> 16) & 0xff);
}
- outb(IDE_BASE + reg_command, cmd);
+ qpci_io_writeb(dev, ide_bar, reg_command, cmd);
if (post_exec) {
- post_exec(sector, nb_sectors);
+ post_exec(dev, ide_bar, sector, nb_sectors);
}
/* Start DMA transfer */
- outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
+ qpci_io_writeb(dev, bmdma_bar, bmreg_cmd,
+ BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
if (flags & CMDF_ABORT) {
- outb(bmdma_base + bmreg_cmd, 0);
+ qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0);
}
/* Wait for the DMA transfer to complete */
do {
- status = inb(bmdma_base + bmreg_status);
+ status = qpci_io_readb(dev, bmdma_bar, bmreg_status);
} while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR));
/* Check IDE status code */
- assert_bit_set(inb(IDE_BASE + reg_status), DRDY);
- assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ);
+ assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ);
/* Reading the status register clears the IRQ */
g_assert(!get_irq(IDE_PRIMARY_IRQ));
/* Stop DMA transfer if still active */
if (status & BM_STS_ACTIVE) {
- outb(bmdma_base + bmreg_cmd, 0);
+ qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0);
}
free_pci_device(dev);
@@ -276,6 +280,8 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
static void test_bmdma_simple_rw(void)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t status;
uint8_t *buf;
uint8_t *cmpbuf;
@@ -289,6 +295,8 @@ static void test_bmdma_simple_rw(void)
},
};
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
buf = g_malloc(len);
cmpbuf = g_malloc(len);
@@ -299,7 +307,7 @@ static void test_bmdma_simple_rw(void)
status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt,
ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, BM_STS_INTR);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
/* Write 0xaa pattern to sector 1 */
memset(buf, 0xaa, len);
@@ -308,14 +316,14 @@ static void test_bmdma_simple_rw(void)
status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt,
ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, BM_STS_INTR);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
/* Read and verify 0x55 pattern in sector 0 */
memset(cmpbuf, 0x55, len);
status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, BM_STS_INTR);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
memread(guest_buf, buf, len);
g_assert(memcmp(buf, cmpbuf, len) == 0);
@@ -325,7 +333,7 @@ static void test_bmdma_simple_rw(void)
status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, BM_STS_INTR);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
memread(guest_buf, buf, len);
g_assert(memcmp(buf, cmpbuf, len) == 0);
@@ -337,6 +345,8 @@ static void test_bmdma_simple_rw(void)
static void test_bmdma_short_prdt(void)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t status;
PrdtEntry prdt[] = {
@@ -346,21 +356,25 @@ static void test_bmdma_short_prdt(void)
},
};
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
/* Normal request */
status = send_dma_request(CMD_READ_DMA, 0, 1,
prdt, ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, 0);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
/* Abort the request before it completes */
status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
prdt, ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, 0);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
}
static void test_bmdma_one_sector_short_prdt(void)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t status;
/* Read 2 sectors but only give 1 sector in PRDT */
@@ -371,21 +385,25 @@ static void test_bmdma_one_sector_short_prdt(void)
},
};
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
/* Normal request */
status = send_dma_request(CMD_READ_DMA, 0, 2,
prdt, ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, 0);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
/* Abort the request before it completes */
status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 2,
prdt, ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, 0);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
}
static void test_bmdma_long_prdt(void)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t status;
PrdtEntry prdt[] = {
@@ -395,23 +413,29 @@ static void test_bmdma_long_prdt(void)
},
};
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
/* Normal request */
status = send_dma_request(CMD_READ_DMA, 0, 1,
prdt, ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
/* Abort the request before it completes */
status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
prdt, ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, BM_STS_INTR);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
}
static void test_bmdma_no_busmaster(void)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t status;
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
/* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be
* able to access it anyway because the Bus Master bit in the PCI command
* register isn't set. This is complete nonsense, but it used to be pretty
@@ -424,7 +448,7 @@ static void test_bmdma_no_busmaster(void)
/* Not entirely clear what the expected result is, but this is what we get
* in practice. At least we want to be aware of any changes. */
g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
}
static void test_bmdma_setup(void)
@@ -454,6 +478,8 @@ static void string_cpu_to_be16(uint16_t *s, size_t bytes)
static void test_identify(void)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t data;
uint16_t buf[256];
int i;
@@ -464,23 +490,25 @@ static void test_identify(void)
"-global ide-hd.ver=%s",
tmp_path, "testdisk", "version");
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
/* IDENTIFY command on device 0*/
- outb(IDE_BASE + reg_device, 0);
- outb(IDE_BASE + reg_command, CMD_IDENTIFY);
+ qpci_io_writeb(dev, ide_bar, reg_device, 0);
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY);
/* Read in the IDENTIFY buffer and check registers */
- data = inb(IDE_BASE + reg_device);
+ data = qpci_io_readb(dev, ide_bar, reg_device);
g_assert_cmpint(data & DEV, ==, 0);
for (i = 0; i < 256; i++) {
- data = inb(IDE_BASE + reg_status);
+ data = qpci_io_readb(dev, ide_bar, reg_status);
assert_bit_set(data, DRDY | DRQ);
assert_bit_clear(data, BSY | DF | ERR);
- ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data);
+ buf[i] = qpci_io_readw(dev, ide_bar, reg_data);
}
- data = inb(IDE_BASE + reg_status);
+ data = qpci_io_readb(dev, ide_bar, reg_status);
assert_bit_set(data, DRDY);
assert_bit_clear(data, BSY | DF | ERR | DRQ);
@@ -505,11 +533,15 @@ static void test_identify(void)
*/
static void make_dirty(uint8_t device)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t status;
size_t len = 512;
uintptr_t guest_buf;
void* buf;
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
guest_buf = guest_alloc(guest_malloc, len);
buf = g_malloc(len);
g_assert(guest_buf);
@@ -527,19 +559,23 @@ static void make_dirty(uint8_t device)
status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt,
ARRAY_SIZE(prdt), NULL);
g_assert_cmphex(status, ==, BM_STS_INTR);
- assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+ assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR);
g_free(buf);
}
static void test_flush(void)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t data;
ide_test_start(
"-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw",
tmp_path);
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
qtest_irq_intercept_in(global_qtest, "ioapic");
/* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */
@@ -549,11 +585,11 @@ static void test_flush(void)
g_free(hmp("qemu-io ide0-hd0 \"break flush_to_os A\""));
/* FLUSH CACHE command on device 0*/
- outb(IDE_BASE + reg_device, 0);
- outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+ qpci_io_writeb(dev, ide_bar, reg_device, 0);
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE);
/* Check status while request is in flight*/
- data = inb(IDE_BASE + reg_status);
+ data = qpci_io_readb(dev, ide_bar, reg_status);
assert_bit_set(data, BSY | DRDY);
assert_bit_clear(data, DF | ERR | DRQ);
@@ -561,11 +597,11 @@ static void test_flush(void)
g_free(hmp("qemu-io ide0-hd0 \"resume A\""));
/* Check registers */
- data = inb(IDE_BASE + reg_device);
+ data = qpci_io_readb(dev, ide_bar, reg_device);
g_assert_cmpint(data & DEV, ==, 0);
do {
- data = inb(IDE_BASE + reg_status);
+ data = qpci_io_readb(dev, ide_bar, reg_status);
} while (data & BSY);
assert_bit_set(data, DRDY);
@@ -576,6 +612,8 @@ static void test_flush(void)
static void test_retry_flush(const char *machine)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t data;
const char *s;
@@ -587,17 +625,19 @@ static void test_retry_flush(const char *machine)
"rerror=stop,werror=stop",
debug_path, tmp_path);
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
qtest_irq_intercept_in(global_qtest, "ioapic");
/* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */
make_dirty(0);
/* FLUSH CACHE command on device 0*/
- outb(IDE_BASE + reg_device, 0);
- outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+ qpci_io_writeb(dev, ide_bar, reg_device, 0);
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE);
/* Check status while request is in flight*/
- data = inb(IDE_BASE + reg_status);
+ data = qpci_io_readb(dev, ide_bar, reg_status);
assert_bit_set(data, BSY | DRDY);
assert_bit_clear(data, DF | ERR | DRQ);
@@ -608,11 +648,11 @@ static void test_retry_flush(const char *machine)
qmp_discard_response(s);
/* Check registers */
- data = inb(IDE_BASE + reg_device);
+ data = qpci_io_readb(dev, ide_bar, reg_device);
g_assert_cmpint(data & DEV, ==, 0);
do {
- data = inb(IDE_BASE + reg_status);
+ data = qpci_io_readb(dev, ide_bar, reg_status);
} while (data & BSY);
assert_bit_set(data, DRDY);
@@ -623,11 +663,16 @@ static void test_retry_flush(const char *machine)
static void test_flush_nodev(void)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
+
ide_test_start("");
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
/* FLUSH CACHE command on device 0*/
- outb(IDE_BASE + reg_device, 0);
- outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+ qpci_io_writeb(dev, ide_bar, reg_device, 0);
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE);
/* Just testing that qemu doesn't crash... */
@@ -654,7 +699,8 @@ typedef struct Read10CDB {
uint16_t padding;
} __attribute__((__packed__)) Read10CDB;
-static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
+static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar,
+ uint64_t lba, int nblocks)
{
Read10CDB pkt = { .padding = 0 };
int i;
@@ -670,7 +716,8 @@ static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
/* Send Packet */
for (i = 0; i < sizeof(Read10CDB)/2; i++) {
- outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i]));
+ qpci_io_writew(dev, ide_bar, reg_data,
+ le16_to_cpu(((uint16_t *)&pkt)[i]));
}
}
@@ -683,13 +730,17 @@ static void nsleep(int64_t nsecs)
static uint8_t ide_wait_clear(uint8_t flag)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
uint8_t data;
time_t st;
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
+
/* Wait with a 5 second timeout */
time(&st);
while (true) {
- data = inb(IDE_BASE + reg_status);
+ data = qpci_io_readb(dev, ide_bar, reg_status);
if (!(data & flag)) {
return data;
}
@@ -723,6 +774,8 @@ static void ide_wait_intr(int irq)
static void cdrom_pio_impl(int nblocks)
{
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
FILE *fh;
int patt_blocks = MAX(16, nblocks);
size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks;
@@ -741,13 +794,14 @@ static void cdrom_pio_impl(int nblocks)
ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 "
"-device ide-cd,drive=sr0,bus=ide.0", tmp_path);
+ dev = get_pci_device(&bmdma_bar, &ide_bar);
qtest_irq_intercept_in(global_qtest, "ioapic");
/* PACKET command on device 0 */
- outb(IDE_BASE + reg_device, 0);
- outb(IDE_BASE + reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF);
- outb(IDE_BASE + reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF));
- outb(IDE_BASE + reg_command, CMD_PACKET);
+ qpci_io_writeb(dev, ide_bar, reg_device, 0);
+ qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF);
+ qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF));
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET);
/* HP0: Check_Status_A State */
nsleep(400);
data = ide_wait_clear(BSY);
@@ -756,7 +810,7 @@ static void cdrom_pio_impl(int nblocks)
assert_bit_clear(data, ERR | DF | BSY);
/* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */
- send_scsi_cdb_read10(0, nblocks);
+ send_scsi_cdb_read10(dev, ide_bar, 0, nblocks);
/* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes.
* If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes.
@@ -780,7 +834,8 @@ static void cdrom_pio_impl(int nblocks)
/* HP4: Transfer_Data */
for (j = 0; j < MIN((limit / 2), rem); j++) {
- rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data));
+ rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar,
+ reg_data));
}
}
diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c
index f36bfe7d0a..04a5c5dc7d 100644
--- a/tests/ivshmem-test.c
+++ b/tests/ivshmem-test.c
@@ -41,7 +41,7 @@ static QPCIDevice *get_device(QPCIBus *pcibus)
typedef struct _IVState {
QTestState *qtest;
- void *reg_base, *mem_base;
+ QPCIBar reg_bar, mem_bar;
QPCIBus *pcibus;
QPCIDevice *dev;
} IVState;
@@ -75,7 +75,7 @@ static inline unsigned in_reg(IVState *s, enum Reg reg)
unsigned res;
global_qtest = s->qtest;
- res = qpci_io_readl(s->dev, s->reg_base + reg);
+ res = qpci_io_readl(s->dev, s->reg_bar, reg);
g_test_message("*%s -> %x\n", name, res);
global_qtest = qtest;
@@ -89,7 +89,26 @@ static inline void out_reg(IVState *s, enum Reg reg, unsigned v)
global_qtest = s->qtest;
g_test_message("%x -> *%s\n", v, name);
- qpci_io_writel(s->dev, s->reg_base + reg, v);
+ qpci_io_writel(s->dev, s->reg_bar, reg, v);
+ global_qtest = qtest;
+}
+
+static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len)
+{
+ QTestState *qtest = global_qtest;
+
+ global_qtest = s->qtest;
+ qpci_memread(s->dev, s->mem_bar, off, buf, len);
+ global_qtest = qtest;
+}
+
+static inline void write_mem(IVState *s, uint64_t off,
+ const void *buf, size_t len)
+{
+ QTestState *qtest = global_qtest;
+
+ global_qtest = s->qtest;
+ qpci_memwrite(s->dev, s->mem_bar, off, buf, len);
global_qtest = qtest;
}
@@ -108,16 +127,14 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix)
s->pcibus = qpci_init_pc(NULL);
s->dev = get_device(s->pcibus);
- s->reg_base = qpci_iomap(s->dev, 0, &barsize);
- g_assert_nonnull(s->reg_base);
+ s->reg_bar = qpci_iomap(s->dev, 0, &barsize);
g_assert_cmpuint(barsize, ==, 256);
if (msix) {
qpci_msix_enable(s->dev);
}
- s->mem_base = qpci_iomap(s->dev, 2, &barsize);
- g_assert_nonnull(s->mem_base);
+ s->mem_bar = qpci_iomap(s->dev, 2, &barsize);
g_assert_cmpuint(barsize, ==, TMPSHMSIZE);
qpci_device_enable(s->dev);
@@ -169,7 +186,7 @@ static void test_ivshmem_single(void)
for (i = 0; i < G_N_ELEMENTS(data); i++) {
data[i] = i;
}
- qtest_memwrite(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data));
+ write_mem(s, 0, data, sizeof(data));
/* verify write */
for (i = 0; i < G_N_ELEMENTS(data); i++) {
@@ -178,7 +195,7 @@ static void test_ivshmem_single(void)
/* read it back and verify read */
memset(data, 0, sizeof(data));
- qtest_memread(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data));
+ read_mem(s, 0, data, sizeof(data));
for (i = 0; i < G_N_ELEMENTS(data); i++) {
g_assert_cmpuint(data[i], ==, i);
}
@@ -201,29 +218,29 @@ static void test_ivshmem_pair(void)
/* host write, guest 1 & 2 read */
memset(tmpshmem, 0x42, TMPSHMSIZE);
- qtest_memread(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE);
+ read_mem(s1, 0, data, TMPSHMSIZE);
for (i = 0; i < TMPSHMSIZE; i++) {
g_assert_cmpuint(data[i], ==, 0x42);
}
- qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE);
+ read_mem(s2, 0, data, TMPSHMSIZE);
for (i = 0; i < TMPSHMSIZE; i++) {
g_assert_cmpuint(data[i], ==, 0x42);
}
/* guest 1 write, guest 2 read */
memset(data, 0x43, TMPSHMSIZE);
- qtest_memwrite(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE);
+ write_mem(s1, 0, data, TMPSHMSIZE);
memset(data, 0, TMPSHMSIZE);
- qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE);
+ read_mem(s2, 0, data, TMPSHMSIZE);
for (i = 0; i < TMPSHMSIZE; i++) {
g_assert_cmpuint(data[i], ==, 0x43);
}
/* guest 2 write, guest 1 read */
memset(data, 0x44, TMPSHMSIZE);
- qtest_memwrite(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE);
+ write_mem(s2, 0, data, TMPSHMSIZE);
memset(data, 0, TMPSHMSIZE);
- qtest_memread(s1->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE);
+ read_mem(s1, 0, data, TMPSHMSIZE);
for (i = 0; i < TMPSHMSIZE; i++) {
g_assert_cmpuint(data[i], ==, 0x44);
}
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c
index 716ab7939e..5180d65279 100644
--- a/tests/libqos/ahci.c
+++ b/tests/libqos/ahci.c
@@ -210,8 +210,7 @@ void ahci_pci_enable(AHCIQState *ahci)
void start_ahci_device(AHCIQState *ahci)
{
/* Map AHCI's ABAR (BAR5) */
- ahci->hba_base = qpci_iomap(ahci->dev, 5, &ahci->barsize);
- g_assert(ahci->hba_base);
+ ahci->hba_bar = qpci_iomap(ahci->dev, 5, &ahci->barsize);
/* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */
qpci_device_enable(ahci->dev);
@@ -351,6 +350,7 @@ void ahci_hba_enable(AHCIQState *ahci)
reg = ahci_rreg(ahci, AHCI_GHC);
ASSERT_BIT_SET(reg, AHCI_GHC_IE);
+ ahci->enabled = true;
/* TODO: The device should now be idling and waiting for commands.
* In the future, a small test-case to inspect the Register D2H FIS
* and clear the initial interrupts might be good. */
diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h
index c69fb5ae90..caaafe3fdf 100644
--- a/tests/libqos/ahci.h
+++ b/tests/libqos/ahci.h
@@ -321,12 +321,13 @@ typedef struct AHCIPortQState {
typedef struct AHCIQState {
QOSState *parent;
QPCIDevice *dev;
- void *hba_base;
+ QPCIBar hba_bar;
uint64_t barsize;
uint32_t fingerprint;
uint32_t cap;
uint32_t cap2;
AHCIPortQState port[32];
+ bool enabled;
} AHCIQState;
/**
@@ -488,12 +489,12 @@ typedef struct AHCIOpts {
static inline uint32_t ahci_mread(AHCIQState *ahci, size_t offset)
{
- return qpci_io_readl(ahci->dev, ahci->hba_base + offset);
+ return qpci_io_readl(ahci->dev, ahci->hba_bar, offset);
}
static inline void ahci_mwrite(AHCIQState *ahci, size_t offset, uint32_t value)
{
- qpci_io_writel(ahci->dev, ahci->hba_base + offset, value);
+ qpci_io_writel(ahci->dev, ahci->hba_bar, offset, value);
}
static inline uint32_t ahci_rreg(AHCIQState *ahci, uint32_t reg_num)
diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c
index 7abb48254e..6226546c28 100644
--- a/tests/libqos/libqos.c
+++ b/tests/libqos/libqos.c
@@ -10,6 +10,8 @@
/**
* Launch QEMU with the given command line,
* and then set up interrupts and our guest malloc interface.
+ * Never returns NULL:
+ * Terminates the application in case an error is encountered.
*/
QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap)
{
diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c
index 9600ed6e41..ded1c54c06 100644
--- a/tests/libqos/pci-pc.c
+++ b/tests/libqos/pci-pc.c
@@ -17,7 +17,6 @@
#include "hw/pci/pci_regs.h"
#include "qemu-common.h"
-#include "qemu/host-utils.h"
#define ACPI_PCIHP_ADDR 0xae00
@@ -26,89 +25,58 @@
typedef struct QPCIBusPC
{
QPCIBus bus;
-
- uint32_t pci_hole_start;
- uint32_t pci_hole_size;
- uint32_t pci_hole_alloc;
-
- uint16_t pci_iohole_start;
- uint16_t pci_iohole_size;
- uint16_t pci_iohole_alloc;
} QPCIBusPC;
-static uint8_t qpci_pc_io_readb(QPCIBus *bus, void *addr)
+static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr)
{
- uintptr_t port = (uintptr_t)addr;
- uint8_t value;
-
- if (port < 0x10000) {
- value = inb(port);
- } else {
- value = readb(port);
- }
-
- return value;
+ return inb(addr);
}
-static uint16_t qpci_pc_io_readw(QPCIBus *bus, void *addr)
+static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val)
{
- uintptr_t port = (uintptr_t)addr;
- uint16_t value;
-
- if (port < 0x10000) {
- value = inw(port);
- } else {
- value = readw(port);
- }
-
- return value;
+ outb(addr, val);
}
-static uint32_t qpci_pc_io_readl(QPCIBus *bus, void *addr)
+static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr)
{
- uintptr_t port = (uintptr_t)addr;
- uint32_t value;
-
- if (port < 0x10000) {
- value = inl(port);
- } else {
- value = readl(port);
- }
+ return inw(addr);
+}
- return value;
+static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val)
+{
+ outw(addr, val);
}
-static void qpci_pc_io_writeb(QPCIBus *bus, void *addr, uint8_t value)
+static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr)
{
- uintptr_t port = (uintptr_t)addr;
+ return inl(addr);
+}
- if (port < 0x10000) {
- outb(port, value);
- } else {
- writeb(port, value);
- }
+static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val)
+{
+ outl(addr, val);
}
-static void qpci_pc_io_writew(QPCIBus *bus, void *addr, uint16_t value)
+static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr)
{
- uintptr_t port = (uintptr_t)addr;
+ return (uint64_t)inl(addr) + ((uint64_t)inl(addr + 4) << 32);
+}
- if (port < 0x10000) {
- outw(port, value);
- } else {
- writew(port, value);
- }
+static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val)
+{
+ outl(addr, val & 0xffffffff);
+ outl(addr + 4, val >> 32);
}
-static void qpci_pc_io_writel(QPCIBus *bus, void *addr, uint32_t value)
+static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len)
{
- uintptr_t port = (uintptr_t)addr;
+ memread(addr, buf, len);
+}
- if (port < 0x10000) {
- outl(port, value);
- } else {
- writel(port, value);
- }
+static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr,
+ const void *buf, size_t len)
+{
+ memwrite(addr, buf, len);
}
static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
@@ -147,84 +115,24 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3
outl(0xcfc, value);
}
-static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr)
-{
- QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
- static const int bar_reg_map[] = {
- PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
- PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
- };
- int bar_reg;
- uint32_t addr;
- uint64_t size;
- uint32_t io_type;
-
- g_assert(barno >= 0 && barno <= 5);
- bar_reg = bar_reg_map[barno];
-
- qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
- addr = qpci_config_readl(dev, bar_reg);
-
- io_type = addr & PCI_BASE_ADDRESS_SPACE;
- if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
- addr &= PCI_BASE_ADDRESS_IO_MASK;
- } else {
- addr &= PCI_BASE_ADDRESS_MEM_MASK;
- }
-
- size = (1ULL << ctzl(addr));
- if (size == 0) {
- return NULL;
- }
- if (sizeptr) {
- *sizeptr = size;
- }
-
- if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
- uint16_t loc;
-
- g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size
- <= s->pci_iohole_size);
- s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size);
- loc = s->pci_iohole_start + s->pci_iohole_alloc;
- s->pci_iohole_alloc += size;
-
- qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
-
- return (void *)(intptr_t)loc;
- } else {
- uint64_t loc;
-
- g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size
- <= s->pci_hole_size);
- s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size);
- loc = s->pci_hole_start + s->pci_hole_alloc;
- s->pci_hole_alloc += size;
-
- qpci_config_writel(dev, bar_reg, loc);
-
- return (void *)(intptr_t)loc;
- }
-}
-
-static void qpci_pc_iounmap(QPCIBus *bus, void *data)
-{
- /* FIXME */
-}
-
QPCIBus *qpci_init_pc(QGuestAllocator *alloc)
{
QPCIBusPC *ret;
ret = g_malloc(sizeof(*ret));
- ret->bus.io_readb = qpci_pc_io_readb;
- ret->bus.io_readw = qpci_pc_io_readw;
- ret->bus.io_readl = qpci_pc_io_readl;
+ ret->bus.pio_readb = qpci_pc_pio_readb;
+ ret->bus.pio_readw = qpci_pc_pio_readw;
+ ret->bus.pio_readl = qpci_pc_pio_readl;
+ ret->bus.pio_readq = qpci_pc_pio_readq;
- ret->bus.io_writeb = qpci_pc_io_writeb;
- ret->bus.io_writew = qpci_pc_io_writew;
- ret->bus.io_writel = qpci_pc_io_writel;
+ ret->bus.pio_writeb = qpci_pc_pio_writeb;
+ ret->bus.pio_writew = qpci_pc_pio_writew;
+ ret->bus.pio_writel = qpci_pc_pio_writel;
+ ret->bus.pio_writeq = qpci_pc_pio_writeq;
+
+ ret->bus.memread = qpci_pc_memread;
+ ret->bus.memwrite = qpci_pc_memwrite;
ret->bus.config_readb = qpci_pc_config_readb;
ret->bus.config_readw = qpci_pc_config_readw;
@@ -234,16 +142,9 @@ QPCIBus *qpci_init_pc(QGuestAllocator *alloc)
ret->bus.config_writew = qpci_pc_config_writew;
ret->bus.config_writel = qpci_pc_config_writel;
- ret->bus.iomap = qpci_pc_iomap;
- ret->bus.iounmap = qpci_pc_iounmap;
-
- ret->pci_hole_start = 0xE0000000;
- ret->pci_hole_size = 0x20000000;
- ret->pci_hole_alloc = 0;
-
- ret->pci_iohole_start = 0xc000;
- ret->pci_iohole_size = 0x4000;
- ret->pci_iohole_alloc = 0;
+ ret->bus.pio_alloc_ptr = 0xc000;
+ ret->bus.mmio_alloc_ptr = 0xE0000000;
+ ret->bus.mmio_limit = 0x100000000ULL;
return &ret->bus;
}
diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c
index 2eaaf9159a..1e5d015bd4 100644
--- a/tests/libqos/pci-spapr.c
+++ b/tests/libqos/pci-spapr.c
@@ -34,14 +34,6 @@ typedef struct QPCIBusSPAPR {
uint64_t mmio32_cpu_base;
QPCIWindow mmio32;
-
- uint64_t pci_hole_start;
- uint64_t pci_hole_size;
- uint64_t pci_hole_alloc;
-
- uint32_t pci_iohole_start;
- uint32_t pci_iohole_size;
- uint32_t pci_iohole_alloc;
} QPCIBusSPAPR;
/*
@@ -50,78 +42,66 @@ typedef struct QPCIBusSPAPR {
* so PCI accessors need to swap data endianness
*/
-static uint8_t qpci_spapr_io_readb(QPCIBus *bus, void *addr)
+static uint8_t qpci_spapr_pio_readb(QPCIBus *bus, uint32_t addr)
+{
+ QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+ return readb(s->pio_cpu_base + addr);
+}
+
+static void qpci_spapr_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val)
+{
+ QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+ writeb(s->pio_cpu_base + addr, val);
+}
+
+static uint16_t qpci_spapr_pio_readw(QPCIBus *bus, uint32_t addr)
+{
+ QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+ return bswap16(readw(s->pio_cpu_base + addr));
+}
+
+static void qpci_spapr_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val)
{
QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
- uint64_t port = (uintptr_t)addr;
- uint8_t v;
- if (port < s->pio.size) {
- v = readb(s->pio_cpu_base + port);
- } else {
- v = readb(s->mmio32_cpu_base + port);
- }
- return v;
+ writew(s->pio_cpu_base + addr, bswap16(val));
}
-static uint16_t qpci_spapr_io_readw(QPCIBus *bus, void *addr)
+static uint32_t qpci_spapr_pio_readl(QPCIBus *bus, uint32_t addr)
{
QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
- uint64_t port = (uintptr_t)addr;
- uint16_t v;
- if (port < s->pio.size) {
- v = readw(s->pio_cpu_base + port);
- } else {
- v = readw(s->mmio32_cpu_base + port);
- }
- return bswap16(v);
+ return bswap32(readl(s->pio_cpu_base + addr));
}
-static uint32_t qpci_spapr_io_readl(QPCIBus *bus, void *addr)
+static void qpci_spapr_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val)
{
QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
- uint64_t port = (uintptr_t)addr;
- uint32_t v;
- if (port < s->pio.size) {
- v = readl(s->pio_cpu_base + port);
- } else {
- v = readl(s->mmio32_cpu_base + port);
- }
- return bswap32(v);
+ writel(s->pio_cpu_base + addr, bswap32(val));
}
-static void qpci_spapr_io_writeb(QPCIBus *bus, void *addr, uint8_t value)
+static uint64_t qpci_spapr_pio_readq(QPCIBus *bus, uint32_t addr)
{
QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
- uint64_t port = (uintptr_t)addr;
- if (port < s->pio.size) {
- writeb(s->pio_cpu_base + port, value);
- } else {
- writeb(s->mmio32_cpu_base + port, value);
- }
+ return bswap64(readq(s->pio_cpu_base + addr));
}
-static void qpci_spapr_io_writew(QPCIBus *bus, void *addr, uint16_t value)
+static void qpci_spapr_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val)
{
QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
- uint64_t port = (uintptr_t)addr;
- value = bswap16(value);
- if (port < s->pio.size) {
- writew(s->pio_cpu_base + port, value);
- } else {
- writew(s->mmio32_cpu_base + port, value);
- }
+ writeq(s->pio_cpu_base + addr, bswap64(val));
}
-static void qpci_spapr_io_writel(QPCIBus *bus, void *addr, uint32_t value)
+static void qpci_spapr_memread(QPCIBus *bus, uint32_t addr,
+ void *buf, size_t len)
{
QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
- uint64_t port = (uintptr_t)addr;
- value = bswap32(value);
- if (port < s->pio.size) {
- writel(s->pio_cpu_base + port, value);
- } else {
- writel(s->mmio32_cpu_base + port, value);
- }
+ memread(s->mmio32_cpu_base + addr, buf, len);
+}
+
+static void qpci_spapr_memwrite(QPCIBus *bus, uint32_t addr,
+ const void *buf, size_t len)
+{
+ QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+ memwrite(s->mmio32_cpu_base + addr, buf, len);
}
static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
@@ -169,72 +149,6 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset,
qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 4, value);
}
-static void *qpci_spapr_iomap(QPCIBus *bus, QPCIDevice *dev, int barno,
- uint64_t *sizeptr)
-{
- QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
- static const int bar_reg_map[] = {
- PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
- PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
- };
- int bar_reg;
- uint32_t addr;
- uint64_t size;
- uint32_t io_type;
-
- g_assert(barno >= 0 && barno <= 5);
- bar_reg = bar_reg_map[barno];
-
- qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
- addr = qpci_config_readl(dev, bar_reg);
-
- io_type = addr & PCI_BASE_ADDRESS_SPACE;
- if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
- addr &= PCI_BASE_ADDRESS_IO_MASK;
- } else {
- addr &= PCI_BASE_ADDRESS_MEM_MASK;
- }
-
- size = (1ULL << ctzl(addr));
- if (size == 0) {
- return NULL;
- }
- if (sizeptr) {
- *sizeptr = size;
- }
-
- if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
- uint16_t loc;
-
- g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size
- <= s->pci_iohole_size);
- s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size);
- loc = s->pci_iohole_start + s->pci_iohole_alloc;
- s->pci_iohole_alloc += size;
-
- qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
-
- return (void *)(unsigned long)loc;
- } else {
- uint64_t loc;
-
- g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size
- <= s->pci_hole_size);
- s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size);
- loc = s->pci_hole_start + s->pci_hole_alloc;
- s->pci_hole_alloc += size;
-
- qpci_config_writel(dev, bar_reg, loc);
-
- return (void *)(unsigned long)loc;
- }
-}
-
-static void qpci_spapr_iounmap(QPCIBus *bus, void *data)
-{
- /* FIXME */
-}
-
#define SPAPR_PCI_BASE (1ULL << 45)
#define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */
@@ -248,13 +162,18 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc)
ret->alloc = alloc;
- ret->bus.io_readb = qpci_spapr_io_readb;
- ret->bus.io_readw = qpci_spapr_io_readw;
- ret->bus.io_readl = qpci_spapr_io_readl;
+ ret->bus.pio_readb = qpci_spapr_pio_readb;
+ ret->bus.pio_readw = qpci_spapr_pio_readw;
+ ret->bus.pio_readl = qpci_spapr_pio_readl;
+ ret->bus.pio_readq = qpci_spapr_pio_readq;
- ret->bus.io_writeb = qpci_spapr_io_writeb;
- ret->bus.io_writew = qpci_spapr_io_writew;
- ret->bus.io_writel = qpci_spapr_io_writel;
+ ret->bus.pio_writeb = qpci_spapr_pio_writeb;
+ ret->bus.pio_writew = qpci_spapr_pio_writew;
+ ret->bus.pio_writel = qpci_spapr_pio_writel;
+ ret->bus.pio_writeq = qpci_spapr_pio_writeq;
+
+ ret->bus.memread = qpci_spapr_memread;
+ ret->bus.memwrite = qpci_spapr_memwrite;
ret->bus.config_readb = qpci_spapr_config_readb;
ret->bus.config_readw = qpci_spapr_config_readw;
@@ -264,9 +183,6 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc)
ret->bus.config_writew = qpci_spapr_config_writew;
ret->bus.config_writel = qpci_spapr_config_writel;
- ret->bus.iomap = qpci_spapr_iomap;
- ret->bus.iounmap = qpci_spapr_iounmap;
-
/* FIXME: We assume the default location of the PHB for now.
* Ideally we'd parse the device tree deposited in the guest to
* get the window locations */
@@ -281,15 +197,9 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc)
ret->mmio32.pci_base = 0x80000000; /* 2 GiB */
ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE;
- ret->pci_hole_start = 0xC0000000;
- ret->pci_hole_size =
- ret->mmio32.pci_base + ret->mmio32.size - ret->pci_hole_start;
- ret->pci_hole_alloc = 0;
-
- ret->pci_iohole_start = 0xc000;
- ret->pci_iohole_size =
- ret->pio.pci_base + ret->pio.size - ret->pci_iohole_start;
- ret->pci_iohole_alloc = 0;
+ ret->bus.pio_alloc_ptr = 0xc000;
+ ret->bus.mmio_alloc_ptr = ret->mmio32.pci_base;
+ ret->bus.mmio_limit = ret->mmio32.pci_base + ret->mmio32.size;
return &ret->bus;
}
diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c
index c3f3382b7c..2dcdeade2a 100644
--- a/tests/libqos/pci.c
+++ b/tests/libqos/pci.c
@@ -14,6 +14,7 @@
#include "libqos/pci.h"
#include "hw/pci/pci_regs.h"
+#include "qemu/host-utils.h"
void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
void (*func)(QPCIDevice *dev, int devfn, void *data),
@@ -103,7 +104,6 @@ void qpci_msix_enable(QPCIDevice *dev)
uint32_t table;
uint8_t bir_table;
uint8_t bir_pba;
- void *offset;
addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
g_assert_cmphex(addr, !=, 0);
@@ -113,18 +113,16 @@ void qpci_msix_enable(QPCIDevice *dev)
table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE);
bir_table = table & PCI_MSIX_FLAGS_BIRMASK;
- offset = qpci_iomap(dev, bir_table, NULL);
- dev->msix_table = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK);
+ dev->msix_table_bar = qpci_iomap(dev, bir_table, NULL);
+ dev->msix_table_off = table & ~PCI_MSIX_FLAGS_BIRMASK;
table = qpci_config_readl(dev, addr + PCI_MSIX_PBA);
bir_pba = table & PCI_MSIX_FLAGS_BIRMASK;
if (bir_pba != bir_table) {
- offset = qpci_iomap(dev, bir_pba, NULL);
+ dev->msix_pba_bar = qpci_iomap(dev, bir_pba, NULL);
}
- dev->msix_pba = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK);
+ dev->msix_pba_off = table & ~PCI_MSIX_FLAGS_BIRMASK;
- g_assert(dev->msix_table != NULL);
- g_assert(dev->msix_pba != NULL);
dev->msix_enabled = true;
}
@@ -140,22 +138,23 @@ void qpci_msix_disable(QPCIDevice *dev)
qpci_config_writew(dev, addr + PCI_MSIX_FLAGS,
val & ~PCI_MSIX_FLAGS_ENABLE);
- qpci_iounmap(dev, dev->msix_table);
- qpci_iounmap(dev, dev->msix_pba);
+ qpci_iounmap(dev, dev->msix_table_bar);
+ qpci_iounmap(dev, dev->msix_pba_bar);
dev->msix_enabled = 0;
- dev->msix_table = NULL;
- dev->msix_pba = NULL;
+ dev->msix_table_off = 0;
+ dev->msix_pba_off = 0;
}
bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry)
{
uint32_t pba_entry;
uint8_t bit_n = entry % 32;
- void *addr = dev->msix_pba + (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4;
+ uint64_t off = (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4;
g_assert(dev->msix_enabled);
- pba_entry = qpci_io_readl(dev, addr);
- qpci_io_writel(dev, addr, pba_entry & ~(1 << bit_n));
+ pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off);
+ qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off,
+ pba_entry & ~(1 << bit_n));
return (pba_entry & (1 << bit_n)) != 0;
}
@@ -163,7 +162,7 @@ bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry)
{
uint8_t addr;
uint16_t val;
- void *vector_addr = dev->msix_table + (entry * PCI_MSIX_ENTRY_SIZE);
+ uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE;
g_assert(dev->msix_enabled);
addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX);
@@ -173,8 +172,9 @@ bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry)
if (val & PCI_MSIX_FLAGS_MASKALL) {
return true;
} else {
- return (qpci_io_readl(dev, vector_addr + PCI_MSIX_ENTRY_VECTOR_CTRL)
- & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0;
+ return (qpci_io_readl(dev, dev->msix_table_bar,
+ vector_off + PCI_MSIX_ENTRY_VECTOR_CTRL)
+ & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0;
}
}
@@ -221,46 +221,174 @@ void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value)
dev->bus->config_writel(dev->bus, dev->devfn, offset, value);
}
+uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off)
+{
+ if (token.addr < QPCI_PIO_LIMIT) {
+ return dev->bus->pio_readb(dev->bus, token.addr + off);
+ } else {
+ uint8_t val;
+ dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
+ return val;
+ }
+}
+
+uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off)
+{
+ if (token.addr < QPCI_PIO_LIMIT) {
+ return dev->bus->pio_readw(dev->bus, token.addr + off);
+ } else {
+ uint16_t val;
+ dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
+ return le16_to_cpu(val);
+ }
+}
+
+uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off)
+{
+ if (token.addr < QPCI_PIO_LIMIT) {
+ return dev->bus->pio_readl(dev->bus, token.addr + off);
+ } else {
+ uint32_t val;
+ dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
+ return le32_to_cpu(val);
+ }
+}
-uint8_t qpci_io_readb(QPCIDevice *dev, void *data)
+uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off)
{
- return dev->bus->io_readb(dev->bus, data);
+ if (token.addr < QPCI_PIO_LIMIT) {
+ return dev->bus->pio_readq(dev->bus, token.addr + off);
+ } else {
+ uint64_t val;
+ dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
+ return le64_to_cpu(val);
+ }
}
-uint16_t qpci_io_readw(QPCIDevice *dev, void *data)
+void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ uint8_t value)
{
- return dev->bus->io_readw(dev->bus, data);
+ if (token.addr < QPCI_PIO_LIMIT) {
+ dev->bus->pio_writeb(dev->bus, token.addr + off, value);
+ } else {
+ dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
+ }
+}
+
+void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ uint16_t value)
+{
+ if (token.addr < QPCI_PIO_LIMIT) {
+ dev->bus->pio_writew(dev->bus, token.addr + off, value);
+ } else {
+ value = cpu_to_le16(value);
+ dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
+ }
}
-uint32_t qpci_io_readl(QPCIDevice *dev, void *data)
+void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ uint32_t value)
{
- return dev->bus->io_readl(dev->bus, data);
+ if (token.addr < QPCI_PIO_LIMIT) {
+ dev->bus->pio_writel(dev->bus, token.addr + off, value);
+ } else {
+ value = cpu_to_le32(value);
+ dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
+ }
}
+void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ uint64_t value)
+{
+ if (token.addr < QPCI_PIO_LIMIT) {
+ dev->bus->pio_writeq(dev->bus, token.addr + off, value);
+ } else {
+ value = cpu_to_le64(value);
+ dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
+ }
+}
-void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value)
+void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ void *buf, size_t len)
{
- dev->bus->io_writeb(dev->bus, data, value);
+ g_assert(token.addr >= QPCI_PIO_LIMIT);
+ dev->bus->memread(dev->bus, token.addr + off, buf, len);
}
-void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value)
+void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ const void *buf, size_t len)
{
- dev->bus->io_writew(dev->bus, data, value);
+ g_assert(token.addr >= QPCI_PIO_LIMIT);
+ dev->bus->memwrite(dev->bus, token.addr + off, buf, len);
}
-void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value)
+QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr)
{
- dev->bus->io_writel(dev->bus, data, value);
+ QPCIBus *bus = dev->bus;
+ static const int bar_reg_map[] = {
+ PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
+ PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
+ };
+ QPCIBar bar;
+ int bar_reg;
+ uint32_t addr, size;
+ uint32_t io_type;
+ uint64_t loc;
+
+ g_assert(barno >= 0 && barno <= 5);
+ bar_reg = bar_reg_map[barno];
+
+ qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
+ addr = qpci_config_readl(dev, bar_reg);
+
+ io_type = addr & PCI_BASE_ADDRESS_SPACE;
+ if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
+ addr &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ addr &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+
+ g_assert(addr); /* Must have *some* size bits */
+
+ size = 1U << ctz32(addr);
+ if (sizeptr) {
+ *sizeptr = size;
+ }
+
+ if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
+ loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size);
+
+ g_assert(loc >= bus->pio_alloc_ptr);
+ g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */
+
+ bus->pio_alloc_ptr = loc + size;
+
+ qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
+ } else {
+ loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size);
+
+ /* Check for space */
+ g_assert(loc >= bus->mmio_alloc_ptr);
+ g_assert(loc + size <= bus->mmio_limit);
+
+ bus->mmio_alloc_ptr = loc + size;
+
+ qpci_config_writel(dev, bar_reg, loc);
+ }
+
+ bar.addr = loc;
+ return bar;
}
-void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr)
+void qpci_iounmap(QPCIDevice *dev, QPCIBar bar)
{
- return dev->bus->iomap(dev->bus, dev, barno, sizeptr);
+ /* FIXME */
}
-void qpci_iounmap(QPCIDevice *dev, void *data)
+QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr)
{
- dev->bus->iounmap(dev->bus, data);
+ QPCIBar bar = { .addr = addr };
+ return bar;
}
void qpci_plug_device_test(const char *driver, const char *id,
diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h
index c06add8dbf..ed480614ff 100644
--- a/tests/libqos/pci.h
+++ b/tests/libqos/pci.h
@@ -15,20 +15,27 @@
#include "libqtest.h"
+#define QPCI_PIO_LIMIT 0x10000
+
#define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn))
typedef struct QPCIDevice QPCIDevice;
typedef struct QPCIBus QPCIBus;
+typedef struct QPCIBar QPCIBar;
-struct QPCIBus
-{
- uint8_t (*io_readb)(QPCIBus *bus, void *addr);
- uint16_t (*io_readw)(QPCIBus *bus, void *addr);
- uint32_t (*io_readl)(QPCIBus *bus, void *addr);
+struct QPCIBus {
+ uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr);
+ uint16_t (*pio_readw)(QPCIBus *bus, uint32_t addr);
+ uint32_t (*pio_readl)(QPCIBus *bus, uint32_t addr);
+ uint64_t (*pio_readq)(QPCIBus *bus, uint32_t addr);
- void (*io_writeb)(QPCIBus *bus, void *addr, uint8_t value);
- void (*io_writew)(QPCIBus *bus, void *addr, uint16_t value);
- void (*io_writel)(QPCIBus *bus, void *addr, uint32_t value);
+ void (*pio_writeb)(QPCIBus *bus, uint32_t addr, uint8_t value);
+ void (*pio_writew)(QPCIBus *bus, uint32_t addr, uint16_t value);
+ void (*pio_writel)(QPCIBus *bus, uint32_t addr, uint32_t value);
+ void (*pio_writeq)(QPCIBus *bus, uint32_t addr, uint64_t value);
+
+ void (*memread)(QPCIBus *bus, uint32_t addr, void *buf, size_t len);
+ void (*memwrite)(QPCIBus *bus, uint32_t addr, const void *buf, size_t len);
uint8_t (*config_readb)(QPCIBus *bus, int devfn, uint8_t offset);
uint16_t (*config_readw)(QPCIBus *bus, int devfn, uint8_t offset);
@@ -41,8 +48,12 @@ struct QPCIBus
void (*config_writel)(QPCIBus *bus, int devfn,
uint8_t offset, uint32_t value);
- void *(*iomap)(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr);
- void (*iounmap)(QPCIBus *bus, void *data);
+ uint16_t pio_alloc_ptr;
+ uint64_t mmio_alloc_ptr, mmio_limit;
+};
+
+struct QPCIBar {
+ uint64_t addr;
};
struct QPCIDevice
@@ -50,8 +61,8 @@ struct QPCIDevice
QPCIBus *bus;
int devfn;
bool msix_enabled;
- void *msix_table;
- void *msix_pba;
+ QPCIBar msix_table_bar, msix_pba_bar;
+ uint64_t msix_table_off, msix_pba_off;
};
void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
@@ -75,16 +86,27 @@ void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value);
void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value);
void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value);
-uint8_t qpci_io_readb(QPCIDevice *dev, void *data);
-uint16_t qpci_io_readw(QPCIDevice *dev, void *data);
-uint32_t qpci_io_readl(QPCIDevice *dev, void *data);
-
-void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value);
-void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value);
-void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value);
-
-void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr);
-void qpci_iounmap(QPCIDevice *dev, void *data);
+uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off);
+uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off);
+uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off);
+uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off);
+
+void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ uint8_t value);
+void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ uint16_t value);
+void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ uint32_t value);
+void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off,
+ uint64_t value);
+
+void qpci_memread(QPCIDevice *bus, QPCIBar token, uint64_t off,
+ void *buf, size_t len);
+void qpci_memwrite(QPCIDevice *bus, QPCIBar token, uint64_t off,
+ const void *buf, size_t len);
+QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr);
+void qpci_iounmap(QPCIDevice *dev, QPCIBar addr);
+QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr);
void qpci_plug_device_test(const char *driver, const char *id,
uint8_t slot, const char *opts);
diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c
index f794d92da5..72d7a961fe 100644
--- a/tests/libqos/usb.c
+++ b/tests/libqos/usb.c
@@ -21,14 +21,12 @@ void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar)
hc->dev = qpci_device_find(pcibus, devfn);
g_assert(hc->dev != NULL);
qpci_device_enable(hc->dev);
- hc->base = qpci_iomap(hc->dev, bar, NULL);
- g_assert(hc->base != NULL);
+ hc->bar = qpci_iomap(hc->dev, bar, NULL);
}
void uhci_port_test(struct qhc *hc, int port, uint16_t expect)
{
- void *addr = hc->base + 0x10 + 2 * port;
- uint16_t value = qpci_io_readw(hc->dev, addr);
+ uint16_t value = qpci_io_readw(hc->dev, hc->bar, 0x10 + 2 * port);
uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1);
g_assert((value & mask) == (expect & mask));
diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h
index 8fe56872b7..423dcfd82f 100644
--- a/tests/libqos/usb.h
+++ b/tests/libqos/usb.h
@@ -5,7 +5,7 @@
struct qhc {
QPCIDevice *dev;
- void *base;
+ QPCIBar bar;
};
void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc,
diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c
index 0cab38f296..7aa8383338 100644
--- a/tests/libqos/virtio-mmio.c
+++ b/tests/libqos/virtio-mmio.c
@@ -15,28 +15,28 @@
#include "libqos/malloc-generic.h"
#include "standard-headers/linux/virtio_ring.h"
-static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr)
+static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
- return readb(dev->addr + addr);
+ return readb(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
}
-static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr)
+static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
- return readw(dev->addr + addr);
+ return readw(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
}
-static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr)
+static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
- return readl(dev->addr + addr);
+ return readl(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
}
-static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr)
+static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off)
{
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
- return readq(dev->addr + addr);
+ return readq(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
}
static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d)
@@ -199,6 +199,7 @@ QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size)
dev->addr = addr;
dev->page_size = page_size;
dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID);
+ dev->vdev.bus = &qvirtio_mmio;
writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size);
diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c
index 6e005c1835..d4bf841f23 100644
--- a/tests/libqos/virtio-pci.c
+++ b/tests/libqos/virtio-pci.c
@@ -62,73 +62,87 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data)
*vpcidev = (QVirtioPCIDevice *)d;
}
-static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr)
+#define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled))
+
+static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr);
+ return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
}
-static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr)
+/* PCI is always read in little-endian order
+ * but virtio ( < 1.0) is in guest order
+ * so with a big-endian guest the order has been reversed,
+ * reverse it again
+ * virtio-1.0 is always little-endian, like PCI, but this
+ * case will be managed inside qvirtio_is_big_endian()
+ */
+
+static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr);
+ uint16_t value;
+
+ value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
+ if (qvirtio_is_big_endian(d)) {
+ value = bswap16(value);
+ }
+ return value;
}
-static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr)
+static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr);
+ uint32_t value;
+
+ value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
+ if (qvirtio_is_big_endian(d)) {
+ value = bswap32(value);
+ }
+ return value;
}
-static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr)
+static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- int i;
- uint64_t u64 = 0;
+ uint64_t val;
- if (target_big_endian()) {
- for (i = 0; i < 8; ++i) {
- u64 |= (uint64_t)qpci_io_readb(dev->pdev,
- (void *)(uintptr_t)addr + i) << (7 - i) * 8;
- }
- } else {
- for (i = 0; i < 8; ++i) {
- u64 |= (uint64_t)qpci_io_readb(dev->pdev,
- (void *)(uintptr_t)addr + i) << i * 8;
- }
+ val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
+ if (qvirtio_is_big_endian(d)) {
+ val = bswap64(val);
}
- return u64;
+ return val;
}
static uint32_t qvirtio_pci_get_features(QVirtioDevice *d)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_HOST_FEATURES);
+ return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES);
}
static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES, features);
+ qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features);
}
static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES);
+ return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES);
}
static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS);
+ return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS);
}
static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS, status);
+ qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status);
}
static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
@@ -152,7 +166,7 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
}
}
} else {
- return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 1;
+ return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1;
}
}
@@ -176,26 +190,26 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
}
}
} else {
- return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 2;
+ return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2;
}
}
static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_SEL, index);
+ qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index);
}
static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- return qpci_io_readw(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NUM);
+ return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM);
}
static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_PFN, pfn);
+ qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn);
}
static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d,
@@ -247,7 +261,7 @@ static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq,
static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
{
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
- qpci_io_writew(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NOTIFY, vq->index);
+ qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index);
}
const QVirtioBus qvirtio_pci = {
@@ -286,20 +300,20 @@ QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type)
QVirtioPCIDevice *dev = NULL;
qvirtio_pci_foreach(bus, device_type, qvirtio_pci_assign_device, &dev);
+ dev->vdev.bus = &qvirtio_pci;
+
return dev;
}
void qvirtio_pci_device_enable(QVirtioPCIDevice *d)
{
qpci_device_enable(d->pdev);
- d->addr = qpci_iomap(d->pdev, 0, NULL);
- g_assert(d->addr != NULL);
+ d->bar = qpci_iomap(d->pdev, 0, NULL);
}
void qvirtio_pci_device_disable(QVirtioPCIDevice *d)
{
- qpci_iounmap(d->pdev, d->addr);
- d->addr = NULL;
+ qpci_iounmap(d->pdev, d->bar);
}
void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
@@ -307,29 +321,33 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
{
uint16_t vector;
uint32_t control;
- void *addr;
+ uint64_t off;
g_assert(d->pdev->msix_enabled);
- addr = d->pdev->msix_table + (entry * 16);
+ off = d->pdev->msix_table_off + (entry * 16);
g_assert_cmpint(entry, >=, 0);
g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
vqpci->msix_entry = entry;
vqpci->msix_addr = guest_alloc(alloc, 4);
- qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR,
- vqpci->msix_addr & ~0UL);
- qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR,
- (vqpci->msix_addr >> 32) & ~0UL);
- qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, vqpci->msix_data);
-
- control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL);
- qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL,
- control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
+ qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL);
+ qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_UPPER_ADDR,
+ (vqpci->msix_addr >> 32) & ~0UL);
+ qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data);
+
+ control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_VECTOR_CTRL);
+ qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_VECTOR_CTRL,
+ control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index);
- qpci_io_writew(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR, entry);
- vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR);
+ qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry);
+ vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR);
g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
}
@@ -338,10 +356,10 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
{
uint16_t vector;
uint32_t control;
- void *addr;
+ uint64_t off;
g_assert(d->pdev->msix_enabled);
- addr = d->pdev->msix_table + (entry * 16);
+ off = d->pdev->msix_table_off + (entry * 16);
g_assert_cmpint(entry, >=, 0);
g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
@@ -350,17 +368,21 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
d->config_msix_data = 0x12345678;
d->config_msix_addr = guest_alloc(alloc, 4);
- qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR,
- d->config_msix_addr & ~0UL);
- qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR,
- (d->config_msix_addr >> 32) & ~0UL);
- qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, d->config_msix_data);
-
- control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL);
- qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL,
- control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
-
- qpci_io_writew(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR, entry);
- vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR);
+ qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL);
+ qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_UPPER_ADDR,
+ (d->config_msix_addr >> 32) & ~0UL);
+ qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_DATA, d->config_msix_data);
+
+ control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_VECTOR_CTRL);
+ qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+ off + PCI_MSIX_ENTRY_VECTOR_CTRL,
+ control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
+
+ qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry);
+ vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR);
g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
}
diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h
index efcac2d3de..38c54c63ea 100644
--- a/tests/libqos/virtio-pci.h
+++ b/tests/libqos/virtio-pci.h
@@ -16,7 +16,7 @@
typedef struct QVirtioPCIDevice {
QVirtioDevice vdev;
QPCIDevice *pdev;
- void *addr;
+ QPCIBar bar;
uint16_t config_msix_entry;
uint64_t config_msix_addr;
uint32_t config_msix_data;
diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c
index 105bccecaa..ec30cb99b2 100644
--- a/tests/libqos/virtio.c
+++ b/tests/libqos/virtio.c
@@ -13,45 +13,40 @@
#include "standard-headers/linux/virtio_config.h"
#include "standard-headers/linux/virtio_ring.h"
-uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
- uint64_t addr)
+uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr)
{
- return bus->config_readb(d, addr);
+ return d->bus->config_readb(d, addr);
}
-uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
- uint64_t addr)
+uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr)
{
- return bus->config_readw(d, addr);
+ return d->bus->config_readw(d, addr);
}
-uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
- uint64_t addr)
+uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr)
{
- return bus->config_readl(d, addr);
+ return d->bus->config_readl(d, addr);
}
-uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
- uint64_t addr)
+uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr)
{
- return bus->config_readq(d, addr);
+ return d->bus->config_readq(d, addr);
}
-uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d)
+uint32_t qvirtio_get_features(QVirtioDevice *d)
{
- return bus->get_features(d);
+ return d->bus->get_features(d);
}
-void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
- uint32_t features)
+void qvirtio_set_features(QVirtioDevice *d, uint32_t features)
{
- bus->set_features(d, features);
+ d->bus->set_features(d, features);
}
-QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
- QGuestAllocator *alloc, uint16_t index)
+QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
+ QGuestAllocator *alloc, uint16_t index)
{
- return bus->virtqueue_setup(d, alloc, index);
+ return d->bus->virtqueue_setup(d, alloc, index);
}
void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
@@ -60,40 +55,40 @@ void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
return bus->virtqueue_cleanup(vq, alloc);
}
-void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d)
+void qvirtio_reset(QVirtioDevice *d)
{
- bus->set_status(d, 0);
- g_assert_cmphex(bus->get_status(d), ==, 0);
+ d->bus->set_status(d, 0);
+ g_assert_cmphex(d->bus->get_status(d), ==, 0);
}
-void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d)
+void qvirtio_set_acknowledge(QVirtioDevice *d)
{
- bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE);
- g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE);
+ d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE);
+ g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE);
}
-void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d)
+void qvirtio_set_driver(QVirtioDevice *d)
{
- bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER);
- g_assert_cmphex(bus->get_status(d), ==,
+ d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER);
+ g_assert_cmphex(d->bus->get_status(d), ==,
VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
}
-void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d)
+void qvirtio_set_driver_ok(QVirtioDevice *d)
{
- bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK);
- g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK |
+ d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK);
+ g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK |
VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
}
-void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
+void qvirtio_wait_queue_isr(QVirtioDevice *d,
QVirtQueue *vq, gint64 timeout_us)
{
gint64 start_time = g_get_monotonic_time();
for (;;) {
clock_step(100);
- if (bus->get_queue_isr_status(d, vq)) {
+ if (d->bus->get_queue_isr_status(d, vq)) {
return;
}
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
@@ -105,8 +100,7 @@ void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
* The virtqueue interrupt must not be raised, making this useful for testing
* event_index functionality.
*/
-uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus,
- QVirtioDevice *d,
+uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d,
QVirtQueue *vq,
uint64_t addr,
gint64 timeout_us)
@@ -116,20 +110,19 @@ uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus,
while ((val = readb(addr)) == 0xff) {
clock_step(100);
- g_assert(!bus->get_queue_isr_status(d, vq));
+ g_assert(!d->bus->get_queue_isr_status(d, vq));
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
}
return val;
}
-void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d,
- gint64 timeout_us)
+void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us)
{
gint64 start_time = g_get_monotonic_time();
for (;;) {
clock_step(100);
- if (bus->get_config_isr_status(d)) {
+ if (d->bus->get_config_isr_status(d)) {
return;
}
g_assert(g_get_monotonic_time() - start_time <= timeout_us);
@@ -253,8 +246,7 @@ uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect)
return vq->free_head++; /* Return and increase, in this order */
}
-void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
- uint32_t free_head)
+void qvirtqueue_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head)
{
/* vq->avail->idx */
uint16_t idx = readw(vq->avail + 2);
@@ -276,7 +268,7 @@ void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
/* < 1 because we add elements to avail queue one by one */
if ((flags & VRING_USED_F_NO_NOTIFY) == 0 &&
(!vq->event || (uint16_t)(idx-avail_event) < 1)) {
- bus->virtqueue_kick(d, vq);
+ d->bus->virtqueue_kick(d, vq);
}
}
diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h
index 0250842bf2..3397a080e9 100644
--- a/tests/libqos/virtio.h
+++ b/tests/libqos/virtio.h
@@ -15,7 +15,10 @@
#define QVIRTIO_F_BAD_FEATURE 0x40000000
+typedef struct QVirtioBus QVirtioBus;
+
typedef struct QVirtioDevice {
+ const QVirtioBus *bus;
/* Device type */
uint16_t device_type;
} QVirtioDevice;
@@ -39,7 +42,7 @@ typedef struct QVRingIndirectDesc {
uint16_t elem;
} QVRingIndirectDesc;
-typedef struct QVirtioBus {
+struct QVirtioBus {
uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr);
uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr);
uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr);
@@ -84,7 +87,13 @@ typedef struct QVirtioBus {
/* Notify changes in virtqueue */
void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq);
-} QVirtioBus;
+};
+
+static inline bool qvirtio_is_big_endian(QVirtioDevice *d)
+{
+ /* FIXME: virtio 1.0 is always little-endian */
+ return qtest_big_endian(global_qtest);
+}
static inline uint32_t qvring_size(uint32_t num, uint32_t align)
{
@@ -93,34 +102,27 @@ static inline uint32_t qvring_size(uint32_t num, uint32_t align)
+ sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num;
}
-uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
- uint64_t addr);
-uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
- uint64_t addr);
-uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
- uint64_t addr);
-uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
- uint64_t addr);
-uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d);
-void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
- uint32_t features);
-
-void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d);
-void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d);
-void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d);
-void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d);
-
-void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d,
+uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr);
+uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr);
+uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr);
+uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr);
+uint32_t qvirtio_get_features(QVirtioDevice *d);
+void qvirtio_set_features(QVirtioDevice *d, uint32_t features);
+
+void qvirtio_reset(QVirtioDevice *d);
+void qvirtio_set_acknowledge(QVirtioDevice *d);
+void qvirtio_set_driver(QVirtioDevice *d);
+void qvirtio_set_driver_ok(QVirtioDevice *d);
+
+void qvirtio_wait_queue_isr(QVirtioDevice *d,
QVirtQueue *vq, gint64 timeout_us);
-uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus,
- QVirtioDevice *d,
+uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d,
QVirtQueue *vq,
uint64_t addr,
gint64 timeout_us);
-void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d,
- gint64 timeout_us);
-QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
- QGuestAllocator *alloc, uint16_t index);
+void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us);
+QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
+ QGuestAllocator *alloc, uint16_t index);
void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
QGuestAllocator *alloc);
@@ -132,8 +134,7 @@ void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data,
uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
bool next);
uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect);
-void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
- uint32_t free_head);
+void qvirtqueue_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head);
void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx);
#endif
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 4be1f77877..0224f06d65 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -881,16 +881,6 @@ static inline int64_t clock_set(int64_t val)
return qtest_clock_set(global_qtest, val);
}
-/**
- * target_big_endian:
- *
- * Returns: True if the architecture under test has a big endian configuration.
- */
-static inline bool target_big_endian(void)
-{
- return qtest_big_endian(global_qtest);
-}
-
QDict *qmp_fd_receive(int fd);
void qmp_fd_sendv(int fd, const char *fmt, va_list ap);
void qmp_fd_send(int fd, const char *fmt, ...);
diff --git a/tests/postcopy-test.c b/tests/postcopy-test.c
index 41ed1a976f..d6613c5fa4 100644
--- a/tests/postcopy-test.c
+++ b/tests/postcopy-test.c
@@ -18,7 +18,7 @@
#include "qemu/sockets.h"
#include "sysemu/char.h"
#include "sysemu/sysemu.h"
-#include "hw/nvram/openbios_firmware_abi.h"
+#include "hw/nvram/chrp_nvram.h"
#define MIN_NVRAM_SIZE 8192 /* from spapr_nvram.c */
@@ -137,15 +137,15 @@ static void init_bootfile_ppc(const char *bootpath)
{
FILE *bootfile;
char buf[MIN_NVRAM_SIZE];
- struct OpenBIOS_nvpart_v1 *header = (struct OpenBIOS_nvpart_v1 *)buf;
+ ChrpNvramPartHdr *header = (ChrpNvramPartHdr *)buf;
memset(buf, 0, MIN_NVRAM_SIZE);
/* Create a "common" partition in nvram to store boot-command property */
- header->signature = OPENBIOS_PART_SYSTEM;
+ header->signature = CHRP_NVPART_SYSTEM;
memcpy(header->name, "common", 6);
- OpenBIOS_finish_partition(header, MIN_NVRAM_SIZE);
+ chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE);
/* FW_MAX_SIZE is 4MB, but slof.bin is only 900KB,
* so let's modify memory between 1MB and 100MB
diff --git a/tests/prom-env-test.c b/tests/prom-env-test.c
index 7a628574c3..0ba6f48607 100644
--- a/tests/prom-env-test.c
+++ b/tests/prom-env-test.c
@@ -9,11 +9,12 @@
* This work is licensed under the terms of the GNU GPL, version 2
* or later. See the COPYING file in the top-level directory.
*
- * This test is used to check that some OpenBIOS machines can be started
- * successfully in TCG mode. To do this, we first put some Forth code into
- * the "boot-command" Open Firmware environment variable. This Forth code
- * writes a well-known magic value to a known location in memory. Then we
- * start the guest so that OpenBIOS can boot and finally run the Forth code.
+ * This test is used to check that some Open Firmware based machines (i.e.
+ * OpenBIOS or SLOF) can be started successfully in TCG mode. To do this, we
+ * first put some Forth code into the "boot-command" Open Firmware environment
+ * variable. This Forth code writes a well-known magic value to a known location
+ * in memory. Then we start the guest so that the firmware can boot and finally
+ * run the Forth code.
* The testing code here then can finally check whether the value has been
* successfully written into the guest memory.
*/
@@ -71,13 +72,16 @@ int main(int argc, char *argv[])
{
const char *sparc_machines[] = { "SPARCbook", "Voyager", "SS-20", NULL };
const char *sparc64_machines[] = { "sun4u", "sun4v", NULL };
- const char *mac_machines[] = { "mac99", "g3beige", NULL };
+ const char *ppc_machines[] = { "mac99", "g3beige", NULL };
+ const char *ppc64_machines[] = { "mac99", "g3beige", "pseries", NULL };
const char *arch = qtest_get_arch();
g_test_init(&argc, &argv, NULL);
- if (!strcmp(arch, "ppc") || !strcmp(arch, "ppc64")) {
- add_tests(mac_machines);
+ if (!strcmp(arch, "ppc")) {
+ add_tests(ppc_machines);
+ } else if (!strcmp(arch, "ppc64")) {
+ add_tests(ppc64_machines);
} else if (!strcmp(arch, "sparc")) {
add_tests(sparc_machines);
} else if (!strcmp(arch, "sparc64")) {
diff --git a/tests/rtas-test.c b/tests/rtas-test.c
index ba0867afbd..276c87ef84 100644
--- a/tests/rtas-test.c
+++ b/tests/rtas-test.c
@@ -14,7 +14,6 @@ static void test_rtas_get_time_of_day(void)
time_t t1, t2;
qs = qtest_spapr_boot("-machine pseries");
- g_assert(qs != NULL);
t1 = time(NULL);
ret = qrtas_get_time_of_day(qs->alloc, &tm, &ns);
diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c
index c2f601a380..7de7dc45ae 100644
--- a/tests/rtl8139-test.c
+++ b/tests/rtl8139-test.c
@@ -22,7 +22,7 @@ static void nop(void)
static QPCIBus *pcibus;
static QPCIDevice *dev;
-static void *dev_base;
+static QPCIBar dev_bar;
static void save_fn(QPCIDevice *dev, int devfn, void *data)
{
@@ -45,14 +45,14 @@ static QPCIDevice *get_device(void)
#define PORT(name, len, val) \
static unsigned __attribute__((unused)) in_##name(void) \
{ \
- unsigned res = qpci_io_read##len(dev, dev_base+(val)); \
+ unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \
g_test_message("*%s -> %x\n", #name, res); \
return res; \
} \
static void out_##name(unsigned v) \
{ \
g_test_message("%x -> *%s\n", v, #name); \
- qpci_io_write##len(dev, dev_base+(val), v); \
+ qpci_io_write##len(dev, dev_bar, (val), v); \
}
PORT(Timer, l, 0x48)
@@ -186,9 +186,7 @@ static void test_init(void)
dev = get_device();
- dev_base = qpci_iomap(dev, 0, &barsize);
-
- g_assert(dev_base != NULL);
+ dev_bar = qpci_iomap(dev, 0, &barsize);
qpci_device_enable(dev);
diff --git a/tests/tco-test.c b/tests/tco-test.c
index 0d201b1fcb..ef02ec5903 100644
--- a/tests/tco-test.c
+++ b/tests/tco-test.c
@@ -41,7 +41,7 @@ typedef struct {
const char *args;
bool noreboot;
QPCIDevice *dev;
- void *tco_io_base;
+ QPCIBar tco_io_bar;
} TestData;
static void test_init(TestData *d)
@@ -70,42 +70,42 @@ static void test_init(TestData *d)
/* set Root Complex BAR */
qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1);
- d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60);
+ d->tco_io_bar = qpci_legacy_iomap(d->dev, PM_IO_BASE_ADDR + 0x60);
}
static void stop_tco(const TestData *d)
{
uint32_t val;
- val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+ val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT);
val |= TCO_TMR_HLT;
- qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+ qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val);
}
static void start_tco(const TestData *d)
{
uint32_t val;
- val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT);
+ val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT);
val &= ~TCO_TMR_HLT;
- qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val);
+ qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val);
}
static void load_tco(const TestData *d)
{
- qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4);
+ qpci_io_writew(d->dev, d->tco_io_bar, TCO_RLD, 4);
}
static void set_tco_timeout(const TestData *d, uint16_t ticks)
{
- qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks);
+ qpci_io_writew(d->dev, d->tco_io_bar, TCO_TMR, ticks);
}
static void clear_tco_status(const TestData *d)
{
- qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008);
- qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002);
- qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004);
+ qpci_io_writew(d->dev, d->tco_io_bar, TCO1_STS, 0x0008);
+ qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0002);
+ qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0004);
}
static void reset_on_second_timeout(bool enable)
@@ -128,25 +128,25 @@ static void test_tco_defaults(void)
d.args = NULL;
d.noreboot = true;
test_init(&d);
- g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==,
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD), ==,
TCO_RLD_DEFAULT);
/* TCO_DAT_IN & TCO_DAT_OUT */
- g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==,
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_DAT_IN), ==,
(TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT);
/* TCO1_STS & TCO2_STS */
- g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==,
+ g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_STS), ==,
(TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT);
/* TCO1_CNT & TCO2_CNT */
- g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==,
+ g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_CNT), ==,
(TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT);
/* TCO_MESSAGE1 & TCO_MESSAGE2 */
- g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==,
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_MESSAGE1), ==,
(TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT);
- g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==,
+ g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, TCO_WDCNT), ==,
TCO_WDCNT_DEFAULT);
- g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==,
+ g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, SW_IRQ_GEN), ==,
SW_IRQ_GEN_DEFAULT);
- g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==,
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_TMR), ==,
TCO_TMR_DEFAULT);
qtest_end();
}
@@ -171,23 +171,23 @@ static void test_tco_timeout(void)
clock_step(ticks * TCO_TICK_NSEC);
/* test first timeout */
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
ret = val & TCO_TIMEOUT ? 1 : 0;
g_assert(ret == 1);
/* test clearing timeout bit */
val |= TCO_TIMEOUT;
- qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
ret = val & TCO_TIMEOUT ? 1 : 0;
g_assert(ret == 0);
/* test second timeout */
clock_step(ticks * TCO_TICK_NSEC);
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
ret = val & TCO_TIMEOUT ? 1 : 0;
g_assert(ret == 1);
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS);
ret = val & TCO_SECOND_TO_STS ? 1 : 0;
g_assert(ret == 1);
@@ -214,13 +214,13 @@ static void test_tco_max_timeout(void)
start_tco(&d);
clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC);
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD);
g_assert_cmpint(val & TCO_RLD_MASK, ==, 1);
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
ret = val & TCO_TIMEOUT ? 1 : 0;
g_assert(ret == 0);
clock_step(TCO_TICK_NSEC);
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
ret = val & TCO_TIMEOUT ? 1 : 0;
g_assert(ret == 1);
@@ -358,11 +358,11 @@ static void test_tco_ticks_counter(void)
start_tco(&d);
do {
- rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK;
+ rld = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD) & TCO_RLD_MASK;
g_assert_cmpint(rld, ==, ticks);
clock_step(TCO_TICK_NSEC);
ticks--;
- } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT));
+ } while (!(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS) & TCO_TIMEOUT));
stop_tco(&d);
qtest_end();
@@ -378,10 +378,10 @@ static void test_tco1_control_bits(void)
test_init(&d);
val = TCO_LOCK;
- qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
+ qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val);
val &= ~TCO_LOCK;
- qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val);
- g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==,
+ qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val);
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_CNT), ==,
TCO_LOCK);
qtest_end();
}
@@ -405,13 +405,13 @@ static void test_tco1_status_bits(void)
start_tco(&d);
clock_step(ticks * TCO_TICK_NSEC);
- qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0);
- qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0);
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS);
+ qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_IN, 0);
+ qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_OUT, 0);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS);
ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0;
g_assert(ret == 1);
- qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val);
- g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0);
+ qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val);
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS), ==, 0);
qtest_end();
}
@@ -434,11 +434,11 @@ static void test_tco2_status_bits(void)
start_tco(&d);
clock_step(ticks * TCO_TICK_NSEC * 2);
- val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS);
+ val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS);
ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0;
g_assert(ret == 1);
- qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val);
- g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0);
+ qpci_io_writew(d.dev, d.tco_io_bar, TCO2_STS, val);
+ g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS), ==, 0);
qtest_end();
}
diff --git a/tests/test-aio.c b/tests/test-aio.c
index 03aa846970..5be99f8287 100644
--- a/tests/test-aio.c
+++ b/tests/test-aio.c
@@ -100,6 +100,7 @@ static void event_ready_cb(EventNotifier *e)
typedef struct {
QemuMutex start_lock;
+ EventNotifier notifier;
bool thread_acquired;
} AcquireTestData;
@@ -111,6 +112,11 @@ static void *test_acquire_thread(void *opaque)
qemu_mutex_lock(&data->start_lock);
qemu_mutex_unlock(&data->start_lock);
+ /* event_notifier_set might be called either before or after
+ * the main thread's call to poll(). The test case's outcome
+ * should be the same in either case.
+ */
+ event_notifier_set(&data->notifier);
aio_context_acquire(ctx);
aio_context_release(ctx);
@@ -125,20 +131,19 @@ static void set_event_notifier(AioContext *ctx, EventNotifier *notifier,
aio_set_event_notifier(ctx, notifier, false, handler);
}
-static void dummy_notifier_read(EventNotifier *unused)
+static void dummy_notifier_read(EventNotifier *n)
{
- g_assert(false); /* should never be invoked */
+ event_notifier_test_and_clear(n);
}
static void test_acquire(void)
{
QemuThread thread;
- EventNotifier notifier;
AcquireTestData data;
/* Dummy event notifier ensures aio_poll() will block */
- event_notifier_init(&notifier, false);
- set_event_notifier(ctx, &notifier, dummy_notifier_read);
+ event_notifier_init(&data.notifier, false);
+ set_event_notifier(ctx, &data.notifier, dummy_notifier_read);
g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */
qemu_mutex_init(&data.start_lock);
@@ -152,12 +157,13 @@ static void test_acquire(void)
/* Block in aio_poll(), let other thread kick us and acquire context */
aio_context_acquire(ctx);
qemu_mutex_unlock(&data.start_lock); /* let the thread run */
- g_assert(!aio_poll(ctx, true));
+ g_assert(aio_poll(ctx, true));
+ g_assert(!data.thread_acquired);
aio_context_release(ctx);
qemu_thread_join(&thread);
- set_event_notifier(ctx, &notifier, NULL);
- event_notifier_cleanup(&notifier);
+ set_event_notifier(ctx, &data.notifier, NULL);
+ event_notifier_cleanup(&data.notifier);
g_assert(data.thread_acquired);
}
diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c
index f73e063d7d..aa88c3cf45 100644
--- a/tests/test-io-channel-socket.c
+++ b/tests/test-io-channel-socket.c
@@ -491,6 +491,37 @@ static void test_io_channel_unix_fd_pass(void)
}
g_free(fdrecv);
}
+
+static void test_io_channel_unix_listen_cleanup(void)
+{
+ QIOChannelSocket *ioc;
+ struct sockaddr_un un;
+ int sock;
+
+#define TEST_SOCKET "test-io-channel-socket.sock"
+
+ ioc = qio_channel_socket_new();
+
+ /* Manually bind ioc without calling the qio api to avoid setting
+ * the LISTEN feature */
+ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET);
+ unlink(TEST_SOCKET);
+ bind(sock, (struct sockaddr *)&un, sizeof(un));
+ ioc->fd = sock;
+ ioc->localAddrLen = sizeof(ioc->localAddr);
+ getsockname(sock, (struct sockaddr *)&ioc->localAddr,
+ &ioc->localAddrLen);
+
+ g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS));
+ object_unref(OBJECT(ioc));
+ g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS));
+
+ unlink(TEST_SOCKET);
+}
+
#endif /* _WIN32 */
@@ -562,6 +593,8 @@ int main(int argc, char **argv)
test_io_channel_unix_async);
g_test_add_func("/io/channel/socket/unix-fd-pass",
test_io_channel_unix_fd_pass);
+ g_test_add_func("/io/channel/socket/unix-listen-cleanup",
+ test_io_channel_unix_listen_cleanup);
#endif /* _WIN32 */
return g_test_run();
diff --git a/tests/test-rfifolock.c b/tests/test-rfifolock.c
deleted file mode 100644
index 471a81114d..0000000000
--- a/tests/test-rfifolock.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * RFifoLock tests
- *
- * Copyright Red Hat, Inc. 2013
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This work is licensed under the terms of the GNU LGPL, version 2 or later.
- * See the COPYING.LIB file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "qemu/rfifolock.h"
-
-static void test_nesting(void)
-{
- RFifoLock lock;
-
- /* Trivial test, ensure the lock is recursive */
- rfifolock_init(&lock, NULL, NULL);
- rfifolock_lock(&lock);
- rfifolock_lock(&lock);
- rfifolock_lock(&lock);
- rfifolock_unlock(&lock);
- rfifolock_unlock(&lock);
- rfifolock_unlock(&lock);
- rfifolock_destroy(&lock);
-}
-
-typedef struct {
- RFifoLock lock;
- int fd[2];
-} CallbackTestData;
-
-static void rfifolock_cb(void *opaque)
-{
- CallbackTestData *data = opaque;
- int ret;
- char c = 0;
-
- ret = write(data->fd[1], &c, sizeof(c));
- g_assert(ret == 1);
-}
-
-static void *callback_thread(void *opaque)
-{
- CallbackTestData *data = opaque;
-
- /* The other thread holds the lock so the contention callback will be
- * invoked...
- */
- rfifolock_lock(&data->lock);
- rfifolock_unlock(&data->lock);
- return NULL;
-}
-
-static void test_callback(void)
-{
- CallbackTestData data;
- QemuThread thread;
- int ret;
- char c;
-
- rfifolock_init(&data.lock, rfifolock_cb, &data);
- ret = qemu_pipe(data.fd);
- g_assert(ret == 0);
-
- /* Hold lock but allow the callback to kick us by writing to the pipe */
- rfifolock_lock(&data.lock);
- qemu_thread_create(&thread, "callback_thread",
- callback_thread, &data, QEMU_THREAD_JOINABLE);
- ret = read(data.fd[0], &c, sizeof(c));
- g_assert(ret == 1);
- rfifolock_unlock(&data.lock);
- /* If we got here then the callback was invoked, as expected */
-
- qemu_thread_join(&thread);
- close(data.fd[0]);
- close(data.fd[1]);
- rfifolock_destroy(&data.lock);
-}
-
-int main(int argc, char **argv)
-{
- g_test_init(&argc, &argv, NULL);
- g_test_add_func("/nesting", test_nesting);
- g_test_add_func("/callback", test_callback);
- return g_test_run();
-}
diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c
index a4ceeaaa43..57af8a034e 100644
--- a/tests/usb-hcd-ehci-test.c
+++ b/tests/usb-hcd-ehci-test.c
@@ -38,8 +38,7 @@ static void uhci_port_update(struct qhc *hc, int port,
static void ehci_port_test(struct qhc *hc, int port, uint32_t expect)
{
- void *addr = hc->base + 0x64 + 4 * port;
- uint32_t value = qpci_io_readl(hc->dev, addr);
+ uint32_t value = qpci_io_readl(hc->dev, hc->bar, 0x64 + 4 * port);
uint16_t mask = ~(PORTSC_CSC | PORTSC_PEDC | PORTSC_OCC);
#if 0
@@ -91,7 +90,7 @@ static void pci_ehci_port_1(void)
static void pci_ehci_config(void)
{
/* hands over all ports from companion uhci to ehci */
- qpci_io_writew(ehci1.dev, ehci1.base + 0x60, 1);
+ qpci_io_writew(ehci1.dev, ehci1.bar, 0x60, 1);
}
static void pci_uhci_port_2(void)
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index a7f06291cb..e4b2900898 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -172,15 +172,15 @@ static void init_virtio_dev(TestServer *s)
g_assert_nonnull(dev);
qvirtio_pci_device_enable(dev);
- qvirtio_reset(&qvirtio_pci, &dev->vdev);
- qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev);
- qvirtio_set_driver(&qvirtio_pci, &dev->vdev);
+ qvirtio_reset(&dev->vdev);
+ qvirtio_set_acknowledge(&dev->vdev);
+ qvirtio_set_driver(&dev->vdev);
- features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
+ features = qvirtio_get_features(&dev->vdev);
features = features & VIRTIO_NET_F_MAC;
- qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
+ qvirtio_set_features(&dev->vdev, features);
- qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+ qvirtio_set_driver_ok(&dev->vdev);
}
static void wait_for_fds(TestServer *s)
@@ -847,24 +847,24 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot)
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET);
qvirtio_pci_device_enable(dev);
- qvirtio_reset(&qvirtio_pci, &dev->vdev);
- qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev);
- qvirtio_set_driver(&qvirtio_pci, &dev->vdev);
+ qvirtio_reset(&dev->vdev);
+ qvirtio_set_acknowledge(&dev->vdev);
+ qvirtio_set_driver(&dev->vdev);
return dev;
}
-static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev)
+static void driver_init(QVirtioDevice *dev)
{
uint32_t features;
- features = qvirtio_get_features(bus, dev);
+ features = qvirtio_get_features(dev);
features = features & ~(QVIRTIO_F_BAD_FEATURE |
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
(1u << VIRTIO_RING_F_EVENT_IDX));
- qvirtio_set_features(bus, dev, features);
+ qvirtio_set_features(dev, features);
- qvirtio_set_driver_ok(bus, dev);
+ qvirtio_set_driver_ok(dev);
}
#define PCI_SLOT 0x04
@@ -896,16 +896,15 @@ static void test_multiqueue(void)
alloc = pc_alloc_init();
for (i = 0; i < queues * 2; i++) {
- vq[i] = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
- alloc, i);
+ vq[i] = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, alloc, i);
}
- driver_init(&qvirtio_pci, &dev->vdev);
+ driver_init(&dev->vdev);
wait_for_rings_started(s, queues * 2);
/* End test */
for (i = 0; i < queues * 2; i++) {
- qvirtqueue_cleanup(&qvirtio_pci, &vq[i]->vq, alloc);
+ qvirtqueue_cleanup(dev->vdev.bus, &vq[i]->vq, alloc);
}
pc_alloc_uninit(alloc);
qvirtio_pci_device_disable(dev);
diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c
index e8b21967d8..9c4f6cb406 100644
--- a/tests/virtio-9p-test.c
+++ b/tests/virtio-9p-test.c
@@ -10,112 +10,111 @@
#include "qemu/osdep.h"
#include "libqtest.h"
#include "qemu-common.h"
-#include "libqos/pci-pc.h"
+#include "libqos/libqos-pc.h"
+#include "libqos/libqos-spapr.h"
#include "libqos/virtio.h"
#include "libqos/virtio-pci.h"
-#include "libqos/malloc.h"
-#include "libqos/malloc-pc.h"
#include "standard-headers/linux/virtio_ids.h"
#include "standard-headers/linux/virtio_pci.h"
static const char mount_tag[] = "qtest";
static char *test_share;
-static void qvirtio_9p_start(void)
+
+static QOSState *qvirtio_9p_start(void)
{
- char *args;
+ const char *arch = qtest_get_arch();
+ const char *cmd = "-fsdev local,id=fsdev0,security_model=none,path=%s "
+ "-device virtio-9p-pci,fsdev=fsdev0,mount_tag=%s";
test_share = g_strdup("/tmp/qtest.XXXXXX");
g_assert_nonnull(mkdtemp(test_share));
- args = g_strdup_printf("-fsdev local,id=fsdev0,security_model=none,path=%s "
- "-device virtio-9p-pci,fsdev=fsdev0,mount_tag=%s",
- test_share, mount_tag);
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ return qtest_pc_boot(cmd, test_share, mount_tag);
+ }
+ if (strcmp(arch, "ppc64") == 0) {
+ return qtest_spapr_boot(cmd, test_share, mount_tag);
+ }
- qtest_start(args);
- g_free(args);
+ g_printerr("virtio-9p tests are only available on x86 or ppc64\n");
+ exit(EXIT_FAILURE);
}
-static void qvirtio_9p_stop(void)
+static void qvirtio_9p_stop(QOSState *qs)
{
- qtest_end();
+ qtest_shutdown(qs);
rmdir(test_share);
g_free(test_share);
}
static void pci_nop(void)
{
- qvirtio_9p_start();
- qvirtio_9p_stop();
+ QOSState *qs;
+
+ qs = qvirtio_9p_start();
+ qvirtio_9p_stop(qs);
}
typedef struct {
QVirtioDevice *dev;
- QGuestAllocator *alloc;
- QPCIBus *bus;
+ QOSState *qs;
QVirtQueue *vq;
} QVirtIO9P;
-static QVirtIO9P *qvirtio_9p_pci_init(void)
+static QVirtIO9P *qvirtio_9p_pci_init(QOSState *qs)
{
QVirtIO9P *v9p;
QVirtioPCIDevice *dev;
v9p = g_new0(QVirtIO9P, 1);
- v9p->alloc = pc_alloc_init();
- v9p->bus = qpci_init_pc(NULL);
- dev = qvirtio_pci_device_find(v9p->bus, VIRTIO_ID_9P);
+ v9p->qs = qs;
+ dev = qvirtio_pci_device_find(v9p->qs->pcibus, VIRTIO_ID_9P);
g_assert_nonnull(dev);
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_9P);
v9p->dev = (QVirtioDevice *) dev;
qvirtio_pci_device_enable(dev);
- qvirtio_reset(&qvirtio_pci, v9p->dev);
- qvirtio_set_acknowledge(&qvirtio_pci, v9p->dev);
- qvirtio_set_driver(&qvirtio_pci, v9p->dev);
+ qvirtio_reset(v9p->dev);
+ qvirtio_set_acknowledge(v9p->dev);
+ qvirtio_set_driver(v9p->dev);
- v9p->vq = qvirtqueue_setup(&qvirtio_pci, v9p->dev, v9p->alloc, 0);
+ v9p->vq = qvirtqueue_setup(v9p->dev, v9p->qs->alloc, 0);
return v9p;
}
static void qvirtio_9p_pci_free(QVirtIO9P *v9p)
{
- qvirtqueue_cleanup(&qvirtio_pci, v9p->vq, v9p->alloc);
- pc_alloc_uninit(v9p->alloc);
+ qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, v9p->qs->alloc);
qvirtio_pci_device_disable(container_of(v9p->dev, QVirtioPCIDevice, vdev));
g_free(v9p->dev);
- qpci_free_pc(v9p->bus);
g_free(v9p);
}
static void pci_basic_config(void)
{
QVirtIO9P *v9p;
- void *addr;
size_t tag_len;
char *tag;
int i;
+ QOSState *qs;
- qvirtio_9p_start();
- v9p = qvirtio_9p_pci_init();
+ qs = qvirtio_9p_start();
+ v9p = qvirtio_9p_pci_init(qs);
- addr = ((QVirtioPCIDevice *) v9p->dev)->addr + VIRTIO_PCI_CONFIG_OFF(false);
- tag_len = qvirtio_config_readw(&qvirtio_pci, v9p->dev,
- (uint64_t)(uintptr_t)addr);
+ tag_len = qvirtio_config_readw(v9p->dev, 0);
g_assert_cmpint(tag_len, ==, strlen(mount_tag));
- addr += sizeof(uint16_t);
tag = g_malloc(tag_len);
for (i = 0; i < tag_len; i++) {
- tag[i] = qvirtio_config_readb(&qvirtio_pci, v9p->dev,
- (uint64_t)(uintptr_t)addr + i);
+ tag[i] = qvirtio_config_readb(v9p->dev, i + 2);
}
g_assert_cmpmem(tag, tag_len, mount_tag, tag_len);
g_free(tag);
qvirtio_9p_pci_free(v9p);
- qvirtio_9p_stop();
+ qvirtio_9p_stop(qs);
}
int main(int argc, char **argv)
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
index 0506917341..0e32e416dd 100644
--- a/tests/virtio-blk-test.c
+++ b/tests/virtio-blk-test.c
@@ -10,12 +10,11 @@
#include "qemu/osdep.h"
#include "libqtest.h"
+#include "libqos/libqos-pc.h"
+#include "libqos/libqos-spapr.h"
#include "libqos/virtio.h"
#include "libqos/virtio-pci.h"
#include "libqos/virtio-mmio.h"
-#include "libqos/pci-pc.h"
-#include "libqos/malloc.h"
-#include "libqos/malloc-pc.h"
#include "libqos/malloc-generic.h"
#include "qemu/bswap.h"
#include "standard-headers/linux/virtio_ids.h"
@@ -58,24 +57,29 @@ static char *drive_create(void)
return tmp_path;
}
-static QPCIBus *pci_test_start(void)
+static QOSState *pci_test_start(void)
{
- char *cmdline;
+ QOSState *qs;
+ const char *arch = qtest_get_arch();
char *tmp_path;
+ const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw "
+ "-drive if=none,id=drive1,file=/dev/null,format=raw "
+ "-device virtio-blk-pci,id=drv0,drive=drive0,"
+ "addr=%x.%x";
tmp_path = drive_create();
- cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s,format=raw "
- "-drive if=none,id=drive1,file=/dev/null,format=raw "
- "-device virtio-blk-pci,id=drv0,drive=drive0,"
- "addr=%x.%x",
- tmp_path, PCI_SLOT, PCI_FN);
- qtest_start(cmdline);
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qs = qtest_pc_boot(cmd, tmp_path, PCI_SLOT, PCI_FN);
+ } else if (strcmp(arch, "ppc64") == 0) {
+ qs = qtest_spapr_boot(cmd, tmp_path, PCI_SLOT, PCI_FN);
+ } else {
+ g_printerr("virtio-blk tests are only available on x86 or ppc64\n");
+ exit(EXIT_FAILURE);
+ }
unlink(tmp_path);
g_free(tmp_path);
- g_free(cmdline);
-
- return qpci_init_pc(NULL);
+ return qs;
}
static void arm_test_start(void)
@@ -110,30 +114,30 @@ static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot)
g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN));
qvirtio_pci_device_enable(dev);
- qvirtio_reset(&qvirtio_pci, &dev->vdev);
- qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev);
- qvirtio_set_driver(&qvirtio_pci, &dev->vdev);
+ qvirtio_reset(&dev->vdev);
+ qvirtio_set_acknowledge(&dev->vdev);
+ qvirtio_set_driver(&dev->vdev);
return dev;
}
-static inline void virtio_blk_fix_request(QVirtioBlkReq *req)
+static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
{
#ifdef HOST_WORDS_BIGENDIAN
- bool host_endian = true;
+ const bool host_is_big_endian = true;
#else
- bool host_endian = false;
+ const bool host_is_big_endian = false;
#endif
- if (target_big_endian() != host_endian) {
+ if (qvirtio_is_big_endian(d) != host_is_big_endian) {
req->type = bswap32(req->type);
req->ioprio = bswap32(req->ioprio);
req->sector = bswap64(req->sector);
}
}
-static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req,
- uint64_t data_size)
+static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d,
+ QVirtioBlkReq *req, uint64_t data_size)
{
uint64_t addr;
uint8_t status = 0xFF;
@@ -141,7 +145,7 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req,
g_assert_cmpuint(data_size % 512, ==, 0);
addr = guest_alloc(alloc, sizeof(*req) + data_size);
- virtio_blk_fix_request(req);
+ virtio_blk_fix_request(d, req);
memwrite(addr, req, 16);
memwrite(addr + 16, req->data, data_size);
@@ -150,8 +154,8 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req,
return addr;
}
-static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
- QGuestAllocator *alloc, QVirtQueue *vq, uint64_t device_specific)
+static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc,
+ QVirtQueue *vq)
{
QVirtioBlkReq req;
uint64_t req_addr;
@@ -161,18 +165,18 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
uint8_t status;
char *data;
- capacity = qvirtio_config_readq(bus, dev, device_specific);
+ capacity = qvirtio_config_readq(dev, 0);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- features = qvirtio_get_features(bus, dev);
+ features = qvirtio_get_features(dev);
features = features & ~(QVIRTIO_F_BAD_FEATURE |
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
(1u << VIRTIO_RING_F_EVENT_IDX) |
(1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(bus, dev, features);
+ qvirtio_set_features(dev, features);
- qvirtio_set_driver_ok(bus, dev);
+ qvirtio_set_driver_ok(dev);
/* Write and read with 3 descriptor layout */
/* Write request */
@@ -182,7 +186,7 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
g_free(req.data);
@@ -190,9 +194,9 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
qvirtqueue_add(vq, req_addr + 16, 512, false, true);
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(bus, dev, vq, free_head);
+ qvirtqueue_kick(dev, vq, free_head);
- qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
+ qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
@@ -204,7 +208,7 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
req.sector = 0;
req.data = g_malloc0(512);
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
g_free(req.data);
@@ -212,9 +216,9 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
qvirtqueue_add(vq, req_addr + 16, 512, true, true);
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(bus, dev, vq, free_head);
+ qvirtqueue_kick(dev, vq, free_head);
- qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
+ qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
@@ -234,15 +238,15 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(vq, req_addr, 528, false, true);
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(bus, dev, vq, free_head);
+ qvirtqueue_kick(dev, vq, free_head);
- qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
+ qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
@@ -254,16 +258,16 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
req.sector = 1;
req.data = g_malloc0(512);
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(alloc, dev, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
qvirtqueue_add(vq, req_addr + 16, 513, true, false);
- qvirtqueue_kick(bus, dev, vq, free_head);
+ qvirtqueue_kick(dev, vq, free_head);
- qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US);
+ qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
@@ -279,42 +283,30 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev,
static void pci_basic(void)
{
QVirtioPCIDevice *dev;
- QPCIBus *bus;
+ QOSState *qs;
QVirtQueuePCI *vqpci;
- QGuestAllocator *alloc;
- void *addr;
- bus = pci_test_start();
- dev = virtio_blk_pci_init(bus, PCI_SLOT);
+ qs = pci_test_start();
+ dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
- alloc = pc_alloc_init();
- vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
- alloc, 0);
+ vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
- /* MSI-X is not enabled */
- addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false);
-
- test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq,
- (uint64_t)(uintptr_t)addr);
+ test_basic(&dev->vdev, qs->alloc, &vqpci->vq);
/* End test */
- qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc);
- pc_alloc_uninit(alloc);
+ qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc);
qvirtio_pci_device_disable(dev);
g_free(dev);
- qpci_free_pc(bus);
- test_end();
+ qtest_shutdown(qs);
}
static void pci_indirect(void)
{
QVirtioPCIDevice *dev;
- QPCIBus *bus;
QVirtQueuePCI *vqpci;
- QGuestAllocator *alloc;
+ QOSState *qs;
QVirtioBlkReq req;
QVRingIndirectDesc *indirect;
- void *addr;
uint64_t req_addr;
uint64_t capacity;
uint32_t features;
@@ -322,28 +314,22 @@ static void pci_indirect(void)
uint8_t status;
char *data;
- bus = pci_test_start();
-
- dev = virtio_blk_pci_init(bus, PCI_SLOT);
+ qs = pci_test_start();
- /* MSI-X is not enabled */
- addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false);
+ dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
- capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
- (uint64_t)(uintptr_t)addr);
+ capacity = qvirtio_config_readq(&dev->vdev, 0);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
+ features = qvirtio_get_features(&dev->vdev);
g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
features = features & ~(QVIRTIO_F_BAD_FEATURE |
(1u << VIRTIO_RING_F_EVENT_IDX) |
(1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
+ qvirtio_set_features(&dev->vdev, features);
- alloc = pc_alloc_init();
- vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
- alloc, 0);
- qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+ vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
+ qvirtio_set_driver_ok(&dev->vdev);
/* Write request */
req.type = VIRTIO_BLK_T_OUT;
@@ -352,23 +338,23 @@ static void pci_indirect(void)
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
g_free(req.data);
- indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2);
+ indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2);
qvring_indirect_desc_add(indirect, req_addr, 528, false);
qvring_indirect_desc_add(indirect, req_addr + 528, 1, true);
free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect);
- qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+ qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
- qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
g_free(indirect);
- guest_free(alloc, req_addr);
+ guest_free(qs->alloc, req_addr);
/* Read request */
req.type = VIRTIO_BLK_T_IN;
@@ -377,17 +363,17 @@ static void pci_indirect(void)
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
g_free(req.data);
- indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2);
+ indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2);
qvring_indirect_desc_add(indirect, req_addr, 16, false);
qvring_indirect_desc_add(indirect, req_addr + 16, 513, true);
free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect);
- qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+ qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
- qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
@@ -398,61 +384,51 @@ static void pci_indirect(void)
g_free(data);
g_free(indirect);
- guest_free(alloc, req_addr);
+ guest_free(qs->alloc, req_addr);
/* End test */
- qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc);
- pc_alloc_uninit(alloc);
+ qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc);
qvirtio_pci_device_disable(dev);
g_free(dev);
- qpci_free_pc(bus);
- test_end();
+ qtest_shutdown(qs);
}
static void pci_config(void)
{
QVirtioPCIDevice *dev;
- QPCIBus *bus;
+ QOSState *qs;
int n_size = TEST_IMAGE_SIZE / 2;
- void *addr;
uint64_t capacity;
- bus = pci_test_start();
+ qs = pci_test_start();
- dev = virtio_blk_pci_init(bus, PCI_SLOT);
+ dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
- /* MSI-X is not enabled */
- addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false);
-
- capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
- (uint64_t)(uintptr_t)addr);
+ capacity = qvirtio_config_readq(&dev->vdev, 0);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+ qvirtio_set_driver_ok(&dev->vdev);
qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', "
" 'size': %d } }", n_size);
- qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
+ qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
- capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
- (uint64_t)(uintptr_t)addr);
+ capacity = qvirtio_config_readq(&dev->vdev, 0);
g_assert_cmpint(capacity, ==, n_size / 512);
qvirtio_pci_device_disable(dev);
g_free(dev);
- qpci_free_pc(bus);
- test_end();
+
+ qtest_shutdown(qs);
}
static void pci_msix(void)
{
QVirtioPCIDevice *dev;
- QPCIBus *bus;
+ QOSState *qs;
QVirtQueuePCI *vqpci;
- QGuestAllocator *alloc;
QVirtioBlkReq req;
int n_size = TEST_IMAGE_SIZE / 2;
- void *addr;
uint64_t req_addr;
uint64_t capacity;
uint32_t features;
@@ -460,41 +436,34 @@ static void pci_msix(void)
uint8_t status;
char *data;
- bus = pci_test_start();
- alloc = pc_alloc_init();
+ qs = pci_test_start();
- dev = virtio_blk_pci_init(bus, PCI_SLOT);
+ dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
qpci_msix_enable(dev->pdev);
- qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
+ qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0);
- /* MSI-X is enabled */
- addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(true);
-
- capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
- (uint64_t)(uintptr_t)addr);
+ capacity = qvirtio_config_readq(&dev->vdev, 0);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
+ features = qvirtio_get_features(&dev->vdev);
features = features & ~(QVIRTIO_F_BAD_FEATURE |
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
(1u << VIRTIO_RING_F_EVENT_IDX) |
(1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
+ qvirtio_set_features(&dev->vdev, features);
- vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
- alloc, 0);
- qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1);
+ vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
+ qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1);
- qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+ qvirtio_set_driver_ok(&dev->vdev);
qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', "
" 'size': %d } }", n_size);
- qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
+ qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
- capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
- (uint64_t)(uintptr_t)addr);
+ capacity = qvirtio_config_readq(&dev->vdev, 0);
g_assert_cmpint(capacity, ==, n_size / 512);
/* Write request */
@@ -504,22 +473,22 @@ static void pci_msix(void)
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+ qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
- qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
g_assert_cmpint(status, ==, 0);
- guest_free(alloc, req_addr);
+ guest_free(qs->alloc, req_addr);
/* Read request */
req.type = VIRTIO_BLK_T_IN;
@@ -527,7 +496,7 @@ static void pci_msix(void)
req.sector = 0;
req.data = g_malloc0(512);
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
g_free(req.data);
@@ -535,10 +504,10 @@ static void pci_msix(void)
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+ qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
- qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
@@ -549,26 +518,22 @@ static void pci_msix(void)
g_assert_cmpstr(data, ==, "TEST");
g_free(data);
- guest_free(alloc, req_addr);
+ guest_free(qs->alloc, req_addr);
/* End test */
- qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc);
- pc_alloc_uninit(alloc);
+ qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc);
qpci_msix_disable(dev->pdev);
qvirtio_pci_device_disable(dev);
g_free(dev);
- qpci_free_pc(bus);
- test_end();
+ qtest_shutdown(qs);
}
static void pci_idx(void)
{
QVirtioPCIDevice *dev;
- QPCIBus *bus;
+ QOSState *qs;
QVirtQueuePCI *vqpci;
- QGuestAllocator *alloc;
QVirtioBlkReq req;
- void *addr;
uint64_t req_addr;
uint64_t capacity;
uint32_t features;
@@ -576,33 +541,27 @@ static void pci_idx(void)
uint8_t status;
char *data;
- bus = pci_test_start();
- alloc = pc_alloc_init();
+ qs = pci_test_start();
- dev = virtio_blk_pci_init(bus, PCI_SLOT);
+ dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
qpci_msix_enable(dev->pdev);
- qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0);
+ qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0);
- /* MSI-X is enabled */
- addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(true);
-
- capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev,
- (uint64_t)(uintptr_t)addr);
+ capacity = qvirtio_config_readq(&dev->vdev, 0);
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
- features = qvirtio_get_features(&qvirtio_pci, &dev->vdev);
+ features = qvirtio_get_features(&dev->vdev);
features = features & ~(QVIRTIO_F_BAD_FEATURE |
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
(1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
(1u << VIRTIO_BLK_F_SCSI));
- qvirtio_set_features(&qvirtio_pci, &dev->vdev, features);
+ qvirtio_set_features(&dev->vdev, features);
- vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
- alloc, 0);
- qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1);
+ vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
+ qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1);
- qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev);
+ qvirtio_set_driver_ok(&dev->vdev);
/* Write request */
req.type = VIRTIO_BLK_T_OUT;
@@ -611,17 +570,16 @@ static void pci_idx(void)
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
g_free(req.data);
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+ qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
- qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
- QVIRTIO_BLK_TIMEOUT_US);
+ qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US);
/* Write request */
req.type = VIRTIO_BLK_T_OUT;
@@ -630,7 +588,7 @@ static void pci_idx(void)
req.data = g_malloc0(512);
strcpy(req.data, "TEST");
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
g_free(req.data);
@@ -639,15 +597,15 @@ static void pci_idx(void)
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+ qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
/* No notification expected */
- status = qvirtio_wait_status_byte_no_isr(&qvirtio_pci, &dev->vdev,
+ status = qvirtio_wait_status_byte_no_isr(&dev->vdev,
&vqpci->vq, req_addr + 528,
QVIRTIO_BLK_TIMEOUT_US);
g_assert_cmpint(status, ==, 0);
- guest_free(alloc, req_addr);
+ guest_free(qs->alloc, req_addr);
/* Read request */
req.type = VIRTIO_BLK_T_IN;
@@ -655,7 +613,7 @@ static void pci_idx(void)
req.sector = 1;
req.data = g_malloc0(512);
- req_addr = virtio_blk_request(alloc, &req, 512);
+ req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
g_free(req.data);
@@ -663,9 +621,9 @@ static void pci_idx(void)
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
- qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head);
+ qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
- qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq,
+ qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq,
QVIRTIO_BLK_TIMEOUT_US);
status = readb(req_addr + 528);
@@ -676,38 +634,38 @@ static void pci_idx(void)
g_assert_cmpstr(data, ==, "TEST");
g_free(data);
- guest_free(alloc, req_addr);
+ guest_free(qs->alloc, req_addr);
/* End test */
- qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc);
- pc_alloc_uninit(alloc);
+ qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc);
qpci_msix_disable(dev->pdev);
qvirtio_pci_device_disable(dev);
g_free(dev);
- qpci_free_pc(bus);
- test_end();
+ qtest_shutdown(qs);
}
static void pci_hotplug(void)
{
- QPCIBus *bus;
QVirtioPCIDevice *dev;
+ QOSState *qs;
+ const char *arch = qtest_get_arch();
- bus = pci_test_start();
+ qs = pci_test_start();
/* plug secondary disk */
qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP,
"'drive': 'drive1'");
- dev = virtio_blk_pci_init(bus, PCI_SLOT_HP);
+ dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP);
g_assert(dev);
qvirtio_pci_device_disable(dev);
g_free(dev);
/* unplug secondary disk */
- qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP);
- qpci_free_pc(bus);
- test_end();
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP);
+ }
+ qtest_shutdown(qs);
}
static void mmio_basic(void)
@@ -724,30 +682,27 @@ static void mmio_basic(void)
g_assert(dev != NULL);
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
- qvirtio_reset(&qvirtio_mmio, &dev->vdev);
- qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev);
- qvirtio_set_driver(&qvirtio_mmio, &dev->vdev);
+ qvirtio_reset(&dev->vdev);
+ qvirtio_set_acknowledge(&dev->vdev);
+ qvirtio_set_driver(&dev->vdev);
alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE);
- vq = qvirtqueue_setup(&qvirtio_mmio, &dev->vdev, alloc, 0);
+ vq = qvirtqueue_setup(&dev->vdev, alloc, 0);
- test_basic(&qvirtio_mmio, &dev->vdev, alloc, vq,
- QVIRTIO_MMIO_DEVICE_SPECIFIC);
+ test_basic(&dev->vdev, alloc, vq);
qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', "
" 'size': %d } }", n_size);
- qvirtio_wait_queue_isr(&qvirtio_mmio, &dev->vdev, vq,
- QVIRTIO_BLK_TIMEOUT_US);
+ qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US);
- capacity = qvirtio_config_readq(&qvirtio_mmio, &dev->vdev,
- QVIRTIO_MMIO_DEVICE_SPECIFIC);
+ capacity = qvirtio_config_readq(&dev->vdev, 0);
g_assert_cmpint(capacity, ==, n_size / 512);
/* End test */
- qvirtqueue_cleanup(&qvirtio_mmio, vq, alloc);
- generic_alloc_uninit(alloc);
+ qvirtqueue_cleanup(dev->vdev.bus, vq, alloc);
g_free(dev);
+ generic_alloc_uninit(alloc);
test_end();
}
@@ -757,12 +712,15 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
- if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 ||
+ strcmp(arch, "ppc64") == 0) {
qtest_add_func("/virtio/blk/pci/basic", pci_basic);
qtest_add_func("/virtio/blk/pci/indirect", pci_indirect);
qtest_add_func("/virtio/blk/pci/config", pci_config);
- qtest_add_func("/virtio/blk/pci/msix", pci_msix);
- qtest_add_func("/virtio/blk/pci/idx", pci_idx);
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qtest_add_func("/virtio/blk/pci/msix", pci_msix);
+ qtest_add_func("/virtio/blk/pci/idx", pci_idx);
+ }
qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug);
} else if (strcmp(arch, "arm") == 0) {
qtest_add_func("/virtio/blk/mmio/basic", mmio_basic);
diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c
index a343a6b048..8f94360480 100644
--- a/tests/virtio-net-test.c
+++ b/tests/virtio-net-test.c
@@ -12,12 +12,10 @@
#include "qemu-common.h"
#include "qemu/sockets.h"
#include "qemu/iov.h"
-#include "libqos/pci-pc.h"
+#include "libqos/libqos-pc.h"
+#include "libqos/libqos-spapr.h"
#include "libqos/virtio.h"
#include "libqos/virtio-pci.h"
-#include "libqos/malloc.h"
-#include "libqos/malloc-pc.h"
-#include "libqos/malloc-generic.h"
#include "qemu/bswap.h"
#include "hw/virtio/virtio-net.h"
#include "standard-headers/linux/virtio_ids.h"
@@ -46,39 +44,43 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot)
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET);
qvirtio_pci_device_enable(dev);
- qvirtio_reset(&qvirtio_pci, &dev->vdev);
- qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev);
- qvirtio_set_driver(&qvirtio_pci, &dev->vdev);
+ qvirtio_reset(&dev->vdev);
+ qvirtio_set_acknowledge(&dev->vdev);
+ qvirtio_set_driver(&dev->vdev);
return dev;
}
-static QPCIBus *pci_test_start(int socket)
+static QOSState *pci_test_start(int socket)
{
- char *cmdline;
-
- cmdline = g_strdup_printf("-netdev socket,fd=%d,id=hs0 -device "
- "virtio-net-pci,netdev=hs0", socket);
- qtest_start(cmdline);
- g_free(cmdline);
-
- return qpci_init_pc(NULL);
+ const char *arch = qtest_get_arch();
+ const char *cmd = "-netdev socket,fd=%d,id=hs0 -device "
+ "virtio-net-pci,netdev=hs0";
+
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ return qtest_pc_boot(cmd, socket);
+ }
+ if (strcmp(arch, "ppc64") == 0) {
+ return qtest_spapr_boot(cmd, socket);
+ }
+ g_printerr("virtio-net tests are only available on x86 or ppc64\n");
+ exit(EXIT_FAILURE);
}
-static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev)
+static void driver_init(QVirtioDevice *dev)
{
uint32_t features;
- features = qvirtio_get_features(bus, dev);
+ features = qvirtio_get_features(dev);
features = features & ~(QVIRTIO_F_BAD_FEATURE |
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
(1u << VIRTIO_RING_F_EVENT_IDX));
- qvirtio_set_features(bus, dev, features);
+ qvirtio_set_features(dev, features);
- qvirtio_set_driver_ok(bus, dev);
+ qvirtio_set_driver_ok(dev);
}
-static void rx_test(const QVirtioBus *bus, QVirtioDevice *dev,
+static void rx_test(QVirtioDevice *dev,
QGuestAllocator *alloc, QVirtQueue *vq,
int socket)
{
@@ -101,19 +103,19 @@ static void rx_test(const QVirtioBus *bus, QVirtioDevice *dev,
req_addr = guest_alloc(alloc, 64);
free_head = qvirtqueue_add(vq, req_addr, 64, true, false);
- qvirtqueue_kick(bus, dev, vq, free_head);
+ qvirtqueue_kick(dev, vq, free_head);
ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test));
g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len));
- qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US);
+ qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US);
memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
g_assert_cmpstr(buffer, ==, "TEST");
guest_free(alloc, req_addr);
}
-static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev,
+static void tx_test(QVirtioDevice *dev,
QGuestAllocator *alloc, QVirtQueue *vq,
int socket)
{
@@ -127,9 +129,9 @@ static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev,
memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4);
free_head = qvirtqueue_add(vq, req_addr, 64, false, false);
- qvirtqueue_kick(bus, dev, vq, free_head);
+ qvirtqueue_kick(dev, vq, free_head);
- qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US);
+ qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US);
guest_free(alloc, req_addr);
ret = qemu_recv(socket, &len, sizeof(len), 0);
@@ -140,7 +142,7 @@ static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev,
g_assert_cmpstr(buffer, ==, "TEST");
}
-static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev,
+static void rx_stop_cont_test(QVirtioDevice *dev,
QGuestAllocator *alloc, QVirtQueue *vq,
int socket)
{
@@ -164,7 +166,7 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev,
req_addr = guest_alloc(alloc, 64);
free_head = qvirtqueue_add(vq, req_addr, 64, true, false);
- qvirtqueue_kick(bus, dev, vq, free_head);
+ qvirtqueue_kick(dev, vq, free_head);
rsp = qmp("{ 'execute' : 'stop'}");
QDECREF(rsp);
@@ -180,36 +182,34 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev,
rsp = qmp("{ 'execute' : 'cont'}");
QDECREF(rsp);
- qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US);
+ qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US);
memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test));
g_assert_cmpstr(buffer, ==, "TEST");
guest_free(alloc, req_addr);
}
-static void send_recv_test(const QVirtioBus *bus, QVirtioDevice *dev,
+static void send_recv_test(QVirtioDevice *dev,
QGuestAllocator *alloc, QVirtQueue *rvq,
QVirtQueue *tvq, int socket)
{
- rx_test(bus, dev, alloc, rvq, socket);
- tx_test(bus, dev, alloc, tvq, socket);
+ rx_test(dev, alloc, rvq, socket);
+ tx_test(dev, alloc, tvq, socket);
}
-static void stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev,
+static void stop_cont_test(QVirtioDevice *dev,
QGuestAllocator *alloc, QVirtQueue *rvq,
QVirtQueue *tvq, int socket)
{
- rx_stop_cont_test(bus, dev, alloc, rvq, socket);
+ rx_stop_cont_test(dev, alloc, rvq, socket);
}
static void pci_basic(gconstpointer data)
{
QVirtioPCIDevice *dev;
- QPCIBus *bus;
+ QOSState *qs;
QVirtQueuePCI *tx, *rx;
- QGuestAllocator *alloc;
- void (*func) (const QVirtioBus *bus,
- QVirtioDevice *dev,
+ void (*func) (QVirtioDevice *dev,
QGuestAllocator *alloc,
QVirtQueue *rvq,
QVirtQueue *tvq,
@@ -219,37 +219,37 @@ static void pci_basic(gconstpointer data)
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
g_assert_cmpint(ret, !=, -1);
- bus = pci_test_start(sv[1]);
- dev = virtio_net_pci_init(bus, PCI_SLOT);
+ qs = pci_test_start(sv[1]);
+ dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT);
- alloc = pc_alloc_init();
- rx = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
- alloc, 0);
- tx = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev,
- alloc, 1);
+ rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
+ tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1);
- driver_init(&qvirtio_pci, &dev->vdev);
- func(&qvirtio_pci, &dev->vdev, alloc, &rx->vq, &tx->vq, sv[0]);
+ driver_init(&dev->vdev);
+ func(&dev->vdev, qs->alloc, &rx->vq, &tx->vq, sv[0]);
/* End test */
close(sv[0]);
- qvirtqueue_cleanup(&qvirtio_pci, &tx->vq, alloc);
- qvirtqueue_cleanup(&qvirtio_pci, &rx->vq, alloc);
- pc_alloc_uninit(alloc);
+ qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc);
+ qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc);
qvirtio_pci_device_disable(dev);
g_free(dev->pdev);
g_free(dev);
- qpci_free_pc(bus);
- test_end();
+ qtest_shutdown(qs);
}
#endif
static void hotplug(void)
{
+ const char *arch = qtest_get_arch();
+
qtest_start("-device virtio-net-pci");
qpci_plug_device_test("virtio-net-pci", "net1", PCI_SLOT_HP, NULL);
- qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP);
+
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP);
+ }
test_end();
}
diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c
index e1b26401f9..dcecf77463 100644
--- a/tests/virtio-rng-test.c
+++ b/tests/virtio-rng-test.c
@@ -20,8 +20,13 @@ static void pci_nop(void)
static void hotplug(void)
{
+ const char *arch = qtest_get_arch();
+
qpci_plug_device_test("virtio-rng-pci", "rng1", PCI_SLOT_HP, NULL);
- qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP);
+
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP);
+ }
}
int main(int argc, char **argv)
diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c
index 79088bb249..69220ef07b 100644
--- a/tests/virtio-scsi-test.c
+++ b/tests/virtio-scsi-test.c
@@ -11,12 +11,10 @@
#include "qemu/osdep.h"
#include "libqtest.h"
#include "block/scsi.h"
+#include "libqos/libqos-pc.h"
+#include "libqos/libqos-spapr.h"
#include "libqos/virtio.h"
#include "libqos/virtio-pci.h"
-#include "libqos/pci-pc.h"
-#include "libqos/malloc.h"
-#include "libqos/malloc-pc.h"
-#include "libqos/malloc-generic.h"
#include "standard-headers/linux/virtio_ids.h"
#include "standard-headers/linux/virtio_pci.h"
#include "standard-headers/linux/virtio_scsi.h"
@@ -29,28 +27,32 @@
typedef struct {
QVirtioDevice *dev;
- QGuestAllocator *alloc;
- QPCIBus *bus;
+ QOSState *qs;
int num_queues;
QVirtQueue *vq[MAX_NUM_QUEUES + 2];
} QVirtIOSCSI;
-static void qvirtio_scsi_start(const char *extra_opts)
+static QOSState *qvirtio_scsi_start(const char *extra_opts)
{
- char *cmdline;
-
- cmdline = g_strdup_printf(
- "-drive id=drv0,if=none,file=/dev/null,format=raw "
- "-device virtio-scsi-pci,id=vs0 "
- "-device scsi-hd,bus=vs0.0,drive=drv0 %s",
- extra_opts ? : "");
- qtest_start(cmdline);
- g_free(cmdline);
+ const char *arch = qtest_get_arch();
+ const char *cmd = "-drive id=drv0,if=none,file=/dev/null,format=raw "
+ "-device virtio-scsi-pci,id=vs0 "
+ "-device scsi-hd,bus=vs0.0,drive=drv0 %s";
+
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ return qtest_pc_boot(cmd, extra_opts ? : "");
+ }
+ if (strcmp(arch, "ppc64") == 0) {
+ return qtest_spapr_boot(cmd, extra_opts ? : "");
+ }
+
+ g_printerr("virtio-scsi tests are only available on x86 or ppc64\n");
+ exit(EXIT_FAILURE);
}
-static void qvirtio_scsi_stop(void)
+static void qvirtio_scsi_stop(QOSState *qs)
{
- qtest_end();
+ qtest_shutdown(qs);
}
static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs)
@@ -58,12 +60,12 @@ static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs)
int i;
for (i = 0; i < vs->num_queues + 2; i++) {
- qvirtqueue_cleanup(&qvirtio_pci, vs->vq[i], vs->alloc);
+ qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], vs->qs->alloc);
}
- pc_alloc_uninit(vs->alloc);
qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev));
g_free(vs->dev);
- qpci_free_pc(vs->bus);
+ qvirtio_scsi_stop(vs->qs);
+ g_free(vs);
}
static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size,
@@ -71,7 +73,7 @@ static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size,
{
uint64_t addr;
- addr = guest_alloc(vs->alloc, alloc_size);
+ addr = guest_alloc(vs->qs->alloc, alloc_size);
if (data) {
memwrite(addr, data, alloc_size);
}
@@ -118,8 +120,8 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
qvirtqueue_add(vq, data_in_addr, data_in_len, true, false);
}
- qvirtqueue_kick(&qvirtio_pci, vs->dev, vq, free_head);
- qvirtio_wait_queue_isr(&qvirtio_pci, vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US);
+ qvirtqueue_kick(vs->dev, vq, free_head);
+ qvirtio_wait_queue_isr(vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US);
response = readb(resp_addr +
offsetof(struct virtio_scsi_cmd_resp, response));
@@ -128,10 +130,10 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
memread(resp_addr, resp_out, sizeof(*resp_out));
}
- guest_free(vs->alloc, req_addr);
- guest_free(vs->alloc, resp_addr);
- guest_free(vs->alloc, data_in_addr);
- guest_free(vs->alloc, data_out_addr);
+ guest_free(vs->qs->alloc, req_addr);
+ guest_free(vs->qs->alloc, resp_addr);
+ guest_free(vs->qs->alloc, data_in_addr);
+ guest_free(vs->qs->alloc, data_out_addr);
return response;
}
@@ -141,31 +143,29 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot)
QVirtIOSCSI *vs;
QVirtioPCIDevice *dev;
struct virtio_scsi_cmd_resp resp;
- void *addr;
int i;
vs = g_new0(QVirtIOSCSI, 1);
- vs->alloc = pc_alloc_init();
- vs->bus = qpci_init_pc(NULL);
- dev = qvirtio_pci_device_find(vs->bus, VIRTIO_ID_SCSI);
+ vs->qs = qvirtio_scsi_start("-drive file=blkdebug::null-co://,"
+ "if=none,id=dr1,format=raw,file.align=4k "
+ "-device scsi-disk,drive=dr1,lun=0,scsi-id=1");
+ dev = qvirtio_pci_device_find(vs->qs->pcibus, VIRTIO_ID_SCSI);
vs->dev = (QVirtioDevice *)dev;
g_assert(dev != NULL);
g_assert_cmphex(vs->dev->device_type, ==, VIRTIO_ID_SCSI);
qvirtio_pci_device_enable(dev);
- qvirtio_reset(&qvirtio_pci, vs->dev);
- qvirtio_set_acknowledge(&qvirtio_pci, vs->dev);
- qvirtio_set_driver(&qvirtio_pci, vs->dev);
+ qvirtio_reset(vs->dev);
+ qvirtio_set_acknowledge(vs->dev);
+ qvirtio_set_driver(vs->dev);
- addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false);
- vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev,
- (uint64_t)(uintptr_t)addr);
+ vs->num_queues = qvirtio_config_readl(vs->dev, 0);
g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES);
for (i = 0; i < vs->num_queues + 2; i++) {
- vs->vq[i] = qvirtqueue_setup(&qvirtio_pci, vs->dev, vs->alloc, i);
+ vs->vq[i] = qvirtqueue_setup(vs->dev, vs->qs->alloc, i);
}
/* Clear the POWER ON OCCURRED unit attention */
@@ -184,15 +184,18 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot)
/* Tests only initialization so far. TODO: Replace with functional tests */
static void pci_nop(void)
{
- qvirtio_scsi_start(NULL);
- qvirtio_scsi_stop();
+ QOSState *qs;
+
+ qs = qvirtio_scsi_start(NULL);
+ qvirtio_scsi_stop(qs);
}
static void hotplug(void)
{
QDict *response;
+ QOSState *qs;
- qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw");
+ qs = qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw");
response = qmp("{\"execute\": \"device_add\","
" \"arguments\": {"
" \"driver\": \"scsi-hd\","
@@ -214,7 +217,7 @@ static void hotplug(void)
g_assert(qdict_haskey(response, "event"));
g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED"));
QDECREF(response);
- qvirtio_scsi_stop();
+ qvirtio_scsi_stop(qs);
}
/* Test WRITE SAME with the lba not aligned */
@@ -230,9 +233,6 @@ static void test_unaligned_write_same(void)
0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00
};
- qvirtio_scsi_start("-drive file=blkdebug::null-co://,if=none,id=dr1"
- ",format=raw,file.align=4k "
- "-device scsi-disk,drive=dr1,lun=0,scsi-id=1");
vs = qvirtio_scsi_pci_init(PCI_SLOT);
g_assert_cmphex(0, ==,
@@ -242,7 +242,6 @@ static void test_unaligned_write_same(void)
virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL));
qvirtio_scsi_pci_free(vs);
- qvirtio_scsi_stop();
}
int main(int argc, char **argv)
diff --git a/ui/curses.c b/ui/curses.c
index d06f724879..2e132a7bfa 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -369,10 +369,10 @@ static void curses_setup(void)
/* ACS_* is not constant. So, we can't initialize statically. */
vga_to_curses['\0'] = ' ';
vga_to_curses[0x04] = ACS_DIAMOND;
- vga_to_curses[0x0a] = ACS_RARROW;
- vga_to_curses[0x0b] = ACS_LARROW;
vga_to_curses[0x18] = ACS_UARROW;
vga_to_curses[0x19] = ACS_DARROW;
+ vga_to_curses[0x1a] = ACS_RARROW;
+ vga_to_curses[0x1b] = ACS_LARROW;
vga_to_curses[0x9c] = ACS_STERLING;
vga_to_curses[0xb0] = ACS_BOARD;
vga_to_curses[0xb1] = ACS_CKBOARD;
diff --git a/ui/gtk.c b/ui/gtk.c
index 25e6d9969d..ca737c48d9 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -912,9 +912,28 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
+ int screen_width, screen_height;
+
int x = (int)motion->x_root;
int y = (int)motion->y_root;
+#if GTK_CHECK_VERSION(3, 22, 0)
+ {
+ GdkDisplay *dpy = gtk_widget_get_display(widget);
+ GdkWindow *win = gtk_widget_get_window(widget);
+ GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
+ GdkRectangle geometry;
+ gdk_monitor_get_geometry(monitor, &geometry);
+ screen_width = geometry.width;
+ screen_height = geometry.height;
+ }
+#else
+ {
+ screen_width = gdk_screen_get_width(screen);
+ screen_height = gdk_screen_get_height(screen);
+ }
+#endif
+
/* In relative mode check to see if client pointer hit
* one of the screen edges, and if so move it back by
* 200 pixels. This is important because the pointer
@@ -928,10 +947,10 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
if (y == 0) {
y += 200;
}
- if (x == (gdk_screen_get_width(screen) - 1)) {
+ if (x == (screen_width - 1)) {
x -= 200;
}
- if (y == (gdk_screen_get_height(screen) - 1)) {
+ if (y == (screen_height - 1)) {
y -= 200;
}
@@ -1051,7 +1070,9 @@ static gboolean gd_text_key_down(GtkWidget *widget,
VirtualConsole *vc = opaque;
QemuConsole *con = vc->gfx.dcl.con;
- if (key->length) {
+ if (key->keyval == GDK_KEY_Delete) {
+ kbd_put_qcode_console(con, Q_KEY_CODE_DELETE);
+ } else if (key->length) {
kbd_put_string_console(con, key->string, key->length);
} else {
int num = gd_map_keycode(vc->s, gtk_widget_get_display(widget),
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index 11c8c9a819..c0c29a5119 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -116,6 +116,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
return 0;
}
+ qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
object_unref(OBJECT(vs->ioc));
vs->ioc = QIO_CHANNEL(tls);
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index 42a8e7be5c..bffb484a8d 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -67,6 +67,8 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
return TRUE;
}
+ qio_channel_set_name(QIO_CHANNEL(tls), "vnc-ws-server-tls");
+
VNC_DEBUG("Start TLS WS handshake process\n");
object_unref(OBJECT(vs->ioc));
vs->ioc = QIO_CHANNEL(tls);
@@ -113,6 +115,7 @@ gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
}
wioc = qio_channel_websock_new_server(vs->ioc);
+ qio_channel_set_name(QIO_CHANNEL(wioc), "vnc-ws-server-websock");
object_unref(OBJECT(vs->ioc));
vs->ioc = QIO_CHANNEL(wioc);
diff --git a/ui/vnc.c b/ui/vnc.c
index 1bedc95b57..2c28a59ff7 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3100,6 +3100,9 @@ static gboolean vnc_listen_io(QIOChannel *ioc,
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err);
if (sioc != NULL) {
+ qio_channel_set_name(QIO_CHANNEL(sioc),
+ ioc != QIO_CHANNEL(vd->lsock) ?
+ "vnc-ws-server" : "vnc-server");
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
vnc_connect(vd, sioc, false,
ioc != QIO_CHANNEL(vd->lsock));
@@ -3788,6 +3791,7 @@ void vnc_display_open(const char *id, Error **errp)
}
vd->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
sioc = qio_channel_socket_new();
+ qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse");
if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) {
goto fail;
}
@@ -3795,6 +3799,7 @@ void vnc_display_open(const char *id, Error **errp)
object_unref(OBJECT(sioc));
} else {
vd->lsock = qio_channel_socket_new();
+ qio_channel_set_name(QIO_CHANNEL(vd->lsock), "vnc-listen");
if (qio_channel_socket_listen_sync(vd->lsock, saddr, errp) < 0) {
goto fail;
}
@@ -3802,6 +3807,7 @@ void vnc_display_open(const char *id, Error **errp)
if (ws_enabled) {
vd->lwebsock = qio_channel_socket_new();
+ qio_channel_set_name(QIO_CHANNEL(vd->lwebsock), "vnc-ws-listen");
if (qio_channel_socket_listen_sync(vd->lwebsock,
wsaddr, errp) < 0) {
object_unref(OBJECT(vd->lsock));
@@ -3845,6 +3851,7 @@ void vnc_display_add_client(const char *id, int csock, bool skipauth)
sioc = qio_channel_socket_new_fd(csock, NULL);
if (sioc) {
+ qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-server");
vnc_connect(vd, sioc, skipauth, false);
object_unref(OBJECT(sioc));
}
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 36c7dcc1fa..ad0f9c7fe4 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -25,7 +25,6 @@ util-obj-y += uuid.o
util-obj-y += throttle.o
util-obj-y += getauxval.o
util-obj-y += readline.o
-util-obj-y += rfifolock.o
util-obj-y += rcu.o
util-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
util-obj-y += qemu-coroutine-sleep.o
diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c
index ce51b37c1d..d20cddec0c 100644
--- a/util/qemu-thread-posix.c
+++ b/util/qemu-thread-posix.c
@@ -80,6 +80,20 @@ void qemu_mutex_unlock(QemuMutex *mutex)
error_exit(err, __func__);
}
+void qemu_rec_mutex_init(QemuRecMutex *mutex)
+{
+ int err;
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ err = pthread_mutex_init(&mutex->lock, &attr);
+ pthread_mutexattr_destroy(&attr);
+ if (err) {
+ error_exit(err, __func__);
+ }
+}
+
void qemu_cond_init(QemuCond *cond)
{
int err;
diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c
index 072806f792..728e76b5b2 100644
--- a/util/qemu-thread-win32.c
+++ b/util/qemu-thread-win32.c
@@ -79,6 +79,31 @@ void qemu_mutex_unlock(QemuMutex *mutex)
LeaveCriticalSection(&mutex->lock);
}
+void qemu_rec_mutex_init(QemuRecMutex *mutex)
+{
+ InitializeCriticalSection(&mutex->lock);
+}
+
+void qemu_rec_mutex_destroy(QemuRecMutex *mutex)
+{
+ DeleteCriticalSection(&mutex->lock);
+}
+
+void qemu_rec_mutex_lock(QemuRecMutex *mutex)
+{
+ EnterCriticalSection(&mutex->lock);
+}
+
+int qemu_rec_mutex_trylock(QemuRecMutex *mutex)
+{
+ return !TryEnterCriticalSection(&mutex->lock);
+}
+
+void qemu_rec_mutex_unlock(QemuRecMutex *mutex)
+{
+ LeaveCriticalSection(&mutex->lock);
+}
+
void qemu_cond_init(QemuCond *cond)
{
memset(cond, 0, sizeof(*cond));
diff --git a/util/rfifolock.c b/util/rfifolock.c
deleted file mode 100644
index 084c2f0ea1..0000000000
--- a/util/rfifolock.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Recursive FIFO lock
- *
- * Copyright Red Hat, Inc. 2013
- *
- * Authors:
- * Stefan Hajnoczi <stefanha@redhat.com>
- *
- * This work is licensed under the terms of the GNU LGPL, version 2 or later.
- * See the COPYING.LIB file in the top-level directory.
- *
- */
-
-#include "qemu/osdep.h"
-#include "qemu/rfifolock.h"
-
-void rfifolock_init(RFifoLock *r, void (*cb)(void *), void *opaque)
-{
- qemu_mutex_init(&r->lock);
- r->head = 0;
- r->tail = 0;
- qemu_cond_init(&r->cond);
- r->nesting = 0;
- r->cb = cb;
- r->cb_opaque = opaque;
-}
-
-void rfifolock_destroy(RFifoLock *r)
-{
- qemu_cond_destroy(&r->cond);
- qemu_mutex_destroy(&r->lock);
-}
-
-/*
- * Theory of operation:
- *
- * In order to ensure FIFO ordering, implement a ticketlock. Threads acquiring
- * the lock enqueue themselves by incrementing the tail index. When the lock
- * is unlocked, the head is incremented and waiting threads are notified.
- *
- * Recursive locking does not take a ticket since the head is only incremented
- * when the outermost recursive caller unlocks.
- */
-void rfifolock_lock(RFifoLock *r)
-{
- qemu_mutex_lock(&r->lock);
-
- /* Take a ticket */
- unsigned int ticket = r->tail++;
-
- if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) {
- r->tail--; /* put ticket back, we're nesting */
- } else {
- while (ticket != r->head) {
- /* Invoke optional contention callback */
- if (r->cb) {
- r->cb(r->cb_opaque);
- }
- qemu_cond_wait(&r->cond, &r->lock);
- }
- qemu_thread_get_self(&r->owner_thread);
- }
-
- r->nesting++;
- qemu_mutex_unlock(&r->lock);
-}
-
-void rfifolock_unlock(RFifoLock *r)
-{
- qemu_mutex_lock(&r->lock);
- assert(r->nesting > 0);
- assert(qemu_thread_is_self(&r->owner_thread));
- if (--r->nesting == 0) {
- r->head++;
- qemu_cond_broadcast(&r->cond);
- }
- qemu_mutex_unlock(&r->lock);
-}